Skip to content

Commit

Permalink
New mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Aug 20, 2024
1 parent e395f26 commit f583cca
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 14 deletions.
8 changes: 8 additions & 0 deletions site/en/external/extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,14 @@ several repo visibility rules:
the apparent name `foo`, and the extension generates a repo with the
specified name `foo`, then for all repos generated by that extension
`foo` refers to the former.
* Similarly, in a module extension's implementation function, repos created
by the extension can refer to each other by their apparent names in
attributes, regardless of the order in which they are created.
* In case of a conflict with a repository visible to the module, labels
passed to repository rule attributes can be wrapped in a call to
[`Label`](/rules/lib/toplevel/attr#label) to ensure that they refer to
the repo visible to the module instead of the repo generated by the
extension of the same name.

## Best practices

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,31 @@ record RepoRuleCall(

private final String repoPrefix;
private final PackageIdentifier basePackageId;
private final RepositoryMapping repoMapping;
private final RepositoryMapping baseRepoMapping;
private final BlazeDirectories directories;
private final ExtendedEventHandler eventHandler;
private final Map<String, RepoRuleCall> deferredRepos = new LinkedHashMap<>();

public ModuleExtensionEvalStarlarkThreadContext(
String repoPrefix,
PackageIdentifier basePackageId,
RepositoryMapping repoMapping,
RepositoryMapping baseRepoMapping,
RepositoryMapping mainRepoMapping,
BlazeDirectories directories,
ExtendedEventHandler eventHandler) {
super(() -> mainRepoMapping);
this.repoPrefix = repoPrefix;
this.basePackageId = basePackageId;
this.repoMapping = repoMapping;
this.baseRepoMapping = baseRepoMapping;
this.directories = directories;
this.eventHandler = eventHandler;
}

public void lazilyCreateRepos(
/**
* Records a call to a repo rule that should be created at the end of the module extension
* evaluation.
*/
public void lazilyCreateRepo(
StarlarkThread thread, Dict<String, Object> kwargs, RuleClass ruleClass)
throws EvalException {
Object nameValue = kwargs.getOrDefault("name", Starlark.NONE);
Expand All @@ -106,18 +110,34 @@ public void lazilyCreateRepos(
name,
new RepoRuleCall(
ruleClass,
// The extension may mutate the values of the kwargs after this function returns.
(Dict<String, Object>) deepCloneStarlarkObject(kwargs),
thread.getCallerLocation(),
thread.getCallStack()));
}

/**
* Returns the repos generated by the extension so far. The key is the "internal name" (as
* specified by the extension) of the repo, and the value is the package containing (only) the
* repo rule.
* Evaluates the repo rule calls recorded by {@link #lazilyCreateRepo} and returns all repos
* generated by the extension. The key is the "internal name" (as specified by the extension) of
* the repo, and the value is the package containing (only) the repo rule.
*/
public ImmutableMap<String, RepoSpec> createAndGetRepos(StarlarkSemantics starlarkSemantics)
throws EvalException, InterruptedException {
// LINT.IfChange
// Make it possible to refer to extension repos in the label attributes of another extension
// repo. Wrapping a label in Label(...) ensures that it is evaluated with respect to the
// containing module's repo mapping instead.
ImmutableMap.Builder<String, RepositoryName> fullRepoMappingEntries = ImmutableMap.builder();
fullRepoMappingEntries.putAll(baseRepoMapping.entries());
fullRepoMappingEntries.putAll(
Maps.asMap(
deferredRepos.keySet(),
apparentName -> RepositoryName.createUnvalidated(repoPrefix + apparentName)));
RepositoryMapping fullRepoMapping =
RepositoryMapping.create(
fullRepoMappingEntries.buildKeepingLast(), baseRepoMapping.ownerRepo());
// LINT.ThenChange(//src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionRepoMappingEntriesFunction.java)

ImmutableMap.Builder<String, RepoSpec> repoSpecs = ImmutableMap.builder();
for (var entry : deferredRepos.entrySet()) {
String name = entry.getKey();
Expand All @@ -127,7 +147,7 @@ public ImmutableMap<String, RepoSpec> createAndGetRepos(StarlarkSemantics starla
Rule rule =
BzlmodRepoRuleCreator.createRule(
basePackageId,
repoMapping,
fullRepoMapping,
directories,
starlarkSemantics,
eventHandler,
Expand Down Expand Up @@ -162,6 +182,7 @@ public ImmutableMap<String, RepoSpec> createAndGetRepos(StarlarkSemantics starla
return repoSpecs.buildOrThrow();
}

/** Deep-clones a potentially mutable Starlark object. Immutable (sub-)objects are not cloned. */
private static Object deepCloneStarlarkObject(Object x) {
if (x instanceof String || x instanceof Boolean) {
return x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@ private ModuleExtensionRepoMappingEntriesValue computeRepoMappingEntries(
// extension generates an internal repo name "bar", then within a repo generated by the
// extension, "bar" will refer to the latter. We should explore a way to differentiate between
// the two to avoid any surprises.
// LINT.IfChange
ImmutableMap.Builder<String, RepositoryName> entries = ImmutableMap.builder();
entries.putAll(bazelDepGraphValue.getFullRepoMapping(moduleKey).entries());
entries.putAll(extensionEvalValue.getCanonicalRepoNameToInternalNames().inverse());
return ModuleExtensionRepoMappingEntriesValue.create(entries.buildKeepingLast(), moduleKey);
// LINT.ThenChange(//src/main/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionEvalStarlarkThreadContext.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ RunModuleExtensionResult run(
SingleExtensionUsagesValue usagesValue,
StarlarkSemantics starlarkSemantics,
ModuleExtensionId extensionId,
RepositoryMapping repositoryMapping)
RepositoryMapping mainRepositoryMapping)
throws InterruptedException, SingleExtensionEvalFunctionException;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public Object call(StarlarkThread thread, Tuple args, Dict<String, Object> kwarg
if (!isExported()) {
throw new EvalException("attempting to instantiate a non-exported repository rule");
}
extensionEvalContext.lazilyCreateRepos(thread, kwargs, getRuleClass());
extensionEvalContext.lazilyCreateRepo(thread, kwargs, getRuleClass());
return Starlark.NONE;
}

Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/starlark/java/eval/EvalException.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public EvalException(Throwable cause) {
super(getCauseMessage(cause), cause);
}

/**
* Constructs an EvalException using the same message as the cause exception and with the given
* callstack.
*/
public EvalException(Throwable cause, List<StarlarkThread.CallStackEntry> callstack) {
this(cause);
this.callstack = ImmutableList.copyOf(callstack);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3066,17 +3066,17 @@ def _other_ext_impl(ctx):
}
assertThat((List<?>) result.get(skyKey).getModule().getGlobal("foo_list"))
.containsExactly(
"@@+other_ext+other_foo//:target1",
"@@+other_ext+other_bar//:target2",
"@@+ext+foo//:target1",
"@@+ext+bar//:target2",
"@@+other_ext+other_foo//:target3",
"@@+other_ext+other_bar//:target4",
"@@+other_ext+other_foo//:foo",
"@@+other_ext+other_bar//:bar")
.inOrder();
assertThat((List<?>) result.get(skyKey).getModule().getGlobal("bar_list"))
.containsExactly(
"@@+other_ext+other_foo//:target5",
"@@+other_ext+other_bar//:target6",
"@@+ext+foo//:target5",
"@@+ext+bar//:target6",
"@@+other_ext+other_foo//:target7",
"@@+other_ext+other_bar//:target8",
"@@+other_ext+other_foo//:foo",
Expand Down

0 comments on commit f583cca

Please sign in to comment.