Skip to content

Commit

Permalink
Use apparent_repo_name only for repository_ctx
Browse files Browse the repository at this point in the history
Repurposed `apparent_repo_name` to only work with `repository_ctx`
objects after finding solutions to other repository name related
problems in bazelbuild/rules_scala#1621.

The one problem I couldn't solve (elegantly) without this function was
setting a default target name for a generated repo. Technically, such
repos could require an attribute to mirror `name`, but that seems like a
terrible user experience.
  • Loading branch information
mbland committed Oct 16, 2024
1 parent 7d712db commit d8b3262
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 105 deletions.
3 changes: 3 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ use_repo(as_extension_test_ext, "bar", "foo")

use_all_repos_test_ext = use_extension("//tests:modules_test.bzl", "use_all_repos_test_ext", dev_dependency = True)
use_repo(use_all_repos_test_ext, "baz", "qux")

apparent_repo_name_test_ext = use_extension("//tests:extensions/apparent_repo_name.bzl", "apparent_repo_name_test_ext", dev_dependency = True)
use_repo(apparent_repo_name_test_ext, "apparent-repo-name-test")
4 changes: 4 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ load("//tests/directory:external_directory_tests.bzl", "external_directory_tests
external_directory_tests(name = "external_directory_tests")

register_unittest_toolchains()

load("//tests:extensions/apparent_repo_name.bzl", "apparent_repo_name_test_macro")

apparent_repo_name_test_macro()
28 changes: 24 additions & 4 deletions docs/modules_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,41 @@ Skylib module containing utilities for Bazel modules and module extensions.
## modules.apparent_repo_name

<pre>
modules.apparent_repo_name(<a href="#modules.apparent_repo_name-label_or_name">label_or_name</a>)
modules.apparent_repo_name(<a href="#modules.apparent_repo_name-repository_ctx">repository_ctx</a>)
</pre>

Return a repository's apparent repository name.
Generates a repository's apparent name from a repository_ctx object.

Useful when generating the default top level `BUILD` target for the
repository.

Example:
```starlark
_ALIAS_TARGET_TEMPLATE = """alias(
name = "{name}",
actual = "@{target_repo_name}",
visibility = ["//visibility:public"],
)
"""

def _alias_repository_impl(repository_ctx):
repository_ctx.file("BUILD", _ALIAS_TARGET_TEMPLATE.format(
name = apparent_repo_name(rctx),
target = rctx.attr.target_repo_name,
))
```


**PARAMETERS**


| Name | Description | Default Value |
| :------------- | :------------- | :------------- |
| <a id="modules.apparent_repo_name-label_or_name"></a>label_or_name | a Label or repository name string | none |
| <a id="modules.apparent_repo_name-repository_ctx"></a>repository_ctx | a repository_ctx object | none |

**RETURNS**

The apparent repository name
An apparent repo name derived from repository_ctx.name


<a id="modules.as_extension"></a>
Expand Down
46 changes: 26 additions & 20 deletions lib/modules.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -114,38 +114,44 @@ def _use_all_repos(module_ctx, reproducible = False):
**extension_metadata_kwargs
)

_repo_attr = (
"repo_name" if hasattr(Label("//:all"), "repo_name") else "workspace_name"
)
def _apparent_repo_name(repository_ctx):
"""Generates a repository's apparent name from a repository_ctx object.
Useful when generating the default top level `BUILD` target for the
repository.
def _apparent_repo_name(label_or_name):
"""Return a repository's apparent repository name.
Example:
```starlark
_ALIAS_TARGET_TEMPLATE = \"\"\"alias(
name = "{name}",
actual = "@{target_repo_name}",
visibility = ["//visibility:public"],
)
\"\"\"
def _alias_repository_impl(repository_ctx):
repository_ctx.file("BUILD", _ALIAS_TARGET_TEMPLATE.format(
name = apparent_repo_name(rctx),
target = rctx.attr.target_repo_name,
))
```
Args:
label_or_name: a Label or repository name string
repository_ctx: a repository_ctx object
Returns:
The apparent repository name
An apparent repo name derived from repository_ctx.name
"""
repo_name = getattr(label_or_name, _repo_attr, label_or_name).lstrip("@")
delimiter_indices = []
repo_name = repository_ctx.name

# Bazed on this pattern from the Bazel source:
# com.google.devtools.build.lib.cmdline.RepositoryName.VALID_REPO_NAME
for i in range(len(repo_name)):
for i in range(len(repo_name) - 1, -1, -1):
c = repo_name[i]
if not (c.isalnum() or c in "_-."):
delimiter_indices.append(i)

if len(delimiter_indices) == 0:
# Already an apparent repo name, apparently.
return repo_name

if len(delimiter_indices) == 1:
# The name is for a top level module, possibly containing a version ID.
return repo_name[:delimiter_indices[0]]
return repo_name[i + 1:]

return repo_name[delimiter_indices[-1] + 1:]
return repo_name

modules = struct(
as_extension = _as_extension,
Expand Down
22 changes: 22 additions & 0 deletions tests/extensions/apparent_repo_name.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Repo rule and module extension used to test modules.apparent_repo_name"""

load("//lib:modules.bzl", "modules")

def _apparent_repo_name_test_repo_impl(repository_ctx):
repo_name = modules.apparent_repo_name(repository_ctx)
test_file = "repo-name.bzl"
repository_ctx.file("WORKSPACE")
repository_ctx.file("BUILD", """exports_files(["%s"])""" % test_file)
repository_ctx.file(test_file, "REPO_NAME = \"%s\"" % repo_name)

apparent_repo_name_test_repo = repository_rule(
_apparent_repo_name_test_repo_impl,
)

def apparent_repo_name_test_macro(*args):
apparent_repo_name_test_repo(name = "apparent-repo-name-test")

apparent_repo_name_test_ext = module_extension(
lambda _: apparent_repo_name_test_macro(),
doc = "Only used for testing modules.apparent_repo_name()",
)
86 changes: 5 additions & 81 deletions tests/modules_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""Test usage of modules.bzl."""

load("@apparent-repo-name-test//:repo-name.bzl", "REPO_NAME")
load("//lib:modules.bzl", "modules")
load("//lib:unittest.bzl", "asserts", "unittest")
load("//rules:build_test.bzl", "build_test")
Expand Down Expand Up @@ -54,91 +55,14 @@ def _apparent_repo_name_test(ctx):

asserts.equals(
env,
"",
modules.apparent_repo_name(""),
msg = "Handles the empty string as input",
)

asserts.equals(
env,
"foo",
modules.apparent_repo_name("foo"),
msg = (
"Return the original name unchanged if it doesn't start with `@`.",
),
)

asserts.equals(
env,
"foo",
modules.apparent_repo_name("@foo"),
msg = "Return the original name without `@` if already apparent.",
)

foo_label = Label("@foo")
foo_canonical_name = getattr(
foo_label,
"repo_name" if hasattr(foo_label, "repo_name") else "workspace_name",
)
asserts.equals(
env,
"foo",
modules.apparent_repo_name(foo_canonical_name),
msg = "Return the apparent name from a canonical name string.",
)

asserts.equals(
env,
"",
modules.apparent_repo_name(Label("//:all")),
msg = "Returns the empty string for a main repository Label.",
)

asserts.equals(
env,
"",
modules.apparent_repo_name(Label("@bazel_skylib//:all")),
"apparent-repo-name-test",
REPO_NAME,
msg = " ".join([
"Returns the empty string for a Label containing the main",
"repository's module name.",
"Returns the original name unchanged under WORKSPACE, and",
"the apparent repo name under Bzlmod.",
]),
)

asserts.equals(
env,
"foo",
modules.apparent_repo_name(Label("@foo")),
msg = "Return the apparent name from a Label.",
)

asserts.equals(
env,
"rules_pkg",
modules.apparent_repo_name(Label("@rules_pkg")),
msg = " ".join([
"Top level module repos have the canonical name delimiter at the",
"end. Therefore, this should not return the empty string, but the",
"name without the leading `@` and trailing delimiter.",
]),
)

asserts.equals(
env,
"stardoc" if _is_bzlmod_enabled else "io_bazel_stardoc",
modules.apparent_repo_name(Label("@io_bazel_stardoc")),
msg = " ".join([
"Label values will already map bazel_dep repo_names to",
"actual repo names under Bzlmod (no-op under WORKSPACE).",
]),
)

asserts.equals(
env,
"foo",
modules.apparent_repo_name("foo+1.2.3"),
msg = "Ignores version numbers in canonical repo names",
)

return unittest.end(env)

apparent_repo_name_test = unittest.make(_apparent_repo_name_test)
Expand Down

0 comments on commit d8b3262

Please sign in to comment.