Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally infer name in use_extension and use_repo_rule #22168

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,10 @@ public void registerToolchains(
name = "extension_name",
doc =
"The name of the module extension to use. A symbol with this name must be exported"
+ " by the Starlark file."),
+ " by the Starlark file. If not specified, the name of the rule is understood"
+ " to be the basename of the .bzl file, minus the .bzl extension.",
defaultValue = "unbound",
allowedTypes = {@ParamType(type = String.class)}),
@Param(
name = "dev_dependency",
doc =
Expand Down Expand Up @@ -415,19 +418,25 @@ public void registerToolchains(
useStarlarkThread = true)
public ModuleExtensionProxy useExtension(
String rawExtensionBzlFile,
String extensionName,
Object maybeExtensionName,
boolean devDependency,
boolean isolate,
StarlarkThread thread)
throws EvalException {
ModuleThreadContext context = ModuleThreadContext.fromOrFail(thread, "use_extension()");
context.setNonModuleCalled();

if (extensionName.equals(ModuleExtensionId.INNATE_EXTENSION_NAME)) {
if (maybeExtensionName.equals(ModuleExtensionId.INNATE_EXTENSION_NAME)) {
throw Starlark.errorf(
"innate extensions cannot be directly used; try `use_repo_rule` instead");
}

String extensionName;
if (maybeExtensionName == Starlark.UNBOUND) {
extensionName = inferName(rawExtensionBzlFile);
} else {
extensionName = (String) maybeExtensionName;
}
String extensionBzlFile = normalizeLabelString(context.getModuleBuilder(), rawExtensionBzlFile);
ModuleExtensionUsageBuilder newUsageBuilder =
new ModuleExtensionUsageBuilder(
Expand Down Expand Up @@ -504,6 +513,27 @@ private Object parseOverrideLabelAttribute(InterimModule.Builder module, String
}
}

private static String inferName(String extensionBzlFile) throws EvalException {
try {
// We do not care about the canonical repository name here.
Label.PackageContext packageContext =
Label.PackageContext.of(
PackageIdentifier.EMPTY_PACKAGE_ID, RepositoryMapping.ALWAYS_FALLBACK);
String bzlFileName =
Label.parseWithPackageContext(extensionBzlFile, packageContext).getName();
if (!bzlFileName.endsWith(".bzl")) {
throw Starlark.errorf(
"The extension file '%s' must have a .bzl extension", extensionBzlFile);
}
// Turn "foo/bar/baz.bzl" into "baz".
return bzlFileName.substring(
bzlFileName.lastIndexOf('/') + 1, bzlFileName.length() - ".bzl".length());
} catch (LabelSyntaxException e) {
throw Starlark.errorf(
"The extension file '%s' is not a valid label: %s", extensionBzlFile, e.getMessage());
}
}

@StarlarkBuiltin(name = "module_extension_proxy", documented = false)
static class ModuleExtensionProxy implements Structure, StarlarkExportable {
private final ModuleExtensionUsageBuilder usageBuilder;
Expand Down Expand Up @@ -639,13 +669,22 @@ public void useRepo(
name = "repo_rule_name",
doc =
"The name of the repo rule to use. A symbol with this name must be exported by the"
+ " Starlark file."),
+ " Starlark file. If not specified, the name of the rule is understood to be"
+ " the basename of the .bzl file, minus the .bzl extension.",
defaultValue = "unbound",
allowedTypes = {@ParamType(type = String.class)}),
},
useStarlarkThread = true)
public RepoRuleProxy useRepoRule(String bzlFile, String ruleName, StarlarkThread thread)
public RepoRuleProxy useRepoRule(String bzlFile, Object maybeRuleName, StarlarkThread thread)
throws EvalException {
ModuleThreadContext context = ModuleThreadContext.fromOrFail(thread, "use_repo_rule()");
context.setNonModuleCalled();
String ruleName;
if (maybeRuleName == Starlark.UNBOUND) {
ruleName = inferName(bzlFile);
} else {
ruleName = (String) maybeRuleName;
}
// The builder for the singular "innate" extension of this module. Note that there's only one
// such usage (and it's fabricated), so the usage location just points to this file.
ModuleExtensionUsageBuilder newUsageBuilder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey;
import static java.util.stream.Collectors.flatMapping;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;

Expand Down Expand Up @@ -73,6 +76,7 @@
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
Expand Down Expand Up @@ -1475,4 +1479,85 @@ public void isolatedUsage_notEnabled() throws Exception {
+ "and thus unavailable with the current flags. It may be enabled by setting "
+ "--experimental_isolated_extension_usages");
}

@Test
public void inferredExtensionName() throws Exception {
scratch.overwriteFile(
rootDirectory.getRelative("MODULE.bazel").getPathString(),
"ext1 = use_extension('@rules_bbb//bbb/extensions:bbb_deps.bzl', 'bbb_deps')",
"ext1.tag1()",
"ext2 = use_extension('@rules_bbb//bbb/extensions:bbb_deps.bzl')",
"ext2.tag2()",
"ext3 = use_extension('@rules_bbb//bbb/extensions:foo/bar/bbb_deps.bzl', 'bbb_deps')",
"ext3.tag3()",
"ext4 = use_extension('@rules_bbb//bbb/extensions:foo/bar/bbb_deps.bzl')",
"ext4.tag4()",
"ext5 = use_extension('@ccc_deps.bzl', 'ccc_deps')",
"ext5.tag5()",
"ext6 = use_extension('@ccc_deps.bzl')",
"ext6.tag6()",
"rule1 = use_repo_rule('@rules_ddd//ddd:ddd_repo.bzl')",
"rule1(name = 'repo1')",
"rule2 = use_repo_rule('@rules_ddd//ddd:foo/bar/ddd_repo.bzl')",
"rule2(name = 'repo2')",
"rule3 = use_repo_rule('@ccc_repo.bzl')",
"rule3(name = 'repo3')");
FakeRegistry registry = registryFactory.newFakeRegistry("/foo");
ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));

EvaluationResult<RootModuleFileValue> result =
evaluator.evaluate(
ImmutableList.of(ModuleFileValue.KEY_FOR_ROOT_MODULE), evaluationContext);
if (result.hasError()) {
fail(result.getError().toString());
}
RootModuleFileValue rootModuleFileValue = result.get(ModuleFileValue.KEY_FOR_ROOT_MODULE);
var tagsByExtension =
rootModuleFileValue.getModule().getExtensionUsages().stream()
.collect(
groupingBy(
usage -> usage.getExtensionBzlFile() + "%" + usage.getExtensionName(),
flatMapping(usage -> usage.getTags().stream().map(Tag::getTagName), toList())));
assertThat(tagsByExtension)
.containsExactly(
"@rules_bbb//bbb/extensions:bbb_deps.bzl%bbb_deps",
List.of("tag1", "tag2"),
"@rules_bbb//bbb/extensions:foo/bar/bbb_deps.bzl%bbb_deps",
List.of("tag3", "tag4"),
"@ccc_deps.bzl//:ccc_deps.bzl%ccc_deps",
List.of("tag5", "tag6"),
"//:MODULE.bazel%_repo_rules",
List.of(
"@rules_ddd//ddd:ddd_repo.bzl%ddd_repo",
"@rules_ddd//ddd:foo/bar/ddd_repo.bzl%ddd_repo", "@ccc_repo.bzl%ccc_repo"));
}

@Test
public void inferredExtensionName_noBzlExtension() throws Exception {
scratch.overwriteFile(
rootDirectory.getRelative("MODULE.bazel").getPathString(),
"use_extension('//:extensions')");
FakeRegistry registry = registryFactory.newFakeRegistry("/foo");
ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));

reporter.removeHandler(failFastHandler); // expect failures
evaluator.evaluate(ImmutableList.of(ModuleFileValue.KEY_FOR_ROOT_MODULE), evaluationContext);
assertContainsEvent(
"Error in use_extension: The extension file '//:extensions' must have a .bzl extension");
}

@Test
public void inferredExtensionName_invalidLabel() throws Exception {
scratch.overwriteFile(
rootDirectory.getRelative("MODULE.bazel").getPathString(), "use_extension('@:extensions')");
FakeRegistry registry = registryFactory.newFakeRegistry("/foo");
ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl()));

reporter.removeHandler(failFastHandler); // expect failures
evaluator.evaluate(ImmutableList.of(ModuleFileValue.KEY_FOR_ROOT_MODULE), evaluationContext);
assertContainsEvent(
"Error in use_extension: The extension file '@:extensions' is not a valid label: invalid "
+ "repository name ':extensions': repo names may contain only A-Z, a-z, 0-9, '-', '_', '.' and '~' "
+ "and must not start with '~'");
}
}
Loading