Update AspectFunction to perform toolchain resolution for multiple exec groups.
RELNOTES: Aspects can now define and use exec groups using the same API as rules.
PiperOrigin-RevId: 453177050
Change-Id: Ia84ff906ede72cadfb6b6acf9deac7c55280bbe5
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index bd86160..af0c8b1 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -490,7 +490,8 @@
Aspect aspect,
OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> prerequisiteMap,
ConfigConditions configConditions,
- @Nullable ResolvedToolchainContext toolchainContext,
+ @Nullable ToolchainCollection<ResolvedToolchainContext> toolchainContexts,
+ @Nullable ExecGroupCollection.Builder execGroupCollectionBuilder,
BuildConfigurationValue aspectConfiguration,
BuildConfigurationValue hostConfiguration,
AspectKeyCreator.AspectKey aspectKey)
@@ -508,10 +509,8 @@
.setPrerequisites(transformPrerequisiteMap(prerequisiteMap))
.setAspectAttributes(mergeAspectAttributes(aspectPath))
.setConfigConditions(configConditions)
- .setToolchainContext(toolchainContext)
- // TODO(b/161222568): Implement the exec_properties attr for aspects and read its value
- // here.
- .setExecGroupCollectionBuilder(ExecGroupCollection.emptyBuilder())
+ .setToolchainContexts(toolchainContexts)
+ .setExecGroupCollectionBuilder(execGroupCollectionBuilder)
.setExecProperties(ImmutableMap.of())
.setRequiredConfigFragments(
RequiredFragmentsUtil.getAspectRequiredFragmentsIfEnabled(
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 5e40f16..ec41d70 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -1792,18 +1792,6 @@
return this;
}
- /** Sets the {@link ResolvedToolchainContext} used to access toolchains used by this rule. */
- public Builder setToolchainContext(ResolvedToolchainContext toolchainContext) {
- Preconditions.checkState(
- this.toolchainContexts == null,
- "toolchainContexts has already been set for this Builder");
- this.toolchainContexts =
- ToolchainCollection.<ResolvedToolchainContext>builder()
- .addDefaultContext(toolchainContext)
- .build();
- return this;
- }
-
/** Sets the collection of {@link ResolvedToolchainContext}s available to this rule. */
@VisibleForTesting
public Builder setToolchainContexts(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index 326b851..bba79c9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -17,6 +17,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -35,6 +36,7 @@
import com.google.devtools.build.lib.analysis.DependencyKey;
import com.google.devtools.build.lib.analysis.DependencyKind;
import com.google.devtools.build.lib.analysis.DuplicateException;
+import com.google.devtools.build.lib.analysis.ExecGroupCollection;
import com.google.devtools.build.lib.analysis.ExecGroupCollection.InvalidExecGroupException;
import com.google.devtools.build.lib.analysis.InconsistentAspectOrderException;
import com.google.devtools.build.lib.analysis.ResolvedToolchainContext;
@@ -78,6 +80,7 @@
import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey;
import com.google.devtools.build.lib.skyframe.BzlLoadFunction.BzlLoadFailedException;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ComputeDependenciesState;
+import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ComputedToolchainContexts;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider;
import com.google.devtools.build.lib.util.OrderedSetMultimap;
import com.google.devtools.build.skyframe.SkyFunction;
@@ -88,7 +91,9 @@
import com.google.devtools.build.skyframe.SkyframeLookupResult;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import javax.annotation.Nullable;
import net.starlark.java.eval.StarlarkSemantics;
@@ -271,19 +276,28 @@
TargetAndConfiguration originalTargetAndConfiguration =
new TargetAndConfiguration(target, configuration);
try {
- UnloadedToolchainContext unloadedToolchainContext =
- getUnloadedToolchainContext(env, key, aspect, configuration);
+ ConfiguredTargetFunction.ComputedToolchainContexts computedToolchainContexts =
+ getUnloadedToolchainContexts(env, key, aspect, configuration);
if (env.valuesMissing()) {
return null;
}
+ ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts = null;
+ ExecGroupCollection.Builder execGroupCollectionBuilder = null;
+ if (computedToolchainContexts != null) {
+ unloadedToolchainContexts = computedToolchainContexts.toolchainCollection;
+ execGroupCollectionBuilder = computedToolchainContexts.execGroupCollectionBuilder;
+ }
+
// Get the configuration targets that trigger this rule's configurable attributes.
ConfigConditions configConditions =
ConfiguredTargetFunction.getConfigConditions(
env,
originalTargetAndConfiguration,
state.transitivePackagesForPackageRootResolution,
- unloadedToolchainContext == null ? null : unloadedToolchainContext.targetPlatform(),
+ unloadedToolchainContexts == null
+ ? null
+ : unloadedToolchainContexts.getTargetPlatform(),
state.transitiveRootCauses);
if (configConditions == null) {
// Those targets haven't yet been resolved.
@@ -302,11 +316,9 @@
originalTargetAndConfiguration,
topologicalAspectPath,
configConditions.asProviders(),
- unloadedToolchainContext == null
+ unloadedToolchainContexts == null
? null
- : ToolchainCollection.builder()
- .addDefaultContext(unloadedToolchainContext)
- .build(),
+ : unloadedToolchainContexts.asToolchainContexts(),
ruleClassProvider,
buildViewProvider.getSkyframeBuildView().getHostConfiguration());
} catch (ConfiguredValueCreationException e) {
@@ -326,17 +338,23 @@
}
// Load the requested toolchains into the ToolchainContext, now that we have dependencies.
- ResolvedToolchainContext toolchainContext = null;
- if (unloadedToolchainContext != null) {
+ ToolchainCollection<ResolvedToolchainContext> toolchainContexts = null;
+ if (unloadedToolchainContexts != null) {
String targetDescription =
String.format(
"aspect %s applied to %s", aspect.getDescriptor().getDescription(), target);
- toolchainContext =
- ResolvedToolchainContext.load(
- unloadedToolchainContext,
- targetDescription,
- // TODO(161222568): Support exec groups on aspects.
- depValueMap.get(DependencyKind.defaultExecGroupToolchain()));
+ ToolchainCollection.Builder<ResolvedToolchainContext> contextsBuilder =
+ ToolchainCollection.builder();
+ for (Map.Entry<String, UnloadedToolchainContext> unloadedContext :
+ unloadedToolchainContexts.getContextMap().entrySet()) {
+ Set<ConfiguredTargetAndData> toolchainDependencies =
+ depValueMap.get(DependencyKind.forExecGroup(unloadedContext.getKey()));
+ contextsBuilder.addContext(
+ unloadedContext.getKey(),
+ ResolvedToolchainContext.load(
+ unloadedContext.getValue(), targetDescription, toolchainDependencies));
+ }
+ toolchainContexts = contextsBuilder.build();
}
return createAspect(
@@ -349,7 +367,8 @@
associatedTarget, target, configuration, /*transitionKeys=*/ null),
configuration,
configConditions,
- toolchainContext,
+ toolchainContexts,
+ execGroupCollectionBuilder,
depValueMap,
state.transitivePackagesForPackageRootResolution);
} catch (DependencyEvaluationException e) {
@@ -537,36 +556,34 @@
}
@Nullable
- private static UnloadedToolchainContext getUnloadedToolchainContext(
+ private static ComputedToolchainContexts getUnloadedToolchainContexts(
Environment env,
AspectKey key,
Aspect aspect,
@Nullable BuildConfigurationValue configuration)
throws InterruptedException, AspectCreationException {
- // Determine what toolchains are needed by this target.
- UnloadedToolchainContext unloadedToolchainContext = null;
- if (configuration != null) {
+ if (configuration == null) {
// Configuration can be null in the case of aspects applied to input files. In this case,
// there are no chances of toolchains being used, so skip it.
- try {
- unloadedToolchainContext =
- (UnloadedToolchainContext)
- env.getValueOrThrow(
- ToolchainContextKey.key()
- .configurationKey(configuration.getKey())
- .toolchainTypes(aspect.getDefinition().getToolchainTypes())
- .build(),
- ToolchainException.class);
- } catch (ToolchainException e) {
- // TODO(katre): better error handling
- throw new AspectCreationException(
- e.getMessage(), new LabelCause(key.getLabel(), e.getDetailedExitCode()));
- }
- }
- if (env.valuesMissing()) {
return null;
}
- return unloadedToolchainContext;
+ // Determine what toolchains are needed by this target.
+ try {
+ return ConfiguredTargetFunction.computeUnloadedToolchainContexts(
+ env,
+ key.getLabel(),
+ true,
+ Predicates.alwaysFalse(),
+ configuration.getKey(),
+ aspect.getDefinition().getToolchainTypes(),
+ aspect.getDefinition().execCompatibleWith(),
+ aspect.getDefinition().execGroups(),
+ null);
+ } catch (ToolchainException e) {
+ // TODO(katre): better error handling
+ throw new AspectCreationException(
+ e.getMessage(), new LabelCause(key.getLabel(), e.getDetailedExitCode()));
+ }
}
/**
@@ -613,12 +630,15 @@
// Compute the Dependency from originalTarget to aliasedLabel
Dependency dep;
try {
- UnloadedToolchainContext unloadedToolchainContext =
- getUnloadedToolchainContext(env, originalKey, aspect, originalTarget.getConfiguration());
+ ConfiguredTargetFunction.ComputedToolchainContexts computedToolchainContexts =
+ getUnloadedToolchainContexts(env, originalKey, aspect, originalTarget.getConfiguration());
if (env.valuesMissing()) {
return null;
}
+ ToolchainCollection<UnloadedToolchainContext> unloadedToolchainContexts =
+ computedToolchainContexts.toolchainCollection;
+
// See comment in compute() above for why we pair target with aspectConfiguration here
TargetAndConfiguration originalTargetAndAspectConfiguration =
new TargetAndConfiguration(originalTarget.getTarget(), aspectConfiguration);
@@ -629,7 +649,9 @@
env,
originalTargetAndAspectConfiguration,
transitivePackagesForPackageRootResolution,
- unloadedToolchainContext == null ? null : unloadedToolchainContext.targetPlatform(),
+ unloadedToolchainContexts == null
+ ? null
+ : unloadedToolchainContexts.getTargetPlatform(),
transitiveRootCauses);
if (configConditions == null) {
// Those targets haven't yet been resolved.
@@ -800,7 +822,8 @@
ConfiguredTargetAndData associatedTarget,
BuildConfigurationValue configuration,
ConfigConditions configConditions,
- ResolvedToolchainContext toolchainContext,
+ @Nullable ToolchainCollection<ResolvedToolchainContext> toolchainContexts,
+ @Nullable ExecGroupCollection.Builder execGroupCollectionBuilder,
OrderedSetMultimap<DependencyKind, ConfiguredTargetAndData> directDeps,
@Nullable NestedSetBuilder<Package> transitivePackagesForPackageRootResolution)
throws AspectFunctionException, InterruptedException {
@@ -842,7 +865,8 @@
aspect,
directDeps,
configConditions,
- toolchainContext,
+ toolchainContexts,
+ execGroupCollectionBuilder,
configuration,
view.getHostConfiguration(),
key);
diff --git a/src/test/shell/integration/BUILD b/src/test/shell/integration/BUILD
index deb7a23..c5149a8 100644
--- a/src/test/shell/integration/BUILD
+++ b/src/test/shell/integration/BUILD
@@ -806,6 +806,7 @@
":test-deps",
"@bazel_tools//tools/bash/runfiles",
],
+ shard_count = 5,
)
sh_test(
diff --git a/src/test/shell/integration/exec_group_test.sh b/src/test/shell/integration/exec_group_test.sh
index 7a93e73..9e1e648 100755
--- a/src/test/shell/integration/exec_group_test.sh
+++ b/src/test/shell/integration/exec_group_test.sh
@@ -681,6 +681,149 @@
EOF
}
+function test_aspect_exec_groups_inherit_toolchains() {
+ local -r pkg=${FUNCNAME[0]}
+ mkdir $pkg || fail "mkdir $pkg"
+
+ write_toolchains_for_exec_group_tests
+
+ # Add an aspect with exec groups.
+ mkdir -p ${pkg}/aspect
+ touch ${pkg}/aspect/BUILD
+ cat >> ${pkg}/aspect/aspect.bzl <<EOF
+def _impl(target, ctx):
+ toolchain = ctx.toolchains['//${pkg}/platform:toolchain_type']
+ print("hi from sample_aspect on %s, toolchain says %s" % (ctx.rule.attr.name, toolchain.message))
+
+ extra_toolchain = ctx.exec_groups["extra"].toolchains["//${pkg}/platform:toolchain_type"]
+ print("exec group extra: hi from sample_aspect on %s, toolchain says %s" % (ctx.rule.attr.name, extra_toolchain.message))
+
+ return []
+
+sample_aspect = aspect(
+ implementation = _impl,
+ exec_groups = {
+ # extra should inherit both the exec constraint and the toolchain.
+ 'extra': exec_group(copy_from_rule = True),
+ },
+ exec_compatible_with = ['//${pkg}/platform:value_foo'],
+ toolchains = ['//${pkg}/platform:toolchain_type'],
+)
+EOF
+
+ # Define a simple rule to put the aspect on.
+ mkdir -p ${pkg}/rule
+ touch ${pkg}/rule/BUILD
+ cat >> ${pkg}/rule/rule.bzl <<EOF
+def _impl(ctx):
+ pass
+
+sample_rule = rule(
+ implementation = _impl,
+)
+EOF
+
+ # Use the aspect and check the results.
+ mkdir -p ${pkg}/demo
+ cat >> ${pkg}/demo/BUILD <<EOF
+load('//${pkg}/rule:rule.bzl', 'sample_rule')
+
+sample_rule(
+ name = 'use',
+)
+EOF
+
+ # Build the target, using debug messages to verify the correct toolchain was selected.
+ bazel build --aspects=//${pkg}/aspect:aspect.bzl%sample_aspect //${pkg}/demo:use &> $TEST_log || fail "Build failed"
+ expect_log "hi from sample_aspect on use, toolchain says foo"
+ expect_log "exec group extra: hi from sample_aspect on use, toolchain says foo"
+}
+
+function test_aspect_exec_groups_different_toolchains() {
+ local -r pkg=${FUNCNAME[0]}
+ mkdir $pkg || fail "mkdir $pkg"
+
+ write_toolchains_for_exec_group_tests
+
+ # Also write a new toolchain.
+ mkdir -p ${pkg}/other
+ cat >> ${pkg}/other/BUILD <<EOF
+package(default_visibility = ['//visibility:public'])
+toolchain_type(name = 'toolchain_type')
+
+load('//${pkg}/platform:toolchain.bzl', 'test_toolchain')
+
+# Define the toolchains.
+test_toolchain(
+ name = 'test_toolchain_impl_other',
+ message = 'other',
+)
+
+# Declare the toolchains.
+toolchain(
+ name = 'test_toolchain_other',
+ toolchain_type = ':toolchain_type',
+ toolchain = ':test_toolchain_impl_other',
+)
+EOF
+
+ cat >> WORKSPACE <<EOF
+register_toolchains('//${pkg}/other:all')
+EOF
+
+ # Add an aspect with exec groups.
+ mkdir -p ${pkg}/aspect
+ touch ${pkg}/aspect/BUILD
+ cat >> ${pkg}/aspect/aspect.bzl <<EOF
+def _impl(target, ctx):
+ toolchain = ctx.toolchains['//${pkg}/platform:toolchain_type']
+ print("hi from sample_aspect on %s, toolchain says %s" % (ctx.rule.attr.name, toolchain.message))
+
+ other_toolchain = ctx.exec_groups["other"].toolchains["//${pkg}/other:toolchain_type"]
+ print("exec group other: hi from sample_aspect on %s, toolchain says %s" % (ctx.rule.attr.name, other_toolchain.message))
+
+ return []
+
+sample_aspect = aspect(
+ implementation = _impl,
+ exec_groups = {
+ # other defines new toolchain types.
+ 'other': exec_group(
+ toolchains = ['//${pkg}/other:toolchain_type'],
+ ),
+ },
+ toolchains = ['//${pkg}/platform:toolchain_type'],
+)
+EOF
+
+ # Define a simple rule to put the aspect on.
+ mkdir -p ${pkg}/rule
+ touch ${pkg}/rule/BUILD
+ cat >> ${pkg}/rule/rule.bzl <<EOF
+def _impl(ctx):
+ pass
+
+sample_rule = rule(
+ implementation = _impl,
+)
+EOF
+
+ # Use the aspect and check the results.
+ mkdir -p ${pkg}/demo
+ cat >> ${pkg}/demo/BUILD <<EOF
+load('//${pkg}/rule:rule.bzl', 'sample_rule')
+
+sample_rule(
+ name = 'use',
+)
+EOF
+
+ # Build the target, using debug messages to verify the correct toolchain was selected.
+ bazel build --aspects=//${pkg}/aspect:aspect.bzl%sample_aspect //${pkg}/demo:use &> $TEST_log || fail "Build failed"
+ expect_log "hi from sample_aspect on use, toolchain says bar"
+ expect_log "exec group other: hi from sample_aspect on use, toolchain says other"
+}
+
# Test basic inheritance of constraints and toolchains on a single rule.
function test_exec_group_rule_constraint_inheritance() {
local -r pkg=${FUNCNAME[0]}