Last active
September 5, 2023 04:47
-
-
Save rrbutani/2b5a1ee5ae22dcc13204511915e451de to your computer and use it in GitHub Desktop.
Workaround for https://github.com/bazelbuild/bazel/issues/19055
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Workarounds for https://github.com/bazelbuild/bazel/issues/19055.""" | |
# TODO(build, bazel, blzmod): if new APIs are introduced resolving this issue: | |
# https://github.com/bazelbuild/bazel/issues/19055, use them in lieu of this | |
# function. | |
def get_sibling_repo_in_module_extension(rctx, repo_name): | |
"""Gets the _canonical_ name for a sibling repo in the module extension. | |
## Background | |
This function is a workaround for [this issue]. | |
See: | |
- [here][canonical-repo-name] for information on canonical and apparent | |
repository names | |
- [here][module-extensions] for information on module extensions | |
As detailed in the linked issue, the repo mapping applied to repositories | |
created within a module extension (i.e. the thing that makes it so that uses | |
of _apparent_ repository names are mapped to _canonical_ repository names) | |
is not in effect during the _execution_ of the repository rule. | |
This makes it so that, within a repository rule invoked from a module | |
extension, uses of labels pointing to repositories created within the same | |
module extension (sibling repos) will not resolve when apparent names are | |
used (see the linked issue for more details). | |
This function uses the first workaround detailed in the issue linked above: | |
it relies on details about bzlmod's repository naming convention to get the | |
_canonical_ name of the sibling repository that is being referenced. | |
## Assumptions | |
We assume that: | |
- you are calling this function from within a repository rule that has | |
been invoked from a module extension | |
+ if it looks like this function has been called from outside a module | |
extension we'll just return `repo_name` | |
- no repositories involved (the calling repository rule's repo and | |
`repo_name`) have a `~` in their name | |
- `repo_name` really is a repository that exists under the module | |
extension that invoked the repository rule this function was invoked | |
from | |
None of these are really correctness issues (you'll just end up with a Label | |
that's not well-formed or doesn't exist — at worst it'll point to the wrong | |
thing). | |
--- | |
Args: | |
rctx: [repository context object][rctx] | |
This function must be invoked from a [repository rule] impl function. | |
repo_name: apparent name of the sibling repository | |
This should be the apparent name of a repository created by the module | |
extension that invoked the current repository rule. | |
Returns: | |
The canonical name of `repo_name`, subject to the caveats listed above. | |
[this issue]: https://github.com/bazelbuild/bazel/issues/19055 | |
[canonical-repo-name]: https://bazel.build/external/overview#canonical-repo-name | |
[module-extensions]: https://bazel.build/external/extension | |
[rctx]: https://bazel.build/rules/lib/builtins/repository_ctx | |
[repository rule]: https://bazel.build/extending/repo | |
""" | |
mangled_repository_name = rctx.name.split("~") | |
if len(mangled_repository_name) < 3: | |
# We should need at least | |
# `{calling_repo_name}~{module extension name}~{current repo name}`. | |
# | |
# If we don't have that, assume that we're not being invoked from within | |
# a module extension. | |
return repo_name | |
else: | |
mangled_repository_name[-1] = repo_name | |
return "~".join(mangled_repository_name) | |
def get_sibling_label_in_module_extension_from_parts(rctx, repo, pkg = "", target = None): | |
# Emulates the default `@foo` and `@foo//bar/baz` label shorthand syntax | |
if target == None: | |
# @foo//bar/baz -> @foo//bar/baz:baz | |
if pkg != "": target = pkg.split("/")[-1] | |
else: target = repo # @foo -> @foo//:foo | |
if "~" in repo: fail( | |
"Label must contain an _apparent_ repo name and must not contain `~`" | |
) | |
if repo == "": fail("main repo cannot be a sibling repo") | |
repo = get_sibling_repo_in_module_extension(rctx, repo) | |
return Label("@@{repo}//{pkg}:{target}".format( | |
repo = repo, | |
pkg = pkg, | |
target = target, | |
)) | |
def _parse_string_label(str_label): | |
# This is bad hacky string Label parsing. Fortunately it doesn't have to be | |
# rigorous... | |
if str_label.startswith("@@") or not str_label.startswith("@"): fail( | |
"invalid string label; must start with `@` and an _apparent_ repo name", | |
str_label | |
) | |
repo, sep, rest = str_label[1:].partition("//") | |
if sep != "//": | |
# assume there's no pkg or target, just return | |
return repo, "", None | |
pkg, sep, target = rest.partition(":") | |
if sep != ":": | |
# assume no target: | |
return repo, pkg, None | |
return repo, pkg, target | |
def get_sibling_label_in_module_extension_from_string(rctx, str_label): | |
# If the repository isn't in the mapping, you can't do | |
# `Label("@repo//...").workspace_name`; it'll error. | |
# | |
# So you have to either pass the label as a string (this function) or by | |
# parts (`_from_parts`). | |
return get_sibling_label_in_module_extension_from_parts( | |
rctx, | |
*_parse_string_label(str_label), | |
) | |
def get_sibling_label_in_module_extension(rctx, label): | |
"""Canonicalizes a label pointing to a sibling repo in the module extension. | |
This function is a wrapper over `get_sibling_repo_in_module_extension` that | |
operates on `Label`s instead of bare repo names. See that function's docs | |
for more details. | |
Args: | |
rctx: repository context object | |
label: [label] with the apparent repo name of a sibling repository | |
Returns: | |
A new `Label`, with the repo name canonicalized. | |
[label]: https://bazel.build/rules/lib/builtins/Label | |
""" | |
if type(label) != "Label": fail("must be a Label") | |
return get_sibling_label_in_module_extension_from_parts( | |
rctx, label.workspace_name, label.package, label.name | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment