Simplify collection of aspect dependencies.
Aspects that require providers from other aspects will list these dependent aspects in `AspectKey#getBaseKeys`. In `AspectFunction`, we collect these keys transitively to build up a "path" of transitively required aspects in topological order - each aspect appears after all of its dependencies.
It took me a while to understand what this code was doing, so I tried to simplify it.
PiperOrigin-RevId: 411896421
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 8b50c58..a047165 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
@@ -18,8 +18,8 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.AspectResolver;
@@ -61,7 +61,6 @@
import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.packages.Aspect;
import com.google.devtools.build.lib.packages.AspectDefinition;
-import com.google.devtools.build.lib.packages.AspectDescriptor;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
import com.google.devtools.build.lib.packages.NativeAspectClass;
@@ -86,8 +85,8 @@
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.ValueOrException2;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -258,12 +257,10 @@
throw new IllegalStateException("Name already verified", e);
}
- SkyframeBuildView view = buildViewProvider.getSkyframeBuildView();
-
if (AliasProvider.isAlias(associatedTarget)) {
return createAliasAspect(
env,
- view.getHostConfiguration(),
+ buildViewProvider.getSkyframeBuildView().getHostConfiguration(),
new TargetAndConfiguration(target, configuration),
aspect,
key,
@@ -278,9 +275,6 @@
associatedTarget.getOriginalLabel(),
associatedTarget.getLabel());
- ConfiguredTargetAndData associatedConfiguredTargetAndData =
- new ConfiguredTargetAndData(associatedTarget, target, configuration, null);
-
// If the incompatible flag is set, the top-level aspect should not be applied on top-level
// targets whose rules do not advertise the aspect's required providers. The aspect should not
// also propagate to these targets dependencies.
@@ -308,47 +302,42 @@
}
}
- ImmutableList.Builder<Aspect> aspectPathBuilder = ImmutableList.builder();
-
- if (!key.getBaseKeys().isEmpty()) {
- // We transitively collect all required aspects to reduce the number of restarts.
- // Semantically it is enough to just request key.getBaseKeys().
- ImmutableList.Builder<SkyKey> aspectPathSkyKeysBuilder = ImmutableList.builder();
- ImmutableMap<AspectDescriptor, SkyKey> aspectKeys =
- getSkyKeysForAspectsAndCollectAspectPath(key.getBaseKeys(), aspectPathSkyKeysBuilder);
-
- Map<SkyKey, SkyValue> values = env.getValues(aspectKeys.values());
+ ImmutableList<Aspect> topologicalAspectPath;
+ if (key.getBaseKeys().isEmpty()) {
+ topologicalAspectPath = ImmutableList.of(aspect);
+ } else {
+ LinkedHashSet<AspectKey> orderedKeys = new LinkedHashSet<>();
+ collectAspectKeysInTopologicalOrder(key.getBaseKeys(), orderedKeys);
+ Map<SkyKey, SkyValue> aspectValues = env.getValues(orderedKeys);
if (env.valuesMissing()) {
return null;
}
- ImmutableList<SkyKey> aspectPathSkyKeys = aspectPathSkyKeysBuilder.build();
- for (SkyKey aspectPathSkyKey : aspectPathSkyKeys) {
- aspectPathBuilder.add(((AspectValue) values.get(aspectPathSkyKey)).getAspect());
+ ImmutableList.Builder<Aspect> topologicalAspectPathBuilder =
+ ImmutableList.builderWithExpectedSize(orderedKeys.size() + 1);
+ for (AspectKey aspectKey : orderedKeys) {
+ AspectValue aspectValue = (AspectValue) aspectValues.get(aspectKey);
+ topologicalAspectPathBuilder.add(aspectValue.getAspect());
}
- try {
- associatedTarget = getBaseTarget(associatedTarget, key.getBaseKeys(), values);
- } catch (DuplicateException e) {
- env.getListener()
- .handle(
- Event.error(
- associatedConfiguredTargetAndData.getTarget().getLocation(), e.getMessage()));
+ topologicalAspectPath = topologicalAspectPathBuilder.add(aspect).build();
+ List<ConfiguredAspect> directlyRequiredAspects =
+ Lists.transform(
+ key.getBaseKeys(), k -> ((AspectValue) aspectValues.get(k)).getConfiguredAspect());
+ try {
+ associatedTarget = MergedConfiguredTarget.of(associatedTarget, directlyRequiredAspects);
+ } catch (DuplicateException e) {
+ env.getListener().handle(Event.error(target.getLocation(), e.getMessage()));
throw new AspectFunctionException(
- new AspectCreationException(
- e.getMessage(), associatedTarget.getLabel(), configuration));
+ new AspectCreationException(e.getMessage(), target.getLabel(), configuration));
}
}
- associatedConfiguredTargetAndData =
- associatedConfiguredTargetAndData.fromConfiguredTarget(associatedTarget);
- aspectPathBuilder.add(aspect);
SkyframeDependencyResolver resolver = new SkyframeDependencyResolver(env);
NestedSetBuilder<Package> transitivePackagesForPackageRootResolution =
storeTransitivePackagesForPackageRootResolution ? NestedSetBuilder.stableOrder() : null;
TargetAndConfiguration originalTargetAndConfiguration =
- new TargetAndConfiguration(associatedConfiguredTargetAndData.getTarget(), configuration);
- ImmutableList<Aspect> aspectPath = aspectPathBuilder.build();
+ new TargetAndConfiguration(target, configuration);
try {
UnloadedToolchainContext unloadedToolchainContext =
getUnloadedToolchainContext(env, key, aspect, configuration);
@@ -378,7 +367,7 @@
env,
resolver,
originalTargetAndConfiguration,
- aspectPath,
+ topologicalAspectPath,
configConditions.asProviders(),
unloadedToolchainContext == null
? null
@@ -387,7 +376,7 @@
.build(),
shouldUseToolchainTransition(configuration, aspect.getDefinition()),
ruleClassProvider,
- view.getHostConfiguration(),
+ buildViewProvider.getSkyframeBuildView().getHostConfiguration(),
transitivePackagesForPackageRootResolution,
transitiveRootCauses);
} catch (ConfiguredValueCreationException e) {
@@ -411,9 +400,7 @@
if (unloadedToolchainContext != null) {
String targetDescription =
String.format(
- "aspect %s applied to %s",
- aspect.getDescriptor().getDescription(),
- associatedConfiguredTargetAndData.getTarget());
+ "aspect %s applied to %s", aspect.getDescriptor().getDescription(), target);
toolchainContext =
ResolvedToolchainContext.load(
unloadedToolchainContext,
@@ -425,10 +412,11 @@
return createAspect(
env,
key,
- aspectPath,
+ topologicalAspectPath,
aspect,
aspectFactory,
- associatedConfiguredTargetAndData,
+ new ConfiguredTargetAndData(
+ associatedTarget, target, configuration, /*transitionKeys=*/ null),
configuration,
configConditions,
toolchainContext,
@@ -579,54 +567,20 @@
}
/**
- * Merges aspects defined by {@code aspectKeys} into the {@code target} using previously computed
- * {@code values}.
+ * Collects {@link AspectKey} dependencies by performing a postorder traversal over {@link
+ * AspectKey#getBaseKeys}.
*
- * @return A {@link ConfiguredTarget} that is a result of a merge.
- * @throws DuplicateException if there is a duplicate provider provided by aspects.
+ * <p>The resulting set of {@code orderedKeys} is topologically ordered: each aspect key appears
+ * after all of its dependencies.
*/
- private static ConfiguredTarget getBaseTarget(
- ConfiguredTarget target, ImmutableList<AspectKey> aspectKeys, Map<SkyKey, SkyValue> values)
- throws DuplicateException {
- ArrayList<ConfiguredAspect> aspectValues = new ArrayList<>();
- for (AspectKey aspectKey : aspectKeys) {
- AspectValue aspectValue = (AspectValue) values.get(aspectKey);
- ConfiguredAspect configuredAspect = aspectValue.getConfiguredAspect();
- aspectValues.add(configuredAspect);
+ private static void collectAspectKeysInTopologicalOrder(
+ List<AspectKey> baseKeys, LinkedHashSet<AspectKey> orderedKeys) {
+ for (AspectKey key : baseKeys) {
+ if (!orderedKeys.contains(key)) {
+ collectAspectKeysInTopologicalOrder(key.getBaseKeys(), orderedKeys);
+ orderedKeys.add(key);
+ }
}
- return MergedConfiguredTarget.of(target, aspectValues);
- }
-
- /**
- * Collect all SkyKeys that are needed for a given list of AspectKeys, including transitive
- * dependencies.
- *
- * <p>Also collects all propagating aspects in correct order.
- */
- private static ImmutableMap<AspectDescriptor, SkyKey> getSkyKeysForAspectsAndCollectAspectPath(
- ImmutableList<AspectKey> keys, ImmutableList.Builder<SkyKey> aspectPathBuilder) {
- HashMap<AspectDescriptor, SkyKey> result = new HashMap<>();
- for (AspectKey key : keys) {
- buildSkyKeys(key, result, aspectPathBuilder);
- }
- return ImmutableMap.copyOf(result);
- }
-
- private static void buildSkyKeys(
- AspectKey key,
- HashMap<AspectDescriptor, SkyKey> result,
- ImmutableList.Builder<SkyKey> aspectPathBuilder) {
- if (result.containsKey(key.getAspectDescriptor())) {
- return;
- }
- ImmutableList<AspectKey> baseKeys = key.getBaseKeys();
- result.put(key.getAspectDescriptor(), key);
- for (AspectKey baseKey : baseKeys) {
- buildSkyKeys(baseKey, result, aspectPathBuilder);
- }
- // Post-order list of aspect SkyKeys gives the order of propagating aspects:
- // the aspect comes after all aspects it transitively sees.
- aspectPathBuilder.add(key);
}
/**
@@ -836,7 +790,7 @@
private AspectValue createAspect(
Environment env,
AspectKey key,
- ImmutableList<Aspect> aspectPath,
+ ImmutableList<Aspect> topologicalAspectPath,
Aspect aspect,
ConfiguredAspectFactory aspectFactory,
ConfiguredTargetAndData associatedTarget,
@@ -879,7 +833,7 @@
.createAspect(
analysisEnvironment,
associatedTarget,
- aspectPath,
+ topologicalAspectPath,
aspectFactory,
aspect,
directDeps,