|  | # Copyright 2024 The Bazel Authors. All rights reserved. | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  |  | 
|  | # WARNING: | 
|  | # https://github.com/bazelbuild/bazel/issues/17713 | 
|  | # .bzl files in this package (tools/build_defs/repo) are evaluated | 
|  | # in a Starlark environment without "@_builtins" injection, and must not refer | 
|  | # to symbols associated with build/workspace .bzl files | 
|  |  | 
|  | """Rules for making directories in the local filesystem available as repos. | 
|  |  | 
|  | ### Setup | 
|  |  | 
|  | To use these rules in a module extension, load them in your .bzl file and then call them from your | 
|  | extension's implementation function. For example, to use `local_repository`: | 
|  |  | 
|  | ```python | 
|  | load("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository") | 
|  |  | 
|  | def _my_extension_impl(mctx): | 
|  | local_repository(name = "foo", path = "foo") | 
|  |  | 
|  | my_extension = module_extension(implementation = _my_extension_impl) | 
|  | ``` | 
|  |  | 
|  | Alternatively, you can directly call these repo rules in your MODULE.bazel file with | 
|  | `use_repo_rule`: | 
|  |  | 
|  | ```python | 
|  | local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "local_repository") | 
|  | local_repository(name = "foo", path = "foo") | 
|  | ``` | 
|  | """ | 
|  |  | 
|  | # The default value for the string attr `build_file_content`. String attrs default to the empty | 
|  | # string by default, but we need another default because the empty string is perfectly valid build | 
|  | # file content and we need to know whether the attribute is actually set. | 
|  | _UNSET = "_UNSET" | 
|  |  | 
|  | def _get_dir_path(rctx): | 
|  | """Turns the string attr `path` into a path object, ensuring that it's a directory.""" | 
|  | path = rctx.workspace_root.get_child(rctx.attr.path) | 
|  | if not path.is_dir: | 
|  | fail( | 
|  | ("The repository's path is \"%s\" (absolute: \"%s\") but it does not exist or is not " + | 
|  | "a directory.") % (rctx.attr.path, path), | 
|  | ) | 
|  | return path | 
|  |  | 
|  | def _local_repository_impl(rctx): | 
|  | rctx.symlink(_get_dir_path(rctx), ".") | 
|  |  | 
|  | local_repository = repository_rule( | 
|  | implementation = _local_repository_impl, | 
|  | attrs = { | 
|  | "path": attr.string( | 
|  | doc = | 
|  | "The path to the directory to make available as a repo. <p>The path can be " + | 
|  | "either absolute, or relative to the workspace root.", | 
|  | mandatory = True, | 
|  | ), | 
|  | }, | 
|  | doc = | 
|  | "Makes a local directory that already contains Bazel files available as a repo. This " + | 
|  | "directory should contain Bazel BUILD files and a repo boundary file already. If it " + | 
|  | "doesn't contain these files, consider using <a " + | 
|  | "href=\"#new_local_repository\"><code>new_local_repository</code></a> instead.", | 
|  | local = True, | 
|  | ) | 
|  |  | 
|  | def _new_local_repository_impl(rctx): | 
|  | if (rctx.attr.build_file == None) == (rctx.attr.build_file_content == _UNSET): | 
|  | fail("exactly one of `build_file` and `build_file_content` must be specified") | 
|  |  | 
|  | children = _get_dir_path(rctx).readdir() | 
|  | for child in children: | 
|  | rctx.symlink(child, child.basename) | 
|  |  | 
|  | # On Windows, `rctx.symlink` actually does a copy for files (for directories, it uses | 
|  | # junctions which basically behave like symlinks as far as we're concerned). So we need to | 
|  | # watch the symlink target as well. | 
|  | if rctx.os.name.startswith("windows") and not child.is_dir: | 
|  | rctx.watch(child) | 
|  |  | 
|  | if rctx.attr.build_file != None: | 
|  | rctx.symlink(rctx.attr.build_file, "BUILD.bazel") | 
|  | if rctx.os.name.startswith("windows"): | 
|  | rctx.watch(rctx.attr.build_file)  # same reason as above | 
|  | else: | 
|  | rctx.file("BUILD.bazel", rctx.attr.build_file_content) | 
|  |  | 
|  | new_local_repository = repository_rule( | 
|  | implementation = _new_local_repository_impl, | 
|  | attrs = { | 
|  | "path": attr.string( | 
|  | doc = | 
|  | "The path to the directory to make available as a repo. <p>The path can be " + | 
|  | "either absolute, or relative to the workspace root.", | 
|  | mandatory = True, | 
|  | ), | 
|  | "build_file": attr.label( | 
|  | doc = | 
|  | "A file to use as a BUILD file for this repo. <p>Exactly one of " + | 
|  | "<code>build_file</code> and <code>build_file_content</code> must be specified. " + | 
|  | "<p>The file addressed by this label does not need to be named BUILD, but can " + | 
|  | "be. Something like <code>BUILD.new-repo-name</code> may work well to " + | 
|  | "distinguish it from actual BUILD files.", | 
|  | ), | 
|  | "build_file_content": attr.string( | 
|  | doc = | 
|  | "The content of the BUILD file to be created for this repo. <p>Exactly one of " + | 
|  | "<code>build_file</code> and <code>build_file_content</code> must be specified.", | 
|  | default = _UNSET, | 
|  | ), | 
|  | }, | 
|  | doc = | 
|  | "Makes a local directory that doesn't contain Bazel files available as a repo. This " + | 
|  | "directory need not contain Bazel BUILD files or a repo boundary file; they will be " + | 
|  | "created by this repo rule. If the directory already contains Bazel files, consider " + | 
|  | "using <a href=\"#local_repository\"><code>local_repository</code></a> instead.", | 
|  | local = True, | 
|  | ) |