diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java index 3cab859e9e31cf..4da8c8598a4a85 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryContext.java @@ -30,6 +30,7 @@ import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.StructImpl; import com.google.devtools.build.lib.packages.StructProvider; +import com.google.devtools.build.lib.packages.Type; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.repository.RepositoryFetchProgress; import com.google.devtools.build.lib.rules.repository.NeedsSkyframeRestartException; @@ -143,11 +144,33 @@ public ImmutableMap getRecordedDirTreeInputs( @StarlarkMethod( name = "name", structField = true, - doc = "The name of the external repository created by this rule.") + doc = + "The canonical name of the external repository created by this rule. This name is" + + " guaranteed to be unique among all external repositories, but its exact format is" + + " not specified. Use original_name" + + " instead to get the name that was originally specified as the name" + + " when this repository rule was instantiated.") public String getName() { return rule.getName(); } + @StarlarkMethod( + name = "original_name", + structField = true, + doc = + "The name that was originally specified as the name attribute when this" + + " repository rule was instantiated. This name is not necessarily unique among" + + " external repositories. Use name instead to get" + + " the canonical name of the external repository.") + public String getOriginalName() { + String originalName = (String) rule.getAttr("$original_name", Type.STRING); + // The original name isn't set for WORKSPACE-defined repositories as well as repositories + // backing Bazel modules. In case of the former, the original name is the same as the name, in + // the latter the original name doesn't matter as the restricted set of rules that can back + // Bazel modules do not use the name. + return originalName != null ? originalName : rule.getName(); + } + @StarlarkMethod( name = "workspace_root", structField = true, diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java index f759d43aae92dd..1c69c2a37d4cef 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkRepositoryModule.java @@ -81,6 +81,7 @@ public StarlarkCallable repositoryRule( builder.setCallStack( callstack.subList(0, callstack.size() - 1)); // pop 'repository_rule' itself + builder.addAttribute(attr("$original_name", STRING).defaultValue("").build()); builder.addAttribute(attr("$local", BOOLEAN).defaultValue(local).build()); builder.addAttribute(attr("$configure", BOOLEAN).defaultValue(configure).build()); if (thread.getSemantics().getBool(BuildLanguageOptions.EXPERIMENTAL_REPO_REMOTE_EXEC)) { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java index 030fb990502419..a15b53307bb2a6 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BzlmodRepoRuleFunction.java @@ -101,7 +101,12 @@ public SkyValue compute(SkyKey skyKey, Environment env) Optional repoSpec = checkRepoFromNonRegistryOverrides(root, repositoryName); if (repoSpec.isPresent()) { return createRuleFromSpec( - repoSpec.get(), repositoryName, basicMainRepoMapping, starlarkSemantics, env); + repoSpec.get(), + repositoryName, + /* originalName= */ null, + basicMainRepoMapping, + starlarkSemantics, + env); } // BazelDepGraphValue is affected by repos found in Step 1, therefore it should NOT @@ -116,7 +121,12 @@ public SkyValue compute(SkyKey skyKey, Environment env) repoSpec = checkRepoFromBazelModules(bazelDepGraphValue, repositoryName); if (repoSpec.isPresent()) { return createRuleFromSpec( - repoSpec.get(), repositoryName, basicMainRepoMapping, starlarkSemantics, env); + repoSpec.get(), + repositoryName, + /* originalName= */ null, + basicMainRepoMapping, + starlarkSemantics, + env); } // Step 3: look for the repo from module extension evaluation results. @@ -142,7 +152,7 @@ public SkyValue compute(SkyKey skyKey, Environment env) } RepoSpec extRepoSpec = extensionValue.generatedRepoSpecs().get(internalRepo); return createRuleFromSpec( - extRepoSpec, repositoryName, basicMainRepoMapping, starlarkSemantics, env); + extRepoSpec, repositoryName, internalRepo, basicMainRepoMapping, starlarkSemantics, env); } private static Optional checkRepoFromNonRegistryOverrides( @@ -168,6 +178,7 @@ private Optional checkRepoFromBazelModules( private BzlmodRepoRuleValue createRuleFromSpec( RepoSpec repoSpec, RepositoryName repositoryName, + @Nullable String originalName, RepositoryMapping basicMainRepoMapping, StarlarkSemantics starlarkSemantics, Environment env) @@ -177,11 +188,13 @@ private BzlmodRepoRuleValue createRuleFromSpec( return null; } - var attributes = + var attributesBuilder = ImmutableMap.builder() .putAll(repoSpec.attributes().attributes()) - .put("name", repositoryName.getName()) - .buildOrThrow(); + .put("name", repositoryName.getName()); + if (originalName != null) { + attributesBuilder.put("$original_name", originalName); + } try { Rule rule = BzlmodRepoRuleCreator.createRule( @@ -194,7 +207,7 @@ private BzlmodRepoRuleValue createRuleFromSpec( StarlarkThread.callStackEntry( "BzlmodRepoRuleFunction.createRuleFromSpec", Location.BUILTIN)), ruleClass, - attributes); + attributesBuilder.buildOrThrow()); return new BzlmodRepoRuleValue(rule.getPackage(), rule.getName()); } catch (InvalidRuleException e) { throw new BzlmodRepoRuleFunctionException(e, Transience.PERSISTENT); diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java index b6b518e2cd3af5..9caf8c94b4a637 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java @@ -300,6 +300,13 @@ public void setup() throws Exception { " ctx.file('WORKSPACE')", " ctx.file('BUILD')", " ctx.file('data.bzl', 'data = '+json.encode(ctx.attr.data))", + " ctx.file(", + " 'names.bzl',", + " 'names='+json.encode({", + " 'name': ctx.name,", + " 'original_name': ctx.original_name,", + " })", + " )", "data_repo = repository_rule(", " implementation=_data_repo_impl,", " attrs={'data':attr.string()})"); @@ -328,8 +335,12 @@ public void simpleExtension() throws Exception { scratch.file( workspaceRoot.getRelative("data.bzl").getPathString(), "load('@foo//:data.bzl', foo_data='data')", + "load('@foo//:names.bzl', foo_names='names')", "load('@bar//:data.bzl', bar_data='data')", - "data = 'foo:'+foo_data+' bar:'+bar_data"); + "load('@bar//:names.bzl', bar_names='names')", + "data = 'foo:'+foo_data+' bar:'+bar_data", + "names = 'foo:'+foo_names['name']+' bar:'+bar_names['name']", + "original_names = 'foo:'+foo_names['original_name']+' bar:'+bar_names['original_name']"); SkyKey skyKey = BzlLoadValue.keyForBuild(Label.parseCanonical("//:data.bzl")); EvaluationResult result = @@ -338,6 +349,10 @@ public void simpleExtension() throws Exception { throw result.getError().getException(); } assertThat(result.get(skyKey).getModule().getGlobal("data")).isEqualTo("foo:fu bar:ba"); + assertThat(result.get(skyKey).getModule().getGlobal("names")) + .isEqualTo("foo:+ext+foo bar:+ext+bar"); + assertThat(result.get(skyKey).getModule().getGlobal("original_names")) + .isEqualTo("foo:foo bar:bar"); } @Test @@ -2896,30 +2911,45 @@ public void innate() throws Exception { workspaceRoot.getRelative("MODULE.bazel").getPathString(), "bazel_dep(name='foo',version='1.0')", "data_repo = use_repo_rule('@foo//:repo.bzl', 'data_repo')", - "data_repo(name='data', data='get up at 6am.')"); + "data_repo(name='data1', data='get up at 6am.')"); scratch.file(workspaceRoot.getRelative("BUILD").getPathString()); scratch.file( workspaceRoot.getRelative("data.bzl").getPathString(), - "load('@data//:data.bzl', self_data='data')", + "load('@data1//:data.bzl', self_data='data')", + "load('@data1//:names.bzl', self_names='names')", "load('@foo//:data.bzl', foo_data='data')", - "data=self_data+' '+foo_data"); + "load('@foo//:names.bzl', foo_names='names')", + "data=self_data+' '+foo_data", + "names=self_names['name']+' '+foo_names['name']", + "original_names=self_names['original_name']+' '+foo_names['original_name']"); registry.addModule( createModuleKey("foo", "1.0"), "module(name='foo',version='1.0')", "data_repo = use_repo_rule('//:repo.bzl', 'data_repo')", - "data_repo(name='data', data='go to bed at 11pm.')"); + "data_repo(name='data2', data='go to bed at 11pm.')"); scratch.file(modulesRoot.getRelative("foo+1.0/WORKSPACE").getPathString()); scratch.file(modulesRoot.getRelative("foo+1.0/BUILD").getPathString()); scratch.file( modulesRoot.getRelative("foo+1.0/data.bzl").getPathString(), - "load('@data//:data.bzl',repo_data='data')", + "load('@data2//:data.bzl',repo_data='data')", "data=repo_data"); + scratch.file( + modulesRoot.getRelative("foo+1.0/names.bzl").getPathString(), + "load('@data2//:names.bzl',repo_names='names')", + "names=repo_names"); scratch.file( modulesRoot.getRelative("foo+1.0/repo.bzl").getPathString(), "def _data_repo_impl(ctx):", " ctx.file('BUILD.bazel')", " ctx.file('data.bzl', 'data='+json.encode(ctx.attr.data))", + " ctx.file(", + " 'names.bzl',", + " 'names='+json.encode({", + " 'name': ctx.name,", + " 'original_name': ctx.original_name,", + " })", + " )", "data_repo = repository_rule(", " implementation=_data_repo_impl, attrs={'data':attr.string()})"); @@ -2931,6 +2961,9 @@ public void innate() throws Exception { } assertThat(result.get(skyKey).getModule().getGlobal("data")) .isEqualTo("get up at 6am. go to bed at 11pm."); + assertThat(result.get(skyKey).getModule().getGlobal("names")) + .isEqualTo("+_repo_rules+data1 foo++_repo_rules+data2"); + assertThat(result.get(skyKey).getModule().getGlobal("original_names")).isEqualTo("data1 data2"); } @Test diff --git a/src/test/py/bazel/bzlmod/mod_command_test.py b/src/test/py/bazel/bzlmod/mod_command_test.py index e62ae55debde1f..b941081e4c6770 100644 --- a/src/test/py/bazel/bzlmod/mod_command_test.py +++ b/src/test/py/bazel/bzlmod/mod_command_test.py @@ -467,13 +467,13 @@ def testShowModuleAndExtensionReposFromBaseModule(self): self.assertRegex(stdout.pop(4), r'^ integrity = ".*",$') self.assertRegex(stdout.pop(19), r'^ path = ".*",$') # lines after 'Rule data_repo defined at (most recent call last):' - stdout.pop(32) - stdout.pop(42) - self.assertRegex(stdout.pop(47), r'^ urls = \[".*"\],$') - self.assertRegex(stdout.pop(47), r'^ integrity = ".*",$') + stdout.pop(33) + stdout.pop(44) + self.assertRegex(stdout.pop(49), r'^ urls = \[".*"\],$') + self.assertRegex(stdout.pop(49), r'^ integrity = ".*",$') # lines after '# Rule http_archive defined at (most recent call last):' stdout.pop(13) - stdout.pop(55) + stdout.pop(57) self.assertListEqual( stdout, [ @@ -507,30 +507,32 @@ def testShowModuleAndExtensionReposFromBaseModule(self): '# ', 'data_repo(', ' name = "ext++ext+repo3",', + ' _original_name = "repo3",', ' data = "requested repo",', ')', '# Rule ext++ext+repo3 instantiated at (most recent call last):', '# in ', '# Rule data_repo defined at (most recent call last):', - # pop(32) + # pop(33) '', '## @my_repo4:', '# ', 'data_repo(', ' name = "ext++ext+repo4",', + ' _original_name = "repo4",', ' data = "requested repo",', ')', '# Rule ext++ext+repo4 instantiated at (most recent call last):', '# in ', '# Rule data_repo defined at (most recent call last):', - # pop(42) + # pop(44) '', '## bar@2.0:', '# ', 'http_archive(', ' name = "bar+",', - # pop(47) -- urls=[...] - # pop(47) -- integrity=... + # pop(49) -- urls=[...] + # pop(49) -- integrity=... ' strip_prefix = "",', ' remote_file_urls = {},', ' remote_file_integrity = {},', @@ -540,7 +542,7 @@ def testShowModuleAndExtensionReposFromBaseModule(self): '# Rule bar+ instantiated at (most recent call last):', '# in ', '# Rule http_archive defined at (most recent call last):', - # pop(55) + # pop(57) '', ], 'wrong output in the show query for module and extension-generated'