| # 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, |
| ) |