diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectContext.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectContext.java
new file mode 100644
index 00000000000000..acfaf989f8ef50
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectContext.java
@@ -0,0 +1,278 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Streams;
+import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap;
+import com.google.devtools.build.lib.packages.Aspect;
+import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
+import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/** Extends {@link RuleContext} to provide all data available during the analysis of an aspect. */
+public final class AspectContext extends RuleContext {
+
+ /**
+ * A list of all aspects applied to the target.
+ *
+ *
The last aspect in the list is the main aspect that this context is for.
+ */
+ private final ImmutableList aspects;
+
+ private final ImmutableList aspectDescriptors;
+
+ // If (separate_aspect_deps) flag is disabled, holds all merged prerequisites otherwise it will
+ // only have the main aspect's prerequisites.
+ private final PrerequisitesCollection mainAspectPrerequisites;
+
+ AspectContext(
+ RuleContext.Builder builder,
+ AspectAwareAttributeMapper aspectAwareAttributeMapper,
+ PrerequisitesCollection ruleAndBaseAspectsPrerequisites,
+ PrerequisitesCollection mainAspectPrerequisites,
+ ExecGroupCollection execGroupCollection) {
+ super(
+ builder, aspectAwareAttributeMapper, ruleAndBaseAspectsPrerequisites, execGroupCollection);
+
+ this.aspects = builder.getAspects();
+ this.aspectDescriptors = aspects.stream().map(Aspect::getDescriptor).collect(toImmutableList());
+ this.mainAspectPrerequisites = mainAspectPrerequisites;
+ }
+
+ /**
+ * Merge the attributes of the aspects in the aspects path.
+ *
+ *
For attributes with the same name, the one that is first encountered takes precedence.
+ */
+ private static ImmutableMap mergeAspectsAttributes(
+ ImmutableList aspects) {
+ if (aspects.isEmpty()) {
+ return ImmutableMap.of();
+ } else if (aspects.size() == 1) {
+ return aspects.get(0).getDefinition().getAttributes();
+ } else {
+ LinkedHashMap aspectAttributes = new LinkedHashMap<>();
+ for (Aspect aspect : aspects) {
+ ImmutableMap currentAttributes = aspect.getDefinition().getAttributes();
+ for (Map.Entry kv : currentAttributes.entrySet()) {
+ aspectAttributes.putIfAbsent(kv.getKey(), kv.getValue());
+ }
+ }
+ return ImmutableMap.copyOf(aspectAttributes);
+ }
+ }
+
+ static AspectContext create(
+ Builder builder,
+ AttributeMap ruleAttributes,
+ ImmutableListMultimap targetsMap,
+ ExecGroupCollection execGroupCollection) {
+
+ if (builder.separateAspectDeps()) {
+ return createAspectContextWithSeparatedPrerequisites(
+ builder, ruleAttributes, targetsMap, execGroupCollection);
+ } else {
+ return createAspectContextWithMergedPrerequisites(
+ builder, ruleAttributes, targetsMap, execGroupCollection);
+ }
+ }
+
+ /**
+ * Merges the rule prerequisites with the aspects prerequisites giving precedence to aspects
+ * prerequisites if any.
+ *
+ *
TODO(b/293304543) merging prerequisites should be not needed after completing the solution
+ * to isolate main aspect dependencies from the underlying rule and aspects dependencies.
+ */
+ private static AspectContext createAspectContextWithMergedPrerequisites(
+ RuleContext.Builder builder,
+ AttributeMap ruleAttributes,
+ ImmutableListMultimap targetsMap,
+ ExecGroupCollection execGroupCollection) {
+
+ ImmutableSortedKeyListMultimap.Builder
+ attributeNameToTargetsMap = ImmutableSortedKeyListMultimap.builder();
+ HashSet processedAttributes = new HashSet<>();
+
+ // add aspects prerequisites
+ for (Aspect aspect : builder.getAspects()) {
+ for (Attribute attribute : aspect.getDefinition().getAttributes().values()) {
+ DependencyKind key =
+ DependencyKind.AttributeDependencyKind.forAspect(attribute, aspect.getAspectClass());
+ if (targetsMap.containsKey(key)) {
+ if (processedAttributes.add(attribute.getName())) {
+ attributeNameToTargetsMap.putAll(attribute.getName(), targetsMap.get(key));
+ }
+ }
+ }
+ }
+
+ // add rule prerequisites
+ for (var entry : targetsMap.asMap().entrySet()) {
+ if (entry.getKey().getOwningAspect() == null) {
+ if (!processedAttributes.contains(entry.getKey().getAttribute().getName())) {
+ attributeNameToTargetsMap.putAll(
+ entry.getKey().getAttribute().getName(), entry.getValue());
+ }
+ }
+ }
+ AspectAwareAttributeMapper ruleAndAspectsAttributes =
+ new AspectAwareAttributeMapper(
+ ruleAttributes, mergeAspectsAttributes(builder.getAspects()));
+ PrerequisitesCollection mergedPrerequisitesCollection =
+ new PrerequisitesCollection(
+ attributeNameToTargetsMap.build(),
+ ruleAndAspectsAttributes,
+ builder.getErrorConsumer(),
+ builder.getRule(),
+ builder.getRuleClassNameForLogging());
+ return new AspectContext(
+ builder,
+ ruleAndAspectsAttributes,
+ /* ruleAndBaseAspectsPrerequisites= */ mergedPrerequisitesCollection,
+ /* mainAspectPrerequisites= */ mergedPrerequisitesCollection,
+ execGroupCollection);
+ }
+
+ /**
+ * Create prerequisites collection for aspect evaluation separating the main aspect prerequisites
+ * from the underlying rule and base aspects prerequisites.
+ */
+ private static AspectContext createAspectContextWithSeparatedPrerequisites(
+ RuleContext.Builder builder,
+ AttributeMap ruleAttributes,
+ ImmutableListMultimap prerequisitesMap,
+ ExecGroupCollection execGroupCollection) {
+ ImmutableSortedKeyListMultimap.Builder
+ mainAspectPrerequisites = ImmutableSortedKeyListMultimap.builder();
+ ImmutableSortedKeyListMultimap.Builder
+ ruleAndBaseAspectsPrerequisites = ImmutableSortedKeyListMultimap.builder();
+
+ Aspect mainAspect = Iterables.getLast(builder.getAspects());
+
+ for (Map.Entry> entry :
+ prerequisitesMap.asMap().entrySet()) {
+ String attributeName = entry.getKey().getAttribute().getName();
+
+ if (mainAspect.getAspectClass().equals(entry.getKey().getOwningAspect())) {
+ mainAspectPrerequisites.putAll(attributeName, entry.getValue());
+ } else {
+ ruleAndBaseAspectsPrerequisites.putAll(attributeName, entry.getValue());
+ }
+ }
+
+ return new AspectContext(
+ builder,
+ new AspectAwareAttributeMapper(
+ ruleAttributes, mergeAspectsAttributes(builder.getAspects())),
+ new PrerequisitesCollection(
+ ruleAndBaseAspectsPrerequisites.build(),
+ mergeRuleAndBaseAspectsAttributes(ruleAttributes, builder.getAspects()),
+ builder.getErrorConsumer(),
+ builder.getRule(),
+ builder.getRuleClassNameForLogging()),
+ new PrerequisitesCollection(
+ mainAspectPrerequisites.build(),
+ mainAspect.getDefinition().getAttributes(),
+ builder.getErrorConsumer(),
+ builder.getRule(),
+ builder.getRuleClassNameForLogging()),
+ execGroupCollection);
+ }
+
+ private static AspectAwareAttributeMapper mergeRuleAndBaseAspectsAttributes(
+ AttributeMap ruleAttributes, ImmutableList aspects) {
+ LinkedHashMap mergedBaseAspectsAttributes = new LinkedHashMap<>();
+ for (int i = 0; i < aspects.size() - 1; i++) {
+ for (Attribute attribute : aspects.get(i).getDefinition().getAttributes().values()) {
+ mergedBaseAspectsAttributes.putIfAbsent(attribute.getName(), attribute);
+ }
+ }
+ return new AspectAwareAttributeMapper(
+ ruleAttributes, ImmutableMap.copyOf(mergedBaseAspectsAttributes));
+ }
+
+ @Override
+ PrerequisitesCollection getOwningPrerequisitesCollection(String attributeName) {
+ if (mainAspectPrerequisites.has(attributeName)) {
+ return mainAspectPrerequisites;
+ }
+ return getRulePrerequisitesCollection();
+ }
+
+ public PrerequisitesCollection getMainAspectPrerequisitesCollection() {
+ return mainAspectPrerequisites;
+ }
+
+ @Override
+ public ImmutableList getAspects() {
+ return aspects;
+ }
+
+ /**
+ * Return the main aspect of this context.
+ *
+ *
It is the last aspect in the list of aspects applied to a target; all other aspects are the
+ * ones main aspect sees as specified by its "required_aspect_providers").
+ */
+ @Override
+ public Aspect getMainAspect() {
+ return Iterables.getLast(aspects);
+ }
+
+ /** All aspects applied to the rule. */
+ @Override
+ public ImmutableList getAspectDescriptors() {
+ return aspectDescriptors;
+ }
+
+ @Override
+ public boolean useAutoExecGroups() {
+ ImmutableMap aspectAttributes =
+ getMainAspect().getDefinition().getAttributes();
+ if (aspectAttributes.containsKey("$use_auto_exec_groups")) {
+ return (boolean) aspectAttributes.get("$use_auto_exec_groups").getDefaultValueUnchecked();
+ } else {
+ return getConfiguration().useAutoExecGroups();
+ }
+ }
+
+ @Override
+ public ImmutableList extends TransitiveInfoCollection> getAllPrerequisites() {
+ boolean separateAspectDeps =
+ getAnalysisEnvironment()
+ .getStarlarkSemantics()
+ .getBool(BuildLanguageOptions.SEPARATE_ASPECT_DEPS);
+ if (separateAspectDeps) {
+ return Streams.concat(
+ mainAspectPrerequisites.getAllPrerequisites().stream(),
+ getRulePrerequisitesCollection().getAllPrerequisites().stream())
+ .collect(toImmutableList());
+ }
+ return super.getAllPrerequisites();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectOnRuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectOnRuleContext.java
deleted file mode 100644
index 76d9e05df637d1..00000000000000
--- a/src/main/java/com/google/devtools/build/lib/analysis/AspectOnRuleContext.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2023 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.analysis;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.packages.Aspect;
-import com.google.devtools.build.lib.packages.AspectDescriptor;
-import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.AttributeMap;
-import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/** Extends {@link RuleContext} to provide all data available during the analysis of an aspect. */
-public final class AspectOnRuleContext extends RuleContext {
-
- /**
- * A list of all aspects applied to the target.
- *
- *
The last aspect in the list is the main aspect that this context is for.
- */
- private final ImmutableList aspects;
-
- private final ImmutableList aspectDescriptors;
-
- AspectOnRuleContext(
- RuleContext.Builder builder,
- AttributeMap ruleAttributes,
- ImmutableListMultimap prerequisitesCollection,
- ExecGroupCollection execGroupCollection) {
- super(
- builder,
- new AspectAwareAttributeMapper(
- ruleAttributes, mergeAspectsAttributes(builder.getAspects())),
- prerequisitesCollection,
- execGroupCollection);
-
- this.aspects = builder.getAspects();
- this.aspectDescriptors = aspects.stream().map(Aspect::getDescriptor).collect(toImmutableList());
- }
-
- /**
- * Merge the attributes of the aspects in the aspects path.
- *
- *
For attributes with the same name, the one that is first encountered takes precedence.
- */
- private static ImmutableMap mergeAspectsAttributes(
- ImmutableList aspects) {
- if (aspects.isEmpty()) {
- return ImmutableMap.of();
- } else if (aspects.size() == 1) {
- return aspects.get(0).getDefinition().getAttributes();
- } else {
- LinkedHashMap aspectAttributes = new LinkedHashMap<>();
- for (Aspect aspect : aspects) {
- ImmutableMap currentAttributes = aspect.getDefinition().getAttributes();
- for (Map.Entry kv : currentAttributes.entrySet()) {
- aspectAttributes.putIfAbsent(kv.getKey(), kv.getValue());
- }
- }
- return ImmutableMap.copyOf(aspectAttributes);
- }
- }
-
- @Override
- public ImmutableList getAspects() {
- return aspects;
- }
-
- /**
- * Return the main aspect of this context.
- *
- *
It is the last aspect in the list of aspects applied to a target; all other aspects are the
- * ones main aspect sees as specified by its "required_aspect_providers").
- */
- @Override
- public Aspect getMainAspect() {
- return Iterables.getLast(aspects);
- }
-
- /** All aspects applied to the rule. */
- @Override
- public ImmutableList getAspectDescriptors() {
- return aspectDescriptors;
- }
-
- @Override
- public boolean useAutoExecGroups() {
- ImmutableMap aspectAttributes =
- getMainAspect().getDefinition().getAttributes();
- if (aspectAttributes.containsKey("$use_auto_exec_groups")) {
- return (boolean) aspectAttributes.get("$use_auto_exec_groups").getDefaultValueUnchecked();
- } else {
- return getConfiguration().useAutoExecGroups();
- }
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index 50912f4befe5fb..208da8fe11db81 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -165,7 +165,7 @@ java_library(
"AnalysisRootCauseEvent.java",
"AnalysisUtils.java",
"AspectCompleteEvent.java",
- "AspectOnRuleContext.java",
+ "AspectContext.java",
"AspectResolutionHelpers.java",
"AspectValue.java",
"BaseRuleClasses.java",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PrerequisiteArtifacts.java b/src/main/java/com/google/devtools/build/lib/analysis/PrerequisiteArtifacts.java
index c584db114d8cc4..ebcfaaeaf1c0c1 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PrerequisiteArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PrerequisiteArtifacts.java
@@ -62,8 +62,14 @@ static PrerequisiteArtifacts get(RuleContext ruleContext, String attributeName)
}
public static NestedSet nestedSet(RuleContext ruleContext, String attributeName) {
+ return nestedSet(ruleContext.getOwningPrerequisitesCollection(attributeName), attributeName);
+ }
+
+ public static NestedSet nestedSet(
+ PrerequisitesCollection prerequisitesCollection, String attributeName) {
NestedSetBuilder result = NestedSetBuilder.stableOrder();
- for (FileProvider target : ruleContext.getPrerequisites(attributeName, FileProvider.class)) {
+ for (FileProvider target :
+ prerequisitesCollection.getPrerequisites(attributeName, FileProvider.class)) {
result.addTransitive(target.getFilesToBuild());
}
return result.build();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PrerequisitesCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/PrerequisitesCollection.java
index 642d5c336ea348..4a611c93ef2118 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PrerequisitesCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PrerequisitesCollection.java
@@ -26,7 +26,6 @@
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.packages.Aspect;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.BuiltinProvider;
@@ -36,7 +35,6 @@
import com.google.devtools.build.lib.packages.StarlarkProviderWrapper;
import com.google.devtools.build.lib.packages.Type.LabelClass;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
@@ -44,82 +42,63 @@
/** Collection of the attributes dependencies available in a {@link RuleContext}. */
public final class PrerequisitesCollection {
+ private interface AttributesMapper {
+ Attribute getAttribute(String attributeName);
+ }
+
private final ImmutableSortedKeyListMultimap
attributeToPrerequisitesMap;
- private final AttributeMap attributes;
+ private final AttributesMapper attributesMapper;
+
private final RuleErrorConsumer ruleErrorConsumer;
private final Rule rule;
private final String ruleClassNameForLogging;
- static PrerequisitesCollection create(
- AttributeMap attributes,
- ImmutableListMultimap prerequisitesMap,
- ImmutableList aspects,
+ PrerequisitesCollection(
+ ImmutableSortedKeyListMultimap attributeToPrerequisitesMap,
+ ImmutableMap attributes,
RuleErrorConsumer ruleErrorConsumer,
Rule rule,
String ruleClassNameForLogging) {
- return new PrerequisitesCollection(
- attributes,
- mergePrerequisites(aspects, prerequisitesMap),
+ this(
+ attributeToPrerequisitesMap,
+ /* attributesMapper= */ attributes::get,
ruleErrorConsumer,
rule,
ruleClassNameForLogging);
}
- /**
- * Merges the rule prerequisites with the aspects prerequisites giving precedence to aspects
- * prerequisites if any.
- *
- *
The class is intended to be sub-classed by {@link AspectOnRuleContext}, in order to share the
- * code. However, it's not intended for sub-classing beyond that, and the constructor is
- * intentionally package private to enforce that.
+ *
The class is intended to be sub-classed by {@link AspectContext}, in order to share the code.
+ * However, it's not intended for sub-classing beyond that, and the constructor is intentionally
+ * package private to enforce that.
*/
public class RuleContext extends TargetContext
implements ActionConstructionContext, ActionRegistry, RuleErrorConsumer, AutoCloseable {
@@ -146,7 +147,14 @@ void validate(
new Attribute.Builder<>(TOOLCHAIN_ATTR_NAME, BuildType.LABEL_LIST).build();
private final Rule rule;
+
+ /**
+ * If this {@code RuleContext} is for rule evaluation, this holds the attribute-based
+ * prerequisites of the rule and if it is for aspect evaluation, it will contain the merged
+ * prerequisites of the rule and the base aspects (rule attributes take precedence).
+ */
private final PrerequisitesCollection prerequisitesCollection;
+
private final ImmutableMap