Roll forward of https://github.com/bazelbuild/bazel/commit/943c83aa58731c4f9561d79c458f254427a8f24c: Command line aspect-on-aspect
Supports aspect-on-aspect for command line aspects. Command line aspects specified via `--aspects` option will support a top-level aspect requiring aspect providers via `required_aspect_providers` to get their values from other top-level aspects advertising it that come before it in the `--aspects` list.
NEW:
- Add `incompatible_ignore_duplicate_top_level_aspects` flag to allow duplicates in `--aspects` list. The flag is set to true by default, otherwise a validation error will be thrown in case of duplicates in top-level aspects.
- Fix the error reporting for duplicate native aspects in `--aspects` list to be reported as a SkyFunction exception instead of crashing with assertion error.
Automated rollback of commit 7b4f9826d2d38ac7d071a4ada7b8a40a7a78226d.
*** Reason for rollback ***
Guard the validation against duplicate aspects in `--aspects` list by a flag to avoid breaking builds with duplicate aspects.
*** Original change description ***
Automated rollback of commit 7649f610c45190735fd7de433b15679b21b2d91b.
*** Reason for rollback ***
The added validation to prevent duplicate aspects in --aspects list breaks //production/datapush/modular/implementations/build:buildtarget_test
*** Original change description ***
Roll forward of https://github.com/bazelbuild/bazel/commit/943c83aa58731c4f9561d79c458f254427a8f24c: Command line aspect-on-aspect
Supports aspect-on-aspect for command line aspects. Command line aspects specified via `--aspects` option will support a top-level aspect requiring aspect providers via `required_a...
***
PiperOrigin-RevId: 389217989
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
index 1e8a0ef..2a8445a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
@@ -30,7 +30,7 @@
import com.google.devtools.build.lib.causes.Cause;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.skyframe.AspectValueKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import java.util.Collection;
import javax.annotation.Nullable;
@@ -40,7 +40,7 @@
* target cannot be completed because of an error in one of its dependencies.
*/
public class AnalysisFailureEvent implements BuildEvent {
- @Nullable private final AspectValueKey failedAspect;
+ @Nullable private final AspectKey failedAspect;
private final ConfiguredTargetKey failedTarget;
private final BuildEventId configuration;
private final NestedSet<Cause> rootCauses;
@@ -48,12 +48,12 @@
public AnalysisFailureEvent(
ActionLookupKey failedTarget, BuildEventId configuration, NestedSet<Cause> rootCauses) {
Preconditions.checkArgument(
- failedTarget instanceof ConfiguredTargetKey || failedTarget instanceof AspectValueKey);
+ failedTarget instanceof ConfiguredTargetKey || failedTarget instanceof AspectKey);
if (failedTarget instanceof ConfiguredTargetKey) {
this.failedAspect = null;
this.failedTarget = (ConfiguredTargetKey) failedTarget;
} else {
- this.failedAspect = (AspectValueKey) failedTarget;
+ this.failedAspect = (AspectKey) failedTarget;
this.failedTarget = failedAspect.getBaseConfiguredTargetKey();
}
if (configuration != null) {
@@ -65,7 +65,7 @@
}
public AnalysisFailureEvent(
- AspectValueKey failedAspect, BuildEventId configuration, NestedSet<Cause> rootCauses) {
+ AspectKey failedAspect, BuildEventId configuration, NestedSet<Cause> rootCauses) {
this.failedAspect = failedAspect;
this.failedTarget = failedAspect.getBaseConfiguredTargetKey();
if (configuration != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
index e8662a2..a39ddb6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
@@ -166,4 +166,19 @@
+ " be used. Example value: \"HOST_CPUS*0.5\".",
converter = CpuResourceConverter.class)
public int oomSensitiveSkyFunctionsSemaphoreSize;
+
+ @Option(
+ name = "incompatible_ignore_duplicate_top_level_aspects",
+ defaultValue = "true",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ metadataTags = {
+ OptionMetadataTag.INCOMPATIBLE_CHANGE,
+ OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
+ },
+ effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+ help =
+ "If true, remove duplicates from --aspects list by keeping only the first occurrence of"
+ + " every aspect. Otherwise, throw validation error if duplicate aspects are"
+ + " encountered.")
+ public boolean ignoreDuplicateTopLevelAspects;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index bec43a1..2e289f3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -55,13 +55,12 @@
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.packages.AspectClass;
-import com.google.devtools.build.lib.packages.AspectDescriptor;
-import com.google.devtools.build.lib.packages.AspectParameters;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.NativeAspectClass;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.pkgcache.PackageManager;
@@ -75,6 +74,7 @@
import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns.Code;
import com.google.devtools.build.lib.skyframe.AspectValueKey;
import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.skyframe.CoverageReportValue;
@@ -86,7 +86,6 @@
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.RegexFilter;
import com.google.devtools.build.skyframe.WalkableGraph;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -275,10 +274,11 @@
.map(TargetAndConfiguration::getConfiguredTargetKey)
.collect(Collectors.toList());
- Multimap<Pair<Label, String>, BuildConfiguration> aspectConfigurations =
- ArrayListMultimap.create();
-
- List<AspectValueKey> aspectKeys = new ArrayList<>();
+ ImmutableList.Builder<AspectClass> aspectClassesBuilder = ImmutableList.builder();
+ if (viewOptions.ignoreDuplicateTopLevelAspects) {
+ // remove duplicates from aspects list
+ aspects = ImmutableSet.copyOf(aspects).asList();
+ }
for (String aspect : aspects) {
// Syntax: label%aspect
int delimiterPosition = aspect.indexOf('%');
@@ -318,38 +318,14 @@
createFailureDetail(errorMessage, Analysis.Code.ASPECT_LABEL_SYNTAX_ERROR),
e);
}
-
String starlarkFunctionName = aspect.substring(delimiterPosition + 1);
- for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
- aspectConfigurations.put(
- Pair.of(targetSpec.getLabel(), aspect), targetSpec.getConfiguration());
- aspectKeys.add(
- AspectValueKey.createStarlarkAspectKey(
- targetSpec.getLabel(),
- // For invoking top-level aspects, use the top-level configuration for both the
- // aspect and the base target while the top-level configuration is untrimmed.
- targetSpec.getConfiguration(),
- targetSpec.getConfiguration(),
- starlarkFileLabel,
- starlarkFunctionName));
- }
+ aspectClassesBuilder.add(new StarlarkAspectClass(starlarkFileLabel, starlarkFunctionName));
} else {
final NativeAspectClass aspectFactoryClass =
ruleClassProvider.getNativeAspectClassMap().get(aspect);
if (aspectFactoryClass != null) {
- for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
- // For invoking top-level aspects, use the top-level configuration for both the
- // aspect and the base target while the top-level configuration is untrimmed.
- BuildConfiguration configuration = targetSpec.getConfiguration();
- aspectConfigurations.put(Pair.of(targetSpec.getLabel(), aspect), configuration);
- aspectKeys.add(
- AspectValueKey.createAspectKey(
- targetSpec.getLabel(),
- configuration,
- new AspectDescriptor(aspectFactoryClass, AspectParameters.EMPTY),
- configuration));
- }
+ aspectClassesBuilder.add(aspectFactoryClass);
} else {
String errorMessage = "Aspect '" + aspect + "' is unknown";
throw new ViewCreationFailedException(
@@ -358,6 +334,25 @@
}
}
+ Multimap<Pair<Label, String>, BuildConfiguration> aspectConfigurations =
+ ArrayListMultimap.create();
+ ImmutableList<AspectClass> aspectClasses = aspectClassesBuilder.build();
+ ImmutableList.Builder<TopLevelAspectsKey> aspectsKeys = ImmutableList.builder();
+ for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
+ BuildConfiguration configuration = targetSpec.getConfiguration();
+ for (AspectClass aspectClass : aspectClasses) {
+ aspectConfigurations.put(
+ Pair.of(targetSpec.getLabel(), aspectClass.getName()), configuration);
+ }
+ // For invoking top-level aspects, use the top-level configuration for both the
+ // aspect and the base target while the top-level configuration is untrimmed.
+ if (!aspectClasses.isEmpty()) {
+ aspectsKeys.add(
+ AspectValueKey.createTopLevelAspectsKey(
+ aspectClasses, targetSpec.getLabel(), configuration));
+ }
+ }
+
for (Pair<Label, String> target : aspectConfigurations.keys()) {
eventBus.post(
new AspectConfiguredEvent(
@@ -382,7 +377,7 @@
skyframeBuildView.configureTargets(
eventHandler,
topLevelCtKeys,
- aspectKeys,
+ aspectsKeys.build(),
Suppliers.memoize(configurationLookupSupplier),
topLevelOptions,
eventBus,
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
index 6b62bd1..c25a910 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
@@ -305,11 +305,18 @@
effectTags = {OptionEffectTag.UNKNOWN},
allowMultiple = true,
help =
- "Comma-separated list of aspects to be applied to top-level targets. All aspects "
- + "are applied to all top-level targets independently. Aspects are specified in "
- + "the form <bzl-file-label>%<aspect_name>, "
- + "for example '//tools:my_def.bzl%my_aspect', where 'my_aspect' is a top-level "
- + "value from from a file tools/my_def.bzl")
+ "Comma-separated list of aspects to be applied to top-level targets. All aspects are"
+ + " applied to all top-level targets. If aspect <code>some_aspect</code> specifies"
+ + " required aspect providers via <code>required_aspect_providers</code>,"
+ + " <code>some_aspect</code> will run after every aspect that was mentioned before it"
+ + " in the aspects list and whose advertised providers satisfy"
+ + " <code>some_aspect</code> required aspect providers. <code>some_aspect</code> will"
+ + " then have access to the values of those aspects' providers. Aspects that do not"
+ + " have such dependency will run independently on the top-level targets."
+ + ""
+ + " Aspects are specified in the form <bzl-file-label>%<aspect_name>, for example"
+ + " '//tools:my_def.bzl%my_aspect', where 'my_aspect' is a top-level value from a"
+ + " file tools/my_def.bzl")
public List<String> aspects;
public BuildRequestOptions() throws OptionsParsingException {}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
index 2b9f2a3..bf0b414 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
@@ -64,4 +64,9 @@
public int hashCode() {
return Objects.hash(extensionLabel, exportedName);
}
+
+ @Override
+ public String toString() {
+ return getName();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
index 391a5b3..f97e08d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
@@ -13,7 +13,6 @@
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -30,29 +29,15 @@
import com.google.devtools.build.skyframe.SkyFunctionName;
import javax.annotation.Nullable;
-/** A base class for keys that have AspectValue as a Sky value. */
-public abstract class AspectValueKey implements ActionLookupKey {
+/** A wrapper class for sky keys needed to compute sky values for aspects. */
+public final class AspectValueKey {
+
+ private AspectValueKey() {}
private static final Interner<AspectKey> aspectKeyInterner = BlazeInterners.newWeakInterner();
- private static final Interner<StarlarkAspectLoadingKey> starlarkAspectKeyInterner =
+ private static final Interner<TopLevelAspectsKey> topLevelAspectsKeyInterner =
BlazeInterners.newWeakInterner();
- /**
- * Gets the name of the aspect that would be returned by the corresponding value's {@code
- * aspectValue.getAspect().getAspectClass().getName()}, if the value could be produced.
- *
- * <p>Only needed for reporting errors in BEP when the key's AspectValue fails evaluation.
- */
- public abstract String getAspectName();
-
- public abstract String getDescription();
-
- @Nullable
- abstract BuildConfigurationValue.Key getAspectConfigurationKey();
-
- /** Returns the key for the base configured target for this aspect. */
- public abstract ConfiguredTargetKey getBaseConfiguredTargetKey();
-
public static AspectKey createAspectKey(
Label label,
@Nullable BuildConfiguration baseConfiguration,
@@ -87,33 +72,48 @@
aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration));
}
- public static StarlarkAspectLoadingKey createStarlarkAspectKey(
+ public static TopLevelAspectsKey createTopLevelAspectsKey(
+ ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
- @Nullable BuildConfiguration aspectConfiguration,
- @Nullable BuildConfiguration targetConfiguration,
- Label starlarkFileLabel,
- String starlarkExportName) {
- return StarlarkAspectLoadingKey.createInternal(
+ @Nullable BuildConfiguration configuration) {
+ return TopLevelAspectsKey.createInternal(
+ topLevelAspectsClasses,
targetLabel,
- aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration),
ConfiguredTargetKey.builder()
.setLabel(targetLabel)
- .setConfiguration(targetConfiguration)
- .build(),
- starlarkFileLabel,
- starlarkExportName);
+ .setConfiguration(configuration)
+ .build());
+ }
+
+ /** Common superclass for {@link AspectKey} and {@link TopLevelAspectsKey}. */
+ public abstract static class AspectBaseKey implements ActionLookupKey {
+ private final ConfiguredTargetKey baseConfiguredTargetKey;
+ private final int hashCode;
+
+ private AspectBaseKey(ConfiguredTargetKey baseConfiguredTargetKey, int hashCode) {
+ this.baseConfiguredTargetKey = baseConfiguredTargetKey;
+ this.hashCode = hashCode;
+ }
+
+ /** Returns the key for the base configured target for this aspect. */
+ public final ConfiguredTargetKey getBaseConfiguredTargetKey() {
+ return baseConfiguredTargetKey;
+ }
+
+ @Override
+ public final int hashCode() {
+ return hashCode;
+ }
}
// Specific subtypes of aspect keys.
/** Represents an aspect applied to a particular target. */
@AutoCodec
- public static final class AspectKey extends AspectValueKey {
- private final ConfiguredTargetKey baseConfiguredTargetKey;
+ public static final class AspectKey extends AspectBaseKey {
private final ImmutableList<AspectKey> baseKeys;
@Nullable private final BuildConfigurationValue.Key aspectConfigurationKey;
private final AspectDescriptor aspectDescriptor;
- private final int hashCode;
private AspectKey(
ConfiguredTargetKey baseConfiguredTargetKey,
@@ -121,11 +121,10 @@
AspectDescriptor aspectDescriptor,
@Nullable BuildConfigurationValue.Key aspectConfigurationKey,
int hashCode) {
+ super(baseConfiguredTargetKey, hashCode);
this.baseKeys = baseKeys;
this.aspectConfigurationKey = aspectConfigurationKey;
- this.baseConfiguredTargetKey = baseConfiguredTargetKey;
this.aspectDescriptor = aspectDescriptor;
- this.hashCode = hashCode;
}
@AutoCodec.VisibleForSerialization
@@ -150,14 +149,19 @@
return SkyFunctions.ASPECT;
}
- @Override
+ /**
+ * Gets the name of the aspect that would be returned by the corresponding value's {@code
+ * aspectValue.getAspect().getAspectClass().getName()}, if the value could be produced.
+ *
+ * <p>Only needed for reporting errors in BEP when the key's AspectValue fails evaluation.
+ */
public String getAspectName() {
return aspectDescriptor.getDescription();
}
@Override
public Label getLabel() {
- return baseConfiguredTargetKey.getLabel();
+ return getBaseConfiguredTargetKey().getLabel();
}
public AspectClass getAspectClass() {
@@ -187,7 +191,6 @@
return baseKeys;
}
- @Override
public String getDescription() {
if (baseKeys.isEmpty()) {
return String.format("%s of %s", aspectDescriptor.getAspectClass().getName(), getLabel());
@@ -215,22 +218,10 @@
* base target's configuration.
*/
@Nullable
- @Override
BuildConfigurationValue.Key getAspectConfigurationKey() {
return aspectConfigurationKey;
}
- /** Returns the key for the base configured target for this aspect. */
- @Override
- public ConfiguredTargetKey getBaseConfiguredTargetKey() {
- return baseConfiguredTargetKey;
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
@Override
public boolean equals(Object other) {
if (this == other) {
@@ -240,10 +231,10 @@
return false;
}
AspectKey that = (AspectKey) other;
- return hashCode == that.hashCode
+ return hashCode() == that.hashCode()
&& Objects.equal(baseKeys, that.baseKeys)
&& Objects.equal(aspectConfigurationKey, that.aspectConfigurationKey)
- && Objects.equal(baseConfiguredTargetKey, that.baseConfiguredTargetKey)
+ && Objects.equal(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
&& Objects.equal(aspectDescriptor, that.aspectDescriptor);
}
@@ -266,7 +257,7 @@
+ " "
+ aspectConfigurationKey
+ " "
- + baseConfiguredTargetKey
+ + getBaseConfiguredTargetKey()
+ " "
+ aspectDescriptor.getParameters();
}
@@ -280,7 +271,7 @@
return createAspectKey(
ConfiguredTargetKey.builder()
.setLabel(label)
- .setConfigurationKey(baseConfiguredTargetKey.getConfigurationKey())
+ .setConfigurationKey(getBaseConfiguredTargetKey().getConfigurationKey())
.build(),
newBaseKeys.build(),
aspectDescriptor,
@@ -288,70 +279,43 @@
}
}
- /** The key for a Starlark aspect. */
+ /** The key for top level aspects specified by --aspects option on a top level target. */
@AutoCodec
- public static final class StarlarkAspectLoadingKey extends AspectValueKey {
+ public static final class TopLevelAspectsKey extends AspectBaseKey {
+ private final ImmutableList<AspectClass> topLevelAspectsClasses;
private final Label targetLabel;
- private final BuildConfigurationValue.Key aspectConfigurationKey;
- private final ConfiguredTargetKey baseConfiguredTargetKey;
- private final Label starlarkFileLabel;
- private final String starlarkValueName;
- private final int hashCode;
@AutoCodec.Instantiator
@AutoCodec.VisibleForSerialization
- static StarlarkAspectLoadingKey createInternal(
+ static TopLevelAspectsKey createInternal(
+ ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
- BuildConfigurationValue.Key aspectConfigurationKey,
- ConfiguredTargetKey baseConfiguredTargetKey,
- Label starlarkFileLabel,
- String starlarkValueName) {
- return starlarkAspectKeyInterner.intern(
- new StarlarkAspectLoadingKey(
+ ConfiguredTargetKey baseConfiguredTargetKey) {
+ return topLevelAspectsKeyInterner.intern(
+ new TopLevelAspectsKey(
+ topLevelAspectsClasses,
targetLabel,
- aspectConfigurationKey,
baseConfiguredTargetKey,
- starlarkFileLabel,
- starlarkValueName,
- Objects.hashCode(
- targetLabel,
- aspectConfigurationKey,
- baseConfiguredTargetKey,
- starlarkFileLabel,
- starlarkValueName)));
+ Objects.hashCode(topLevelAspectsClasses, targetLabel, baseConfiguredTargetKey)));
}
- private StarlarkAspectLoadingKey(
+ private TopLevelAspectsKey(
+ ImmutableList<AspectClass> topLevelAspectsClasses,
Label targetLabel,
- BuildConfigurationValue.Key aspectConfigurationKey,
ConfiguredTargetKey baseConfiguredTargetKey,
- Label starlarkFileLabel,
- String starlarkValueName,
int hashCode) {
+ super(baseConfiguredTargetKey, hashCode);
+ this.topLevelAspectsClasses = topLevelAspectsClasses;
this.targetLabel = targetLabel;
- this.aspectConfigurationKey = aspectConfigurationKey;
- this.baseConfiguredTargetKey = baseConfiguredTargetKey;
- this.starlarkFileLabel = starlarkFileLabel;
- this.starlarkValueName = starlarkValueName;
- this.hashCode = hashCode;
}
@Override
public SkyFunctionName functionName() {
- return SkyFunctions.LOAD_STARLARK_ASPECT;
+ return SkyFunctions.TOP_LEVEL_ASPECTS;
}
- String getStarlarkValueName() {
- return starlarkValueName;
- }
-
- Label getStarlarkFileLabel() {
- return starlarkFileLabel;
- }
-
- @Override
- public String getAspectName() {
- return String.format("%s%%%s", starlarkFileLabel, starlarkValueName);
+ ImmutableList<AspectClass> getTopLevelAspectsClasses() {
+ return topLevelAspectsClasses;
}
@Override
@@ -359,27 +323,8 @@
return targetLabel;
}
- @Override
- public String getDescription() {
- // Starlark aspects are referred to on command line with <file>%<value ame>
- return String.format("%s%%%s of %s", starlarkFileLabel, starlarkValueName, targetLabel);
- }
-
- @Nullable
- @Override
- BuildConfigurationValue.Key getAspectConfigurationKey() {
- return aspectConfigurationKey;
- }
-
- /** Returns the key for the base configured target for this aspect. */
- @Override
- public ConfiguredTargetKey getBaseConfiguredTargetKey() {
- return baseConfiguredTargetKey;
- }
-
- @Override
- public int hashCode() {
- return hashCode;
+ String getDescription() {
+ return topLevelAspectsClasses + " on " + getLabel();
}
@Override
@@ -387,35 +332,14 @@
if (o == this) {
return true;
}
- if (!(o instanceof StarlarkAspectLoadingKey)) {
+ if (!(o instanceof TopLevelAspectsKey)) {
return false;
}
- StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o;
- return hashCode == that.hashCode
+ TopLevelAspectsKey that = (TopLevelAspectsKey) o;
+ return hashCode() == that.hashCode()
&& Objects.equal(targetLabel, that.targetLabel)
- && Objects.equal(aspectConfigurationKey, that.aspectConfigurationKey)
- && Objects.equal(baseConfiguredTargetKey, that.baseConfiguredTargetKey)
- && Objects.equal(starlarkFileLabel, that.starlarkFileLabel)
- && Objects.equal(starlarkValueName, that.starlarkValueName);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("targetLabel", targetLabel)
- .add("aspectConfigurationKey", aspectConfigurationKey)
- .add("baseConfiguredTargetKey", baseConfiguredTargetKey)
- .add("starlarkFileLabel", starlarkFileLabel)
- .add("starlarkValueName", starlarkValueName)
- .toString();
- }
-
- AspectKey toAspectKey(AspectClass aspectClass) {
- return AspectKey.createAspectKey(
- baseConfiguredTargetKey,
- ImmutableList.of(),
- new AspectDescriptor(aspectClass, AspectParameters.EMPTY),
- aspectConfigurationKey);
+ && Objects.equal(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
+ && Objects.equal(topLevelAspectsClasses, that.topLevelAspectsClasses);
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index 39518ce..ac2ee83 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -26,6 +26,7 @@
"BazelSkyframeExecutorConstants.java",
"BuildConfigurationFunction.java",
"BuildInfoCollectionFunction.java",
+ "BuildTopLevelAspectsDetailsFunction.java",
"BzlLoadFunction.java",
"BzlmodRepoRuleFunction.java",
"CompletionFunction.java",
@@ -38,6 +39,7 @@
"ExternalFilesHelper.java",
"ExternalPackageFunction.java",
"FileStateFunction.java",
+ "LoadStarlarkAspectFunction.java",
"LocalRepositoryLookupFunction.java",
"NonRuleConfiguredTargetValue.java",
"PackageFunction.java",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java
new file mode 100644
index 0000000..eb68e55
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java
@@ -0,0 +1,302 @@
+// Copyright 2021 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.skyframe;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Interner;
+import com.google.devtools.build.lib.analysis.AspectCollection;
+import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.Aspect;
+import com.google.devtools.build.lib.packages.AspectClass;
+import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.packages.AspectsListBuilder;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
+import com.google.devtools.build.lib.packages.StarlarkAspect;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingValue;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import net.starlark.java.eval.EvalException;
+
+/**
+ * SkyFunction to load top level aspects, build the dependency relation between them based on the
+ * aspects required by the top level aspects and the aspect providers they require and advertise
+ * using {@link AspectCollection}.
+ *
+ * <p>This is needed to compute the relationship between top-level aspects once for all top-level
+ * targets in the command. The {@link SkyValue} of this function contains a list of {@link
+ * AspectDetails} objects which contain the aspect descriptor and a list of the used aspects this
+ * aspect depends on. Then {@link ToplevelStarlarkAspectFunction} adds the target information to
+ * create {@link AspectKey}.
+ */
+public class BuildTopLevelAspectsDetailsFunction implements SkyFunction {
+ BuildTopLevelAspectsDetailsFunction() {}
+
+ private static final Interner<BuildTopLevelAspectsDetailsKey>
+ buildTopLevelAspectsDetailsKeyInterner = BlazeInterners.newWeakInterner();
+
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env)
+ throws BuildTopLevelAspectsDetailsFunctionException, InterruptedException {
+ BuildTopLevelAspectsDetailsKey topLevelAspectsDetailsKey =
+ (BuildTopLevelAspectsDetailsKey) skyKey.argument();
+
+ ImmutableList<Aspect> topLevelAspects =
+ getTopLevelAspects(env, topLevelAspectsDetailsKey.getTopLevelAspectsClasses());
+
+ if (topLevelAspects == null) {
+ return null; // some aspects are not loaded
+ }
+
+ AspectCollection aspectCollection;
+ try {
+ aspectCollection = AspectCollection.create(topLevelAspects);
+ } catch (AspectCycleOnPathException e) {
+ // This exception should never happen because aspects duplicates are not allowed in top-level
+ // aspects and their existence should have been caught and reported by `getTopLevelAspects()`.
+ env.getListener().handle(Event.error(e.getMessage()));
+ throw new BuildTopLevelAspectsDetailsFunctionException(
+ new TopLevelAspectsDetailsBuildFailedException(e.getMessage()));
+ }
+ return new BuildTopLevelAspectsDetailsValue(getTopLevelAspectsDetails(aspectCollection));
+ }
+
+ @Nullable
+ @Override
+ public String extractTag(SkyKey skyKey) {
+ return null;
+ }
+
+ @Nullable
+ private static ImmutableList<Aspect> getTopLevelAspects(
+ Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
+ throws InterruptedException, BuildTopLevelAspectsDetailsFunctionException {
+ AspectsListBuilder aspectsList = new AspectsListBuilder();
+
+ ImmutableList.Builder<StarlarkAspectLoadingKey> aspectLoadingKeys = ImmutableList.builder();
+ for (AspectClass aspectClass : topLevelAspectsClasses) {
+ if (aspectClass instanceof StarlarkAspectClass) {
+ aspectLoadingKeys.add(
+ LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey(
+ (StarlarkAspectClass) aspectClass));
+ }
+ }
+
+ Map<SkyKey, SkyValue> loadedAspects = env.getValues(aspectLoadingKeys.build());
+ if (env.valuesMissing()) {
+ return null;
+ }
+
+ for (AspectClass aspectClass : topLevelAspectsClasses) {
+ if (aspectClass instanceof StarlarkAspectClass) {
+ StarlarkAspectLoadingValue aspectLoadingValue =
+ (StarlarkAspectLoadingValue)
+ loadedAspects.get(
+ LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey(
+ (StarlarkAspectClass) aspectClass));
+ StarlarkAspect starlarkAspect = aspectLoadingValue.getAspect();
+ try {
+ starlarkAspect.attachToAspectsList(
+ /** baseAspectName= */
+ null,
+ aspectsList,
+ /** inheritedRequiredProviders= */
+ ImmutableList.of(),
+ /** inheritedAttributeAspects= */
+ ImmutableList.of(),
+ /** allowAspectsParameters= */
+ false);
+ } catch (EvalException e) {
+ env.getListener().handle(Event.error(e.getMessage()));
+ throw new BuildTopLevelAspectsDetailsFunctionException(
+ new TopLevelAspectsDetailsBuildFailedException(e.getMessage()));
+ }
+ } else {
+ try {
+ aspectsList.addAspect((NativeAspectClass) aspectClass);
+ } catch (AssertionError ex) {
+ env.getListener().handle(Event.error(ex.getMessage()));
+ throw new BuildTopLevelAspectsDetailsFunctionException(
+ new TopLevelAspectsDetailsBuildFailedException(ex.getMessage()));
+ }
+ }
+ }
+ return aspectsList.buildAspects();
+ }
+
+ private static Collection<AspectDetails> getTopLevelAspectsDetails(
+ AspectCollection aspectCollection) {
+ Map<AspectDescriptor, AspectDetails> result = new HashMap<>();
+ for (AspectCollection.AspectDeps aspectDeps : aspectCollection.getUsedAspects()) {
+ buildAspectDetails(aspectDeps, result);
+ }
+ return result.values();
+ }
+
+ private static AspectDetails buildAspectDetails(
+ AspectCollection.AspectDeps aspectDeps, Map<AspectDescriptor, AspectDetails> result) {
+ if (result.containsKey(aspectDeps.getAspect())) {
+ return result.get(aspectDeps.getAspect());
+ }
+
+ ImmutableList.Builder<AspectDetails> dependentAspects = ImmutableList.builder();
+ for (AspectCollection.AspectDeps path : aspectDeps.getUsedAspects()) {
+ dependentAspects.add(buildAspectDetails(path, result));
+ }
+
+ AspectDetails aspectDetails =
+ new AspectDetails(dependentAspects.build(), aspectDeps.getAspect());
+ result.put(aspectDetails.getAspectDescriptor(), aspectDetails);
+ return aspectDetails;
+ }
+
+ public static BuildTopLevelAspectsDetailsKey createBuildTopLevelAspectsDetailsKey(
+ ImmutableList<AspectClass> aspectClasses) {
+ return BuildTopLevelAspectsDetailsKey.createInternal(aspectClasses);
+ }
+
+ /** Exceptions thrown from BuildTopLevelAspectsDetailsFunction. */
+ public static class BuildTopLevelAspectsDetailsFunctionException extends SkyFunctionException {
+ public BuildTopLevelAspectsDetailsFunctionException(
+ TopLevelAspectsDetailsBuildFailedException cause) {
+ super(cause, Transience.PERSISTENT);
+ }
+ }
+
+ static final class TopLevelAspectsDetailsBuildFailedException extends Exception
+ implements SaneAnalysisException {
+ private final DetailedExitCode detailedExitCode;
+
+ private TopLevelAspectsDetailsBuildFailedException(String errorMessage) {
+ super(errorMessage);
+ this.detailedExitCode =
+ DetailedExitCode.of(
+ FailureDetail.newBuilder()
+ .setMessage(errorMessage)
+ .setAnalysis(Analysis.newBuilder().setCode(Code.ASPECT_CREATION_FAILED))
+ .build());
+ }
+
+ @Override
+ public DetailedExitCode getDetailedExitCode() {
+ return detailedExitCode;
+ }
+ }
+
+ /**
+ * Details of the top-level aspects including the {@link AspectDescriptor} and a list of the
+ * aspects it depends on. This is used to build the {@link AspectKey} when combined with
+ * configured target details.
+ */
+ public static final class AspectDetails {
+ private final ImmutableList<AspectDetails> usedAspects;
+ private final AspectDescriptor aspectDescriptor;
+
+ AspectDetails(ImmutableList<AspectDetails> usedAspects, AspectDescriptor aspectDescriptor) {
+ this.usedAspects = usedAspects;
+ this.aspectDescriptor = aspectDescriptor;
+ }
+
+ public AspectDescriptor getAspectDescriptor() {
+ return aspectDescriptor;
+ }
+
+ public ImmutableList<AspectDetails> getUsedAspects() {
+ return usedAspects;
+ }
+ }
+
+ /** SkyKey for building top-level aspects details. */
+ public static final class BuildTopLevelAspectsDetailsKey implements SkyKey {
+ private final ImmutableList<AspectClass> topLevelAspectsClasses;
+ private final int hashCode;
+
+ @AutoCodec.Instantiator
+ @AutoCodec.VisibleForSerialization
+ static BuildTopLevelAspectsDetailsKey createInternal(
+ ImmutableList<AspectClass> topLevelAspectsClasses) {
+ return buildTopLevelAspectsDetailsKeyInterner.intern(
+ new BuildTopLevelAspectsDetailsKey(
+ topLevelAspectsClasses, java.util.Objects.hashCode(topLevelAspectsClasses)));
+ }
+
+ private BuildTopLevelAspectsDetailsKey(
+ ImmutableList<AspectClass> topLevelAspectsClasses, int hashCode) {
+ this.topLevelAspectsClasses = topLevelAspectsClasses;
+ this.hashCode = hashCode;
+ }
+
+ @Override
+ public SkyFunctionName functionName() {
+ return SkyFunctions.BUILD_TOP_LEVEL_ASPECTS_DETAILS;
+ }
+
+ ImmutableList<AspectClass> getTopLevelAspectsClasses() {
+ return topLevelAspectsClasses;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof BuildTopLevelAspectsDetailsKey)) {
+ return false;
+ }
+ BuildTopLevelAspectsDetailsKey that = (BuildTopLevelAspectsDetailsKey) o;
+ return hashCode == that.hashCode
+ && Objects.equal(topLevelAspectsClasses, that.topLevelAspectsClasses);
+ }
+ }
+
+ /**
+ * SkyValue for {@code BuildTopLevelAspectsDetailsKey} wraps a list of the {@code AspectDetails}
+ * of the top level aspects.
+ */
+ public static final class BuildTopLevelAspectsDetailsValue implements SkyValue {
+ private final ImmutableList<AspectDetails> aspectsDetails;
+
+ private BuildTopLevelAspectsDetailsValue(Collection<AspectDetails> aspectsDetails) {
+ this.aspectsDetails = ImmutableList.copyOf(aspectsDetails);
+ }
+
+ public ImmutableList<AspectDetails> getAspectsDetails() {
+ return aspectsDetails;
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java
new file mode 100644
index 0000000..4d81910
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java
@@ -0,0 +1,169 @@
+// Copyright 2021 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.skyframe;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Interner;
+import com.google.devtools.build.lib.causes.LabelCause;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
+import com.google.devtools.build.lib.packages.StarlarkAspect;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import javax.annotation.Nullable;
+
+/**
+ * SkyFunction to load aspects from Starlark extensions and return StarlarkAspect.
+ *
+ * <p>Used for loading top-level aspects. At top level, in {@link
+ * com.google.devtools.build.lib.analysis.BuildView}, we cannot invoke two SkyFunctions one after
+ * another, so BuildView calls this function to do the work.
+ */
+public class LoadStarlarkAspectFunction implements SkyFunction {
+ private static final Interner<StarlarkAspectLoadingKey> starlarkAspectLoadingKeyInterner =
+ BlazeInterners.newWeakInterner();
+
+ LoadStarlarkAspectFunction() {}
+
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env)
+ throws LoadStarlarkAspectFunctionException, InterruptedException {
+ StarlarkAspectLoadingKey aspectLoadingKey = (StarlarkAspectLoadingKey) skyKey.argument();
+
+ Label extensionLabel = aspectLoadingKey.getAspectClass().getExtensionLabel();
+ String exportedName = aspectLoadingKey.getAspectClass().getExportedName();
+ StarlarkAspect starlarkAspect;
+ try {
+ starlarkAspect = AspectFunction.loadStarlarkAspect(env, extensionLabel, exportedName);
+ if (starlarkAspect == null) {
+ return null;
+ }
+ if (!starlarkAspect.getParamAttributes().isEmpty()) {
+ String msg =
+ String.format(
+ "Cannot instantiate parameterized aspect %s at the top level.",
+ starlarkAspect.getName());
+ throw new AspectCreationException(
+ msg,
+ new LabelCause(
+ extensionLabel,
+ createDetailedCode(msg, Code.PARAMETERIZED_TOP_LEVEL_ASPECT_INVALID)));
+ }
+ } catch (AspectCreationException e) {
+ throw new LoadStarlarkAspectFunctionException(e);
+ }
+
+ return new StarlarkAspectLoadingValue(starlarkAspect);
+ }
+
+ @Nullable
+ @Override
+ public String extractTag(SkyKey skyKey) {
+ return null;
+ }
+
+ private static DetailedExitCode createDetailedCode(String msg, Code code) {
+ return DetailedExitCode.of(
+ FailureDetail.newBuilder()
+ .setMessage(msg)
+ .setAnalysis(Analysis.newBuilder().setCode(code))
+ .build());
+ }
+
+ /** Exceptions thrown from LoadStarlarkAspectFunction. */
+ public static class LoadStarlarkAspectFunctionException extends SkyFunctionException {
+ public LoadStarlarkAspectFunctionException(AspectCreationException cause) {
+ super(cause, Transience.PERSISTENT);
+ }
+ }
+
+ public static StarlarkAspectLoadingKey createStarlarkAspectLoadingKey(
+ StarlarkAspectClass aspectClass) {
+ return StarlarkAspectLoadingKey.createInternal(aspectClass);
+ }
+
+ /** Skykey for loading Starlark aspect. */
+ @AutoCodec
+ public static final class StarlarkAspectLoadingKey implements SkyKey {
+ private final StarlarkAspectClass aspectClass;
+ private final int hashCode;
+
+ @AutoCodec.Instantiator
+ @AutoCodec.VisibleForSerialization
+ static StarlarkAspectLoadingKey createInternal(StarlarkAspectClass aspectClass) {
+ return starlarkAspectLoadingKeyInterner.intern(
+ new StarlarkAspectLoadingKey(aspectClass, java.util.Objects.hashCode(aspectClass)));
+ }
+
+ private StarlarkAspectLoadingKey(StarlarkAspectClass aspectClass, int hashCode) {
+ this.aspectClass = aspectClass;
+ this.hashCode = hashCode;
+ }
+
+ @Override
+ public SkyFunctionName functionName() {
+ return SkyFunctions.LOAD_STARLARK_ASPECT;
+ }
+
+ StarlarkAspectClass getAspectClass() {
+ return aspectClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof StarlarkAspectLoadingKey)) {
+ return false;
+ }
+ StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o;
+ return hashCode == that.hashCode && Objects.equal(aspectClass, that.aspectClass);
+ }
+
+ @Override
+ public String toString() {
+ return aspectClass.toString();
+ }
+ }
+
+ /** SkyValue for {@code StarlarkAspectLoadingKey} holds the loaded {@code StarlarkAspect}. */
+ public static class StarlarkAspectLoadingValue implements SkyValue {
+ private final StarlarkAspect starlarkAspect;
+
+ public StarlarkAspectLoadingValue(StarlarkAspect starlarkAspect) {
+ this.starlarkAspect = starlarkAspect;
+ }
+
+ public StarlarkAspect getAspect() {
+ return starlarkAspect;
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
index cb07796..c94ce04 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
@@ -88,6 +88,10 @@
public static final SkyFunctionName ASPECT = SkyFunctionName.createHermetic("ASPECT");
static final SkyFunctionName LOAD_STARLARK_ASPECT =
SkyFunctionName.createHermetic("LOAD_STARLARK_ASPECT");
+ static final SkyFunctionName TOP_LEVEL_ASPECTS =
+ SkyFunctionName.createHermetic("TOP_LEVEL_ASPECTS");
+ static final SkyFunctionName BUILD_TOP_LEVEL_ASPECTS_DETAILS =
+ SkyFunctionName.createHermetic("BUILD_TOP_LEVEL_ASPECTS_DETAILS");
public static final SkyFunctionName TARGET_COMPLETION =
SkyFunctionName.create(
"TARGET_COMPLETION", ShareabilityOfValue.NEVER, FunctionHermeticity.HERMETIC);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
index c718762..792dc66 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
@@ -88,7 +88,9 @@
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.skyframe.ArtifactConflictFinder.ConflictException;
import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor.TopLevelActionConflictReport;
+import com.google.devtools.build.lib.skyframe.ToplevelStarlarkAspectFunction.TopLevelAspectsValue;
import com.google.devtools.build.lib.util.DetailedExitCode;
import com.google.devtools.build.lib.util.OrderedSetMultimap;
import com.google.devtools.build.lib.util.Pair;
@@ -380,7 +382,7 @@
public SkyframeAnalysisResult configureTargets(
ExtendedEventHandler eventHandler,
List<ConfiguredTargetKey> ctKeys,
- List<AspectValueKey> aspectKeys,
+ ImmutableList<TopLevelAspectsKey> topLevelAspectsKey,
Supplier<Map<BuildConfigurationValue.Key, BuildConfiguration>> configurationLookupSupplier,
TopLevelArtifactContext topLevelArtifactContextForConflictPruning,
EventBus eventBus,
@@ -397,7 +399,7 @@
skyframeExecutor.configureTargets(
eventHandler,
ctKeys,
- aspectKeys,
+ topLevelAspectsKey,
keepGoing,
numThreads,
cpuHeavySkyKeysThreadPoolSize);
@@ -405,21 +407,33 @@
enableAnalysis(false);
}
- Map<AspectKey, ConfiguredAspect> aspects = Maps.newHashMapWithExpectedSize(aspectKeys.size());
+ int numOfAspects = 0;
+ if (!topLevelAspectsKey.isEmpty()) {
+ numOfAspects =
+ topLevelAspectsKey.size() * topLevelAspectsKey.get(0).getTopLevelAspectsClasses().size();
+ }
+ Map<AspectKey, ConfiguredAspect> aspects = Maps.newHashMapWithExpectedSize(numOfAspects);
Root singleSourceRoot = skyframeExecutor.getForcedSingleSourceRootIfNoExecrootSymlinkCreation();
NestedSetBuilder<Package> packages =
singleSourceRoot == null ? NestedSetBuilder.stableOrder() : null;
- for (AspectValueKey aspectKey : aspectKeys) {
- AspectValue value = (AspectValue) result.get(aspectKey);
+ ImmutableList.Builder<AspectKey> aspectKeysBuilder = ImmutableList.builder();
+
+ for (TopLevelAspectsKey key : topLevelAspectsKey) {
+ TopLevelAspectsValue value = (TopLevelAspectsValue) result.get(key);
if (value == null) {
// Skip aspects that couldn't be applied to targets.
continue;
}
- aspects.put(value.getKey(), value.getConfiguredAspect());
- if (packages != null) {
- packages.addTransitive(value.getTransitivePackagesForPackageRootResolution());
+ for (SkyValue val : value.getTopLevelAspectsValues()) {
+ AspectValue aspectValue = (AspectValue) val;
+ aspects.put(aspectValue.getKey(), aspectValue.getConfiguredAspect());
+ if (packages != null) {
+ packages.addTransitive(aspectValue.getTransitivePackagesForPackageRootResolution());
+ }
+ aspectKeysBuilder.add(aspectValue.getKey());
}
}
+ ImmutableList<AspectKey> aspectKeys = aspectKeysBuilder.build();
Collection<ConfiguredTarget> cts = Lists.newArrayListWithCapacity(ctKeys.size());
for (ConfiguredTargetKey value : ctKeys) {
@@ -561,7 +575,7 @@
BuildConfigurationValue.Key configKey =
ctKey instanceof ConfiguredTargetKey
? ((ConfiguredTargetKey) ctKey).getConfigurationKey()
- : ((AspectValueKey) ctKey).getAspectConfigurationKey();
+ : ((AspectKey) ctKey).getAspectConfigurationKey();
eventBus.post(
new AnalysisFailureEvent(
ctKey,
@@ -597,13 +611,9 @@
.collect(toImmutableList());
aspects =
- aspectKeys.stream()
- .filter(topLevelActionConflictReport::isErrorFree)
- .map(result::get)
- .map(AspectValue.class::cast)
- .collect(
- ImmutableMap.toImmutableMap(
- AspectValue::getKey, AspectValue::getConfiguredAspect));
+ aspects.entrySet().stream()
+ .filter(e -> topLevelActionConflictReport.isErrorFree(e.getKey()))
+ .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
}
return new SkyframeAnalysisResult(
@@ -745,17 +755,15 @@
.reportCycles(errorInfo.getCycleInfo(), errorKey, eventHandler);
Exception cause = errorInfo.getException();
Preconditions.checkState(cause != null || !errorInfo.getCycleInfo().isEmpty(), errorInfo);
-
- if (errorKey.argument() instanceof AspectValueKey) {
+ if (errorKey.argument() instanceof TopLevelAspectsKey) {
// We skip Aspects in the keepGoing case; the failures should already have been reported to
// the event handler.
if (!keepGoing && noKeepGoingException == null) {
- AspectValueKey aspectKey = (AspectValueKey) errorKey.argument();
+ TopLevelAspectsKey aspectKey = (TopLevelAspectsKey) errorKey.argument();
failedAspectLabel = aspectKey.getBaseConfiguredTargetKey();
-
String errorMsg =
String.format(
- "Analysis of aspect '%s' failed; build aborted", aspectKey.getDescription());
+ "Analysis of aspects '%s' failed; build aborted", aspectKey.getDescription());
noKeepGoingException = createViewCreationFailedException(cause, errorMsg);
}
continue;
@@ -776,7 +784,7 @@
}
Preconditions.checkState(
errorKey.argument() instanceof ConfiguredTargetKey,
- "expected '%s' to be a AspectValueKey or ConfiguredTargetKey",
+ "expected '%s' to be a TopLevelAspectsKey or ConfiguredTargetKey",
errorKey.argument());
ConfiguredTargetKey label = (ConfiguredTargetKey) errorKey.argument();
Label topLevelLabel = label.getLabel();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index d5e9066..74521b1 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -162,6 +162,7 @@
import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns;
import com.google.devtools.build.lib.skyframe.ArtifactConflictFinder.ConflictException;
import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.FileDirtinessChecker;
import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
import com.google.devtools.build.lib.skyframe.MetadataConsumerForMetrics.FilesMetricConsumer;
@@ -574,7 +575,10 @@
new BuildViewProvider(),
ruleClassProvider,
shouldStoreTransitivePackagesInLoadingAndAnalysis()));
- map.put(SkyFunctions.LOAD_STARLARK_ASPECT, new ToplevelStarlarkAspectFunction());
+ map.put(SkyFunctions.LOAD_STARLARK_ASPECT, new LoadStarlarkAspectFunction());
+ map.put(SkyFunctions.TOP_LEVEL_ASPECTS, new ToplevelStarlarkAspectFunction());
+ map.put(
+ SkyFunctions.BUILD_TOP_LEVEL_ASPECTS_DETAILS, new BuildTopLevelAspectsDetailsFunction());
map.put(SkyFunctions.ACTION_LOOKUP_CONFLICT_FINDING, new ActionLookupConflictFindingFunction());
map.put(
SkyFunctions.TOP_LEVEL_ACTION_LOOKUP_CONFLICT_FINDING,
@@ -2345,7 +2349,7 @@
EvaluationResult<ActionLookupValue> configureTargets(
ExtendedEventHandler eventHandler,
List<ConfiguredTargetKey> values,
- List<AspectValueKey> aspectKeys,
+ ImmutableList<TopLevelAspectsKey> aspectKeys,
boolean keepGoing,
int numThreads,
int cpuHeavySkyKeysThreadPoolSize)
@@ -3082,7 +3086,7 @@
}
final AnalysisTraversalResult getActionLookupValuesInBuild(
- List<ConfiguredTargetKey> topLevelCtKeys, List<AspectValueKey> aspectKeys)
+ List<ConfiguredTargetKey> topLevelCtKeys, ImmutableList<AspectKey> aspectKeys)
throws InterruptedException {
AnalysisTraversalResult result = new AnalysisTraversalResult();
if (!isAnalysisIncremental()) {
@@ -3102,7 +3106,7 @@
for (ConfiguredTargetKey key : topLevelCtKeys) {
findActionsRecursively(walkableGraph, key, seen, result);
}
- for (AspectValueKey key : aspectKeys) {
+ for (AspectKey key : aspectKeys) {
findActionsRecursively(walkableGraph, key, seen, result);
}
return result;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java b/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
index cf8f828..693bde7 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
@@ -39,7 +39,7 @@
Predicates.or(
SkyFunctions.isSkyFunction(SkyFunctions.CONFIGURED_TARGET),
SkyFunctions.isSkyFunction(SkyFunctions.ASPECT),
- SkyFunctions.isSkyFunction(SkyFunctions.LOAD_STARLARK_ASPECT),
+ SkyFunctions.isSkyFunction(SkyFunctions.TOP_LEVEL_ASPECTS),
SkyFunctions.isSkyFunction(TransitiveTargetKey.NAME),
SkyFunctions.isSkyFunction(SkyFunctions.PREPARE_ANALYSIS_PHASE));
@@ -65,8 +65,6 @@
return ((ConfiguredTargetKey) key.argument()).prettyPrint();
} else if (key instanceof AspectKey) {
return ((AspectKey) key.argument()).prettyPrint();
- } else if (key instanceof AspectValueKey) {
- return ((AspectValueKey) key).getDescription();
} else {
return getLabel(key).toString();
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
index a858e35..4fd3bb1 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
@@ -14,22 +14,26 @@
package com.google.devtools.build.lib.skyframe;
-import com.google.devtools.build.lib.causes.LabelCause;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.packages.StarlarkAspect;
-import com.google.devtools.build.lib.server.FailureDetails.Analysis;
-import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
-import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
-import com.google.devtools.build.lib.skyframe.AspectValueKey.StarlarkAspectLoadingKey;
-import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.ActionLookupValue;
+import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
+import com.google.devtools.build.lib.skyframe.BuildTopLevelAspectsDetailsFunction.AspectDetails;
+import com.google.devtools.build.lib.skyframe.BuildTopLevelAspectsDetailsFunction.BuildTopLevelAspectsDetailsValue;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import javax.annotation.Nullable;
/**
- * SkyFunction to load aspects from Starlark extensions and calculate their values.
+ * SkyFunction to run the aspects path obtained from top-level aspects on the list of top-level
+ * targets.
*
* <p>Used for loading top-level aspects. At top level, in {@link
* com.google.devtools.build.lib.analysis.BuildView}, we cannot invoke two SkyFunctions one after
@@ -41,34 +45,29 @@
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env)
- throws LoadStarlarkAspectFunctionException, InterruptedException {
- StarlarkAspectLoadingKey aspectLoadingKey = (StarlarkAspectLoadingKey) skyKey.argument();
- String starlarkValueName = aspectLoadingKey.getStarlarkValueName();
- Label starlarkFileLabel = aspectLoadingKey.getStarlarkFileLabel();
+ throws TopLevelStarlarkAspectFunctionException, InterruptedException {
+ TopLevelAspectsKey topLevelAspectsKey = (TopLevelAspectsKey) skyKey.argument();
- StarlarkAspect starlarkAspect;
- try {
- starlarkAspect = AspectFunction.loadStarlarkAspect(env, starlarkFileLabel, starlarkValueName);
- if (starlarkAspect == null) {
- return null;
- }
- if (!starlarkAspect.getParamAttributes().isEmpty()) {
- String msg =
- String.format(
- "Cannot instantiate parameterized aspect %s at the top level.",
- starlarkAspect.getName());
- throw new AspectCreationException(
- msg,
- new LabelCause(
- starlarkFileLabel,
- createDetailedCode(msg, Code.PARAMETERIZED_TOP_LEVEL_ASPECT_INVALID)));
- }
- } catch (AspectCreationException e) {
- throw new LoadStarlarkAspectFunctionException(e);
+ BuildTopLevelAspectsDetailsValue topLevelAspectsDetails =
+ (BuildTopLevelAspectsDetailsValue)
+ env.getValue(
+ BuildTopLevelAspectsDetailsFunction.createBuildTopLevelAspectsDetailsKey(
+ topLevelAspectsKey.getTopLevelAspectsClasses()));
+ if (topLevelAspectsDetails == null) {
+ return null; // some aspects details are not ready
}
- SkyKey aspectKey = aspectLoadingKey.toAspectKey(starlarkAspect.getAspectClass());
- return env.getValue(aspectKey);
+ Collection<AspectKey> aspectsKeys =
+ getTopLevelAspectsKeys(
+ topLevelAspectsDetails.getAspectsDetails(),
+ topLevelAspectsKey.getBaseConfiguredTargetKey());
+
+ Map<SkyKey, SkyValue> result = env.getValues(aspectsKeys);
+ if (env.valuesMissing()) {
+ return null; // some aspects keys are not evaluated
+ }
+
+ return new TopLevelAspectsValue(result.values());
}
@Nullable
@@ -77,18 +76,63 @@
return null;
}
- private static DetailedExitCode createDetailedCode(String msg, Code code) {
- return DetailedExitCode.of(
- FailureDetail.newBuilder()
- .setMessage(msg)
- .setAnalysis(Analysis.newBuilder().setCode(code))
- .build());
+ private static Collection<AspectKey> getTopLevelAspectsKeys(
+ ImmutableList<AspectDetails> aspectsDetails, ConfiguredTargetKey topLevelTargetKey) {
+ Map<AspectDescriptor, AspectKey> result = new HashMap<>();
+ for (AspectDetails aspect : aspectsDetails) {
+ buildAspectKey(aspect, result, topLevelTargetKey);
+ }
+ return result.values();
+ }
+
+ private static AspectKey buildAspectKey(
+ AspectDetails aspect,
+ Map<AspectDescriptor, AspectKey> result,
+ ConfiguredTargetKey topLevelTargetKey) {
+ if (result.containsKey(aspect.getAspectDescriptor())) {
+ return result.get(aspect.getAspectDescriptor());
+ }
+
+ ImmutableList.Builder<AspectKey> dependentAspects = ImmutableList.builder();
+ for (AspectDetails depAspect : aspect.getUsedAspects()) {
+ dependentAspects.add(buildAspectKey(depAspect, result, topLevelTargetKey));
+ }
+
+ AspectKey aspectKey =
+ AspectValueKey.createAspectKey(
+ aspect.getAspectDescriptor(),
+ dependentAspects.build(),
+ topLevelTargetKey.getConfigurationKey(),
+ topLevelTargetKey);
+ result.put(aspectKey.getAspectDescriptor(), aspectKey);
+ return aspectKey;
}
/** Exceptions thrown from ToplevelStarlarkAspectFunction. */
- public static class LoadStarlarkAspectFunctionException extends SkyFunctionException {
- public LoadStarlarkAspectFunctionException(AspectCreationException cause) {
+ public static class TopLevelStarlarkAspectFunctionException extends SkyFunctionException {
+ public TopLevelStarlarkAspectFunctionException(AspectCreationException cause) {
super(cause, Transience.PERSISTENT);
}
}
+
+ /**
+ * SkyValue for {@code TopLevelAspectsKey} wraps a list of the {@code AspectValue} of the top
+ * level aspects applied on the same top level target.
+ */
+ public static class TopLevelAspectsValue implements ActionLookupValue {
+ private final ImmutableList<SkyValue> topLevelAspectsValues;
+
+ public TopLevelAspectsValue(Collection<SkyValue> topLevelAspectsValues) {
+ this.topLevelAspectsValues = ImmutableList.copyOf(topLevelAspectsValues);
+ }
+
+ public ImmutableList<SkyValue> getTopLevelAspectsValues() {
+ return topLevelAspectsValues;
+ }
+
+ @Override
+ public ImmutableList<ActionAnalysisMetadata> getActions() {
+ return ImmutableList.of();
+ }
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
index 4a6c42e..f29f0e7 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
@@ -881,16 +881,18 @@
}
@Test
- public void duplicateAspectsDeduped() throws Exception {
+ public void duplicateTopLevelAspects_duplicateAspectsIgnored() throws Exception {
AspectApplyingToFiles aspectApplyingToFiles = new AspectApplyingToFiles();
setRulesAndAspectsAvailableInTests(ImmutableList.of(aspectApplyingToFiles), ImmutableList.of());
pkg("a", "java_binary(name = 'x', main_class = 'x.FooBar', srcs = ['x.java'])");
+
AnalysisResult analysisResult =
update(
new EventBus(),
defaultFlags(),
ImmutableList.of(aspectApplyingToFiles.getName(), aspectApplyingToFiles.getName()),
"//a:x_deploy.jar");
+
ConfiguredAspect aspect = Iterables.getOnlyElement(analysisResult.getAspectsMap().values());
AspectApplyingToFiles.Provider provider =
aspect.getProvider(AspectApplyingToFiles.Provider.class);
@@ -898,6 +900,25 @@
}
@Test
+ public void duplicateTopLevelAspects_duplicateAspectsNotAllowed() throws Exception {
+ AspectApplyingToFiles aspectApplyingToFiles = new AspectApplyingToFiles();
+ setRulesAndAspectsAvailableInTests(ImmutableList.of(aspectApplyingToFiles), ImmutableList.of());
+ pkg("a", "java_binary(name = 'x', main_class = 'x.FooBar', srcs = ['x.java'])");
+ useConfiguration("--incompatible_ignore_duplicate_top_level_aspects=false");
+ reporter.removeHandler(failFastHandler);
+
+ assertThrows(
+ ViewCreationFailedException.class,
+ () ->
+ update(
+ new EventBus(),
+ defaultFlags(),
+ ImmutableList.of(aspectApplyingToFiles.getName(), aspectApplyingToFiles.getName()),
+ "//a:x_deploy.jar"));
+ assertContainsEvent("Aspect AspectApplyingToFiles has already been added");
+ }
+
+ @Test
public void sameConfiguredAttributeOnAspectAndRule() throws Exception {
scratch.file(
"a/a.bzl",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TargetCycleReporterTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TargetCycleReporterTest.java
index 17ec740..f491bea 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TargetCycleReporterTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TargetCycleReporterTest.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
import com.google.devtools.build.skyframe.CycleInfo;
import com.google.devtools.build.skyframe.SkyKey;
import org.junit.Test;
@@ -70,12 +71,12 @@
+ "target //foo:c");
SkyKey starlarkAspectKey =
- AspectValueKey.createStarlarkAspectKey(
+ AspectValueKey.createTopLevelAspectsKey(
+ ImmutableList.of(
+ new StarlarkAspectClass(
+ Label.parseAbsoluteUnchecked("//foo:b"), "my Starlark key")),
Label.parseAbsoluteUnchecked("//foo:a"),
- targetConfig,
- targetConfig,
- Label.parseAbsoluteUnchecked("//foo:b"),
- "my Starlark key");
+ targetConfig);
assertThat(cycleReporter.getAdditionalMessageAboutCycle(reporter, starlarkAspectKey, cycle))
.contains(
"The cycle is caused by a visibility edge from //foo:b to the non-package_group "
diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkDefinedAspectsTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkDefinedAspectsTest.java
index c43cb96..f67cb7b 100644
--- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkDefinedAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkDefinedAspectsTest.java
@@ -29,17 +29,21 @@
import com.google.devtools.build.lib.analysis.AnalysisResult;
import com.google.devtools.build.lib.analysis.AspectValue;
import com.google.devtools.build.lib.analysis.ConfiguredAspect;
+import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.analysis.config.HostTransition;
import com.google.devtools.build.lib.analysis.config.transitions.NoTransition;
import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
+import com.google.devtools.build.lib.analysis.util.TestAspects;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.AspectDefinition;
+import com.google.devtools.build.lib.packages.RequiredProviders;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
import com.google.devtools.build.lib.packages.StarlarkProvider;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
@@ -48,10 +52,12 @@
import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkInt;
@@ -4193,6 +4199,2536 @@
assertThat(aspectBResult).isEqualTo("aspect_b on target //test:dep_target cannot find prov_b");
}
+ /**
+ * --aspects = a3, a2, a1: aspect a1 requires provider a1p, aspect a2 requires provider a2p and
+ * provides a1p and aspect a3 provides a2p. The three aspects will propagate together but aspect
+ * a1 will only see a1p and aspect a2 will only see a2p.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_stackOfAspects() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "a2_result = provider()",
+ "a3_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " if a2p in target:",
+ " result += ' and sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' and cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " result = 'aspect a2 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " if a2p in target:",
+ " result += ' and sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' and cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a2_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a2_result(value = complete_result), a1p(value = 'a1p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ " required_aspect_providers = [a2p],",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " result = 'aspect a3 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " if a2p in target:",
+ " result += ' and sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' and cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a3_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a3_result(value = complete_result), a2p(value = 'a2p_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a2p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a3 = getConfiguredAspect(configuredAspects, "a3");
+ assertThat(a3).isNotNull();
+ StarlarkProvider.Key a3Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a3_result");
+ StructImpl a3ResultProvider = (StructImpl) a3.get(a3Result);
+ assertThat((Sequence<?>) a3ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a3 on target //test:dep_target cannot see a1p and cannot see a2p",
+ "aspect a3 on target //test:main cannot see a1p and cannot see a2p");
+
+ ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
+ assertThat(a2).isNotNull();
+ StarlarkProvider.Key a2Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a2_result");
+ StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
+ assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a2 on target //test:dep_target cannot see a1p and sees a2p = a2p_val",
+ "aspect a2 on target //test:main cannot see a1p and sees a2p = a2p_val");
+
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a1p = a1p_val and cannot see a2p",
+ "aspect a1 on target //test:main sees a1p = a1p_val and cannot see a2p");
+ }
+
+ /**
+ * --aspects = a3, a2, a1: aspect a1 requires provider a1p, aspect a2 and aspect a3 provides a1p.
+ * This should fail because provider a1p is provided twice.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_requiredProviderProvidedTwiceFailed() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a1p(value = 'a1p_a2_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " return [a1p(value = 'a1p_a3_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+ reporter.removeHandler(failFastHandler);
+
+ // The call to `update` does not throw an exception when "--keep_going" is passed in the
+ // WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
+ if (keepGoing()) {
+ AnalysisResult result =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+ assertThat(result.hasError()).isTrue();
+ } else {
+ assertThrows(
+ ViewCreationFailedException.class,
+ () ->
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main"));
+ }
+ assertContainsEvent("ERROR /workspace/test/BUILD:2:12: Provider a1p provided twice");
+ }
+
+ /**
+ * --aspects = a3, a1, a2: aspect a1 requires provider a1p, aspect a2 and aspect a3 provide a1p.
+ * a1 should see the value provided by a3 because a3 is listed before a1.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_requiredProviderProvidedTwicePassed() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a1p(value = 'a1p_a2_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " return [a1p(value = 'a1p_a3_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a1", "test/defs.bzl%a2"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a1p = a1p_a3_val",
+ "aspect a1 on target //test:main sees a1p = a1p_a3_val");
+ }
+
+ @Test
+ public void testTopLevelAspectOnAspect_requiredProviderNotProvided() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a2p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target cannot see a1p",
+ "aspect a1 on target //test:main cannot see a1p");
+ }
+
+ /**
+ * --aspects = a1, a2: aspect a1 requires provider a1p, aspect a2 provides a1p but it was listed
+ * after a1 so aspect a1 cannot see a1p value.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_requiredProviderProvidedAfterTheAspect() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a1p(value = 'a1p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2"), "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target cannot see a1p",
+ "aspect a1 on target //test:main cannot see a1p");
+ }
+
+ /**
+ * --aspects = a2, a1: aspect a1 requires provider a1p, aspect a2 provides a1p. But aspect a2
+ * propagates along different attr_aspects from a1 so a1 cannot get a1p on all dependency targets.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_differentAttrAspects() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result += ctx.rule.attr.dep[a1_result].value",
+ " if ctx.rule.attr.extra_dep:",
+ " complete_result += ctx.rule.attr.extra_dep[a1_result].value",
+ " complete_result += [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep', 'extra_dep'],",
+ " required_aspect_providers = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a1p(value = 'a1p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a1p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " 'extra_dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ " extra_dep = ':extra_dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'extra_dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a1p = a1p_val",
+ "aspect a1 on target //test:extra_dep_target cannot see a1p",
+ "aspect a1 on target //test:main sees a1p = a1p_val");
+ }
+
+ /**
+ * --aspects = a2, a1: aspect a1 requires provider a1p, aspect a2 provides a1p. But aspect a2
+ * propagates along different required_providers from a1 so a1 cannot get a1p on all dependency
+ * targets.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_differentRequiredRuleProviders() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a1_result = provider()",
+ "rule_prov_a = provider()",
+ "rule_prov_b = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " complete_result = []",
+ " if hasattr(ctx.rule.attr, 'deps'):",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result += dep[a1_result].value",
+ " complete_result += [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [a1p],",
+ " required_providers = [[rule_prov_a], [rule_prov_b]],",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a1p(value = 'a1p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a1p],",
+ " required_providers = [rule_prov_a],",
+ ")",
+ "",
+ "def _main_rule_impl(ctx):",
+ " return [rule_prov_a(), rule_prov_b()]",
+ "main_rule = rule(",
+ " implementation = _main_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")",
+ "",
+ "def _rule_with_prov_a_impl(ctx):",
+ " return [rule_prov_a()]",
+ "rule_with_prov_a = rule(",
+ " implementation = _rule_with_prov_a_impl,",
+ " provides = [rule_prov_a]",
+ ")",
+ "",
+ "def _rule_with_prov_b_impl(ctx):",
+ " return [rule_prov_b()]",
+ "rule_with_prov_b = rule(",
+ " implementation = _rule_with_prov_b_impl,",
+ " provides = [rule_prov_b]",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'main_rule', 'rule_with_prov_a', 'rule_with_prov_b')",
+ "main_rule(",
+ " name = 'main',",
+ " deps = [':target_with_prov_a', ':target_with_prov_b'],",
+ ")",
+ "rule_with_prov_a(",
+ " name = 'target_with_prov_a',",
+ ")",
+ "rule_with_prov_b(",
+ " name = 'target_with_prov_b',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:target_with_prov_a sees a1p = a1p_val",
+ "aspect a1 on target //test:target_with_prov_b cannot see a1p",
+ "aspect a1 on target //test:main sees a1p = a1p_val");
+ }
+
+ /**
+ * --aspects = a3, a2, a1: both aspects a1 and a2 require provider a3p, aspect a3 provides a3p. a1
+ * and a2 should be able to read a3p.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_providerRequiredByMultipleAspects() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a3p = provider()",
+ "a1_result = provider()",
+ "a2_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a3p in target:",
+ " result += ' sees a3p = {}'.format(target[a3p].value)",
+ " else:",
+ " result += ' cannot see a3p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a1_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a3p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " result = 'aspect a2 on target {}'.format(target.label)",
+ " if a3p in target:",
+ " result += ' sees a3p = {}'.format(target[a3p].value)",
+ " else:",
+ " result += ' cannot see a3p'",
+ " complete_result = []",
+ " if ctx.rule.attr.dep:",
+ " complete_result = ctx.rule.attr.dep[a2_result].value + [result]",
+ " else:",
+ " complete_result = [result]",
+ " return [a2_result(value = complete_result)]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['dep'],",
+ " required_aspect_providers = [a3p]",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " return [a3p(value = 'a3p_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['dep'],",
+ " provides = [a3p],",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " dep = ':dep_target',",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
+ assertThat(a2).isNotNull();
+ StarlarkProvider.Key a2Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a2_result");
+ StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
+ assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a2 on target //test:dep_target sees a3p = a3p_val",
+ "aspect a2 on target //test:main sees a3p = a3p_val");
+
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a3p = a3p_val",
+ "aspect a1 on target //test:main sees a3p = a3p_val");
+ }
+
+ /**
+ * --aspects = a1, a2, a3: aspect a3 requires a1p and a2p, a1 provides a1p and a2 provides a2p.
+ *
+ * <p>top level target (main) has two dependencies t1 and t2. Aspects a1 and a3 can propagate to
+ * t1 and aspects a2 and a3 can propagate to t2. Both t1 and t2 have t0 as dependency, aspect a3
+ * will run twice on t0 once with aspects path (a1, a3) and the other with (a2, a3).
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_diamondCase() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a1p = provider()",
+ "a2p = provider()",
+ "a3_result = provider()",
+ "",
+ "r1p = provider()",
+ "r2p = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " return [a1p(value = 'a1p_val')]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_providers = [r1p],",
+ " provides = [a1p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " required_providers = [r2p],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " result = 'aspect a3 on target {}'.format(target.label)",
+ " if a1p in target:",
+ " result += ' sees a1p = {}'.format(target[a1p].value)",
+ " else:",
+ " result += ' cannot see a1p'",
+ " if a2p in target:",
+ " result += ' and sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' and cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a3_result].value)",
+ " complete_result.append(result)",
+ " return [a3_result(value = complete_result)]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [[a1p], [a2p]],",
+ ")",
+ "",
+ "def _r0_impl(ctx):",
+ " return [r1p(), r2p()]",
+ "r0 = rule(",
+ " implementation = _r0_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ " provides = [r1p, r2p]",
+ ")",
+ "def _r1_impl(ctx):",
+ " return [r1p()]",
+ "r1 = rule(",
+ " implementation = _r1_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ " provides = [r1p]",
+ ")",
+ "def _r2_impl(ctx):",
+ " return [r2p()]",
+ "r2 = rule(",
+ " implementation = _r2_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ " provides = [r2p]",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'r0', 'r1', 'r2')",
+ "r0(",
+ " name = 'main',",
+ " deps = [':t1', ':t2'],",
+ ")",
+ "r1(",
+ " name = 't1',",
+ " deps = [':t0'],",
+ ")",
+ "r2(",
+ " name = 't2',",
+ " deps = [':t0'],",
+ ")",
+ "r0(",
+ " name = 't0',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a3"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a3 = getConfiguredAspect(configuredAspects, "a3");
+ assertThat(a3).isNotNull();
+ StarlarkProvider.Key a3Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a3_result");
+ StructImpl a3ResultProvider = (StructImpl) a3.get(a3Result);
+ assertThat((Sequence<?>) a3ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a3 on target //test:t0 sees a1p = a1p_val and cannot see a2p",
+ "aspect a3 on target //test:t0 cannot see a1p and sees a2p = a2p_val",
+ "aspect a3 on target //test:t1 sees a1p = a1p_val and cannot see a2p",
+ "aspect a3 on target //test:t2 cannot see a1p and sees a2p = a2p_val",
+ "aspect a3 on target //test:main sees a1p = a1p_val and sees a2p = a2p_val");
+ }
+
+ /**
+ * --aspects = a1, a2, a1: aspect a1 requires a2p, a2 provides a2p.
+ *
+ * <p>top level aspects list is deduplicated by default and only the first occurrence of a1 will
+ * be there so a1 won't get the value of a2p.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_duplicateAspectsIgnored() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [a2p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " deps = [':dep_target'],",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:main cannot see a2p",
+ "aspect a1 on target //test:dep_target cannot see a2p");
+ }
+
+ @Test
+ public void testTopLevelAspectOnAspect_duplicateAspectsNotAllowed() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [a2p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " deps = [':dep_target'],",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+ useConfiguration("--incompatible_ignore_duplicate_top_level_aspects=false");
+ reporter.removeHandler(failFastHandler);
+
+ // The call to `update` does not throw an exception when "--keep_going" is passed in the
+ // WithKeepGoing test suite. Otherwise, it throws ViewCreationFailedException.
+ if (keepGoing()) {
+ AnalysisResult result =
+ update(
+ ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+ assertThat(result.hasError()).isTrue();
+ } else {
+ assertThrows(
+ ViewCreationFailedException.class,
+ () ->
+ update(
+ ImmutableList.of("test/defs.bzl%a1", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main"));
+ }
+ assertContainsEvent("aspect //test:defs.bzl%a1 added more than once");
+ }
+
+ /**
+ * --aspects = a1 requires provider a2p provided by aspect a2. a1 is applied on top level target
+ * `main` whose rule propagates aspect a2 to its `deps`. So a1 on `main` cannot see a2p but it can
+ * see a2p on `main` deps.
+ */
+ @Test
+ public void testTopLevelAspectOnAspect_requiredAspectProviderOnlyAvailableOnDep()
+ throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [a2p]",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(aspects=[a2]),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " deps = [':dep_target'],",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult = update(ImmutableList.of("test/defs.bzl%a1"), "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a2p = a2p_val",
+ "aspect a1 on target //test:main cannot see a2p");
+ }
+
+ @Test
+ public void testTopLevelAspectOnAspect_multipleTopLevelTargets() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [a2p],",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 't1',",
+ ")",
+ "simple_rule(",
+ " name = 't2',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%a2", "test/defs.bzl%a1"), "//test:t2", "//test:t1");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1Ont1 = getConfiguredAspect(configuredAspects, "a1", "t1");
+ assertThat(a1Ont1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1Ont1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly("aspect a1 on target //test:t1 sees a2p = a2p_val");
+
+ ConfiguredAspect a1Ont2 = getConfiguredAspect(configuredAspects, "a1", "t2");
+ assertThat(a1Ont2).isNotNull();
+ a1ResultProvider = (StructImpl) a1Ont2.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly("aspect a1 on target //test:t2 sees a2p = a2p_val");
+ }
+
+ @Test
+ public void testTopLevelAspectOnAspect_multipleRequiredProviders() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a3p = provider()",
+ "a1_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " if a3p in target:",
+ " result += ' and sees a3p = {}'.format(target[a3p].value)",
+ " else:",
+ " result += ' and cannot see a3p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [[a2p], [a3p]],",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " return [a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p]",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " return [a3p(value = 'a3p_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a3p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " deps = [':dep_target'],",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a2p = a2p_val and sees a3p = a3p_val",
+ "aspect a1 on target //test:main sees a2p = a2p_val and sees a3p = a3p_val");
+ }
+
+ @Test
+ public void testTopLevelAspectOnAspect_multipleRequiredProviders2() throws Exception {
+ scratch.file(
+ "test/defs.bzl",
+ "a2p = provider()",
+ "a3p = provider()",
+ "a1_result = provider()",
+ "a2_result = provider()",
+ "",
+ "def _a1_impl(target, ctx):",
+ " result = 'aspect a1 on target {}'.format(target.label)",
+ " if a2p in target:",
+ " result += ' sees a2p = {}'.format(target[a2p].value)",
+ " else:",
+ " result += ' cannot see a2p'",
+ " if a3p in target:",
+ " result += ' and sees a3p = {}'.format(target[a3p].value)",
+ " else:",
+ " result += ' and cannot see a3p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a1_result].value)",
+ " complete_result.append(result)",
+ " return [a1_result(value = complete_result)]",
+ "a1 = aspect(",
+ " implementation = _a1_impl,",
+ " attr_aspects = ['deps'],",
+ " required_aspect_providers = [[a2p], [a3p]],",
+ ")",
+ "",
+ "def _a2_impl(target, ctx):",
+ " result = 'aspect a2 on target {}'.format(target.label)",
+ " if a3p in target:",
+ " result += ' sees a3p = {}'.format(target[a3p].value)",
+ " else:",
+ " result += ' cannot see a3p'",
+ " complete_result = []",
+ " if ctx.rule.attr.deps:",
+ " for dep in ctx.rule.attr.deps:",
+ " complete_result.extend(dep[a2_result].value)",
+ " complete_result.append(result)",
+ " return [a2_result(value = complete_result), a2p(value = 'a2p_val')]",
+ "a2 = aspect(",
+ " implementation = _a2_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a2p],",
+ " required_aspect_providers = [a3p]",
+ ")",
+ "",
+ "def _a3_impl(target, ctx):",
+ " return [a3p(value = 'a3p_val')]",
+ "a3 = aspect(",
+ " implementation = _a3_impl,",
+ " attr_aspects = ['deps'],",
+ " provides = [a3p]",
+ ")",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " pass",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'deps': attr.label_list(),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'simple_rule')",
+ "simple_rule(",
+ " name = 'main',",
+ " deps = [':dep_target'],",
+ ")",
+ "simple_rule(",
+ " name = 'dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%a3", "test/defs.bzl%a2", "test/defs.bzl%a1"),
+ "//test:main");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect a1 = getConfiguredAspect(configuredAspects, "a1");
+ assertThat(a1).isNotNull();
+ StarlarkProvider.Key a1Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a1_result");
+ StructImpl a1ResultProvider = (StructImpl) a1.get(a1Result);
+ assertThat((Sequence<?>) a1ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a1 on target //test:dep_target sees a2p = a2p_val and sees a3p = a3p_val",
+ "aspect a1 on target //test:main sees a2p = a2p_val and sees a3p = a3p_val");
+
+ ConfiguredAspect a2 = getConfiguredAspect(configuredAspects, "a2");
+ assertThat(a2).isNotNull();
+ StarlarkProvider.Key a2Result =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "a2_result");
+ StructImpl a2ResultProvider = (StructImpl) a2.get(a2Result);
+ assertThat((Sequence<?>) a2ResultProvider.getValue("value"))
+ .containsExactly(
+ "aspect a2 on target //test:dep_target sees a3p = a3p_val",
+ "aspect a2 on target //test:main sees a3p = a3p_val");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_stackOfRequiredAspects() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl, requires = [aspect_c])",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ assertThat(configuredAspects).hasSize(3);
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_aspectRequiredByMultipleAspects() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl, requires = [aspect_c])",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_c])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ assertThat(configuredAspects).hasSize(3);
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_aspectRequiredByMultipleAspects2() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_d = aspect(implementation = _impl)",
+ "aspect_c = aspect(implementation = _impl, requires = [aspect_d])",
+ "aspect_b = aspect(implementation = _impl, requires = [aspect_d])",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_b, aspect_c])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ assertThat(configuredAspects).hasSize(4);
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_c")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_d")).isNotNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requireExistingAspect_passed() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_b", "test/defs.bzl%aspect_a"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ assertThat(configuredAspects).hasSize(2);
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_a")).isNotNull();
+ assertThat(getConfiguredAspect(configuredAspects, "aspect_b")).isNotNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requireExistingAspect_failed() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AssertionError expected =
+ assertThrows(
+ AssertionError.class,
+ () ->
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains(
+ "aspect //test:defs.bzl%aspect_b was added before as a required"
+ + " aspect of aspect //test:defs.bzl%aspect_a");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritDefaultValues() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl, requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ assertThat(configuredAspects).hasSize(2);
+
+ // aspect_b inherits the required providers and propagation attributes from aspect_a
+ AspectKey aspectB = getAspectKey(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ assertThat(aspectB.getInheritedRequiredProviders())
+ .isEqualTo(RequiredProviders.acceptAnyBuilder().build());
+ assertThat(aspectB.getInheritedAttributeAspects()).isEmpty();
+
+ AspectKey aspectA = getAspectKey(configuredAspects, "aspect_a");
+ assertThat(aspectA).isNotNull();
+ assertThat(aspectA.getInheritedRequiredProviders()).isNull();
+ assertThat(aspectA.getInheritedAttributeAspects()).isEmpty();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAttrAspectsFromSingleAspect()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl,",
+ " attr_aspects = ['deps'],",
+ " requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectB = getAspectKey(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ assertThat(aspectB.getInheritedAttributeAspects()).containsExactly("deps");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritRequiredProvidersFromSingleAspect()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl,",
+ " required_providers=[['java', cc], ['python']],",
+ " requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectB = getAspectKey(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ assertThat(aspectB.getInheritedRequiredProviders().getDescription())
+ .isEqualTo("['java', 'cc'] or 'python'");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresExistingAspect_inheritAttrAspectsFromSingleAspect()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl,",
+ " attr_aspects = ['deps'],",
+ " requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_b", "test/defs.bzl%aspect_a"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectB = getAspectKey(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ assertThat(aspectB.getInheritedAttributeAspects()).containsExactly("deps");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresExistingAspect_inheritRequiredProvidersFromSingleAspect()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl)",
+ "aspect_a = aspect(implementation = _impl,",
+ " required_providers=[['java', cc], ['python']],",
+ " requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_b", "test/defs.bzl%aspect_a"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectB = getAspectKey(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ assertThat(aspectB.getInheritedRequiredProviders().getDescription())
+ .isEqualTo("['java', 'cc'] or 'python'");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAttrAspectsFromMultipleAspects()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['extra_deps'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['deps'])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedAttributeAspects()).containsExactly("deps", "extra_deps");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritRequiredProvidersFromMultipleAspects()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers=['go'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers=[['java', cc], ['python']])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedRequiredProviders().getDescription())
+ .isEqualTo("['java', 'cc'] or 'python' or 'go'");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAllAttrAspects() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['extra_deps'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['*'])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ // propagate along all attributes '*'
+ assertThat(aspectC.getInheritedAttributeAspects()).isNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAllRequiredProviders() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers = [])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers=[['java', cc], ['python']])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_a", "test/defs.bzl%aspect_b"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedRequiredProviders())
+ .isEqualTo(RequiredProviders.acceptAnyBuilder().build());
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAttrAspectsFromAspectsStack()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['extra_deps'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_b],",
+ " attr_aspects = ['deps'])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedAttributeAspects()).containsExactly("deps", "extra_deps");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritRequiredProvidersFromAspectsStack()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers=['go'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_b],",
+ " required_providers=[['java', cc], ['python']])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedRequiredProviders().getDescription())
+ .isEqualTo("['java', 'cc'] or 'python' or 'go'");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiringAspect_inheritAllAttrAspectsFromAspectsStack()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " attr_aspects = ['*'])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_b],",
+ " attr_aspects = ['deps'])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ // propagate along all attributes '*'
+ assertThat(aspectC.getInheritedAttributeAspects()).isNull();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritAllRequiredProvidersFromAspectsStack()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "cc = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_c = aspect(implementation = _impl)",
+ "aspect_b = aspect(implementation = _impl,",
+ " requires = [aspect_c],",
+ " required_providers=[cc])",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_b],",
+ " required_providers=[])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey aspectC = getAspectKey(configuredAspects, "aspect_c");
+ assertThat(aspectC).isNotNull();
+ assertThat(aspectC.getInheritedRequiredProviders())
+ .isEqualTo(RequiredProviders.acceptAnyBuilder().build());
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requiredNativeAspect_inheritsAttrAspects()
+ throws Exception {
+ exposeNativeAspectToStarlark();
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [starlark_native_aspect],",
+ " attr_aspects = ['deps'])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey nativeAspect = getAspectKey(configuredAspects, "StarlarkNativeAspectWithProvider");
+ assertThat(nativeAspect).isNotNull();
+ assertThat(nativeAspect.getInheritedAttributeAspects()).containsExactly("deps");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requiredNativeAspect_inheritsRequiredProviders()
+ throws Exception {
+ exposeNativeAspectToStarlark();
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "rule_prov = provider()",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [starlark_native_aspect],",
+ " required_providers = [['java', rule_prov], ['python']])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ AspectKey nativeAspect = getAspectKey(configuredAspects, "StarlarkNativeAspectWithProvider");
+ assertThat(nativeAspect).isNotNull();
+ assertThat(nativeAspect.getInheritedRequiredProviders().getDescription())
+ .isEqualTo("['java', 'rule_prov'] or 'python'");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requiredNativeAspect_parametersNotAllowed()
+ throws Exception {
+ exposeNativeAspectToStarlark();
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [parametrized_native_aspect])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AssertionError expected =
+ assertThrows(
+ AssertionError.class,
+ () -> update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains(
+ "Cannot use parameterized aspect ParametrizedAspectWithProvider at the top level.");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_requiredStarlarkAspect_parametersNotAllowed()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "def _impl(target, ctx):",
+ " return []",
+ "aspect_b = aspect(implementation = _impl,",
+ " attrs = {'attr': attr.string(values=['val'])})",
+ "aspect_a = aspect(implementation = _impl,",
+ " requires = [aspect_b])");
+ scratch.file("test/BUILD", "cc_binary(name = 'main_target')");
+
+ AssertionError expected =
+ assertThrows(
+ AssertionError.class,
+ () -> update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target"));
+ assertThat(expected)
+ .hasMessageThat()
+ .contains("Cannot use parameterized aspect //test:defs.bzl%aspect_b at the top level.");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_ruleAttributes() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "RequiredAspectProv = provider()",
+ "BaseAspectProv = provider()",
+ "",
+ "def _required_aspect_impl(target, ctx):",
+ " p_val = ['In required_aspect, p = {} on target {}'",
+ " .format(ctx.rule.attr.p, target.label)]",
+ " if ctx.rule.attr.dep:",
+ " p_val += ctx.rule.attr.dep[RequiredAspectProv].p_val",
+ " return [RequiredAspectProv(p_val = p_val)]",
+ "required_aspect = aspect(",
+ " implementation = _required_aspect_impl,",
+ " attr_aspects = ['dep'],",
+ ")",
+ "",
+ "def _base_aspect_impl(target, ctx):",
+ " p_val = ['In base_aspect, p = {} on target {}'.format(ctx.rule.attr.p, target.label)]",
+ " if ctx.rule.attr.dep:",
+ " p_val += ctx.rule.attr.dep[BaseAspectProv].p_val",
+ " return [BaseAspectProv(p_val = p_val)]",
+ "base_aspect = aspect(",
+ " implementation = _base_aspect_impl,",
+ " attr_aspects = ['dep'],",
+ " requires = [required_aspect],",
+ ")",
+ "",
+ "def _rule_impl(ctx):",
+ " pass",
+ "",
+ "my_rule = rule(",
+ " implementation = _rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " 'p' : attr.string(values = ['main_val', 'dep_val']),",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ " dep = ':dep_target',",
+ " p = 'main_val',",
+ ")",
+ "my_rule(",
+ " name = 'dep_target',",
+ " p = 'dep_val',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
+
+ // Both base_aspect and required_aspect can see the attributes of the target they run on
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect requiredAspect = getConfiguredAspect(configuredAspects, "required_aspect");
+ assertThat(requiredAspect).isNotNull();
+ StarlarkProvider.Key requiredAspectProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "RequiredAspectProv");
+ StructImpl requiredAspectProvider = (StructImpl) requiredAspect.get(requiredAspectProv);
+ assertThat((Sequence<?>) requiredAspectProvider.getValue("p_val"))
+ .containsExactly(
+ "In required_aspect, p = dep_val on target //test:dep_target",
+ "In required_aspect, p = main_val on target //test:main_target");
+
+ ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
+ assertThat(baseAspect).isNotNull();
+ StarlarkProvider.Key baseAspectProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "BaseAspectProv");
+ StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
+ assertThat((Sequence<?>) baseAspectProvider.getValue("p_val"))
+ .containsExactly(
+ "In base_aspect, p = dep_val on target //test:dep_target",
+ "In base_aspect, p = main_val on target //test:main_target");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritPropagationAttributes() throws Exception {
+ // base_aspect propagates over base_dep attribute and requires first_required_aspect which
+ // propagates over first_dep attribute and requires second_required_aspect which propagates over
+ // second_dep attribute
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "BaseAspectProv = provider()",
+ "FirstRequiredAspectProv = provider()",
+ "SecondRequiredAspectProv = provider()",
+ "",
+ "def _second_required_aspect_impl(target, ctx):",
+ " result = []",
+ " if getattr(ctx.rule.attr, 'second_dep'):",
+ " result += getattr(ctx.rule.attr, 'second_dep')[SecondRequiredAspectProv].result",
+ " result += ['second_required_aspect run on target {}'.format(target.label)]",
+ " return [SecondRequiredAspectProv(result = result)]",
+ "second_required_aspect = aspect(",
+ " implementation = _second_required_aspect_impl,",
+ " attr_aspects = ['second_dep'],",
+ ")",
+ "",
+ "def _first_required_aspect_impl(target, ctx):",
+ " result = []",
+ " result += target[SecondRequiredAspectProv].result",
+ " if getattr(ctx.rule.attr, 'first_dep'):",
+ " result += getattr(ctx.rule.attr, 'first_dep')[FirstRequiredAspectProv].result",
+ " result += ['first_required_aspect run on target {}'.format(target.label)]",
+ " return [FirstRequiredAspectProv(result = result)]",
+ "first_required_aspect = aspect(",
+ " implementation = _first_required_aspect_impl,",
+ " attr_aspects = ['first_dep'],",
+ " requires = [second_required_aspect],",
+ ")",
+ "",
+ "def _base_aspect_impl(target, ctx):",
+ " result = []",
+ " result += target[FirstRequiredAspectProv].result",
+ " if getattr(ctx.rule.attr, 'base_dep'):",
+ " result += getattr(ctx.rule.attr, 'base_dep')[BaseAspectProv].result",
+ " result += ['base_aspect run on target {}'.format(target.label)]",
+ " return [BaseAspectProv(result = result)]",
+ "base_aspect = aspect(",
+ " implementation = _base_aspect_impl,",
+ " attr_aspects = ['base_dep'],",
+ " requires = [first_required_aspect],",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " pass",
+ "",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ " attrs = {",
+ " 'base_dep': attr.label(),",
+ " 'first_dep': attr.label(),",
+ " 'second_dep': attr.label()",
+ " },",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ " base_dep = ':base_dep_target',",
+ " first_dep = ':first_dep_target',",
+ " second_dep = ':second_dep_target',",
+ ")",
+ "my_rule(",
+ " name = 'base_dep_target',",
+ ")",
+ "my_rule(",
+ " name = 'first_dep_target',",
+ ")",
+ "my_rule(",
+ " name = 'second_dep_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
+
+ // base_aspect should propagate only along its attr_aspects: 'base_dep'
+ // first_required_aspect should propagate along 'base_dep' and 'first_dep'
+ // second_required_aspect should propagate along 'base_dep', 'first_dep' and 'second_dep'
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
+ assertThat(baseAspect).isNotNull();
+ StarlarkProvider.Key baseAspectProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "BaseAspectProv");
+ StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
+ assertThat((Sequence<?>) baseAspectProvider.getValue("result"))
+ .containsExactly(
+ "second_required_aspect run on target //test:second_dep_target",
+ "second_required_aspect run on target //test:main_target",
+ "second_required_aspect run on target //test:first_dep_target",
+ "second_required_aspect run on target //test:base_dep_target",
+ "first_required_aspect run on target //test:first_dep_target",
+ "first_required_aspect run on target //test:main_target",
+ "first_required_aspect run on target //test:base_dep_target",
+ "base_aspect run on target //test:base_dep_target",
+ "base_aspect run on target //test:main_target");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inheritRequiredProviders() throws Exception {
+ // aspect_a requires provider Prov_A and requires aspect_b which requires
+ // provider Prov_B and requires aspect_c which requires provider Prov_C
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "Prov_A = provider()",
+ "Prov_B = provider()",
+ "Prov_C = provider()",
+ "",
+ "CollectorProv = provider()",
+ "",
+ "def _aspect_c_impl(target, ctx):",
+ " collector_result = ['aspect_c run on target {} and value of Prov_C = {}'",
+ " .format(target.label, target[Prov_C].val)]",
+ " return [CollectorProv(result = collector_result)]",
+ "aspect_c = aspect(",
+ " implementation = _aspect_c_impl,",
+ " required_providers = [Prov_C],",
+ ")",
+ "",
+ "def _aspect_b_impl(target, ctx):",
+ " collector_result = []",
+ " collector_result += ctx.rule.attr.dep[CollectorProv].result",
+ " collector_result += ['aspect_b run on target {} and value of Prov_B = {}'",
+ " .format(target.label, target[Prov_B].val)]",
+ " return [CollectorProv(result = collector_result)]",
+ "aspect_b = aspect(",
+ " implementation = _aspect_b_impl,",
+ " required_providers = [Prov_B],",
+ " requires = [aspect_c],",
+ ")",
+ "",
+ "def _aspect_a_impl(target, ctx):",
+ " collector_result = []",
+ " collector_result += ctx.rule.attr.dep[CollectorProv].result",
+ " collector_result += ['aspect_a run on target {} and value of Prov_A = {}'",
+ " .format(target.label, target[Prov_A].val)]",
+ " return [CollectorProv(result = collector_result)]",
+ "aspect_a = aspect(",
+ " implementation = _aspect_a_impl,",
+ " attr_aspects = ['dep'],",
+ " required_providers = [Prov_A],",
+ " requires = [aspect_b],",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " return [Prov_A(val='main_val_a')]",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ " provides = [Prov_A]",
+ ")",
+ "",
+ "def _rule_with_prov_a_impl(ctx):",
+ " return [Prov_A(val='val_a')]",
+ "rule_with_prov_a = rule(",
+ " implementation = _rule_with_prov_a_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ " provides = [Prov_A]",
+ ")",
+ "",
+ "def _rule_with_prov_b_impl(ctx):",
+ " return [Prov_B(val = 'val_b')]",
+ "rule_with_prov_b = rule(",
+ " implementation = _rule_with_prov_b_impl,",
+ " attrs = {",
+ " 'dep': attr.label(),",
+ " },",
+ " provides = [Prov_B]",
+ ")",
+ "",
+ "def _rule_with_prov_c_impl(ctx):",
+ " return [Prov_C(val = 'val_c')]",
+ "rule_with_prov_c = rule(",
+ " implementation = _rule_with_prov_c_impl,",
+ " provides = [Prov_C]",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule', 'rule_with_prov_a', 'rule_with_prov_b',"
+ + " 'rule_with_prov_c')",
+ "my_rule(",
+ " name = 'main_target',",
+ " dep = ':target_with_prov_a',",
+ ")",
+ "rule_with_prov_a(",
+ " name = 'target_with_prov_a',",
+ " dep = ':target_with_prov_b'",
+ ")",
+ "rule_with_prov_b(",
+ " name = 'target_with_prov_b',",
+ " dep = ':target_with_prov_c'",
+ ")",
+ "rule_with_prov_c(",
+ " name = 'target_with_prov_c'",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%aspect_a"), "//test:main_target");
+
+ // aspect_a should run on main_target and target_with_prov_a
+ // aspect_b can reach target_with_prov_b because it inherits the required_providers of aspect_a
+ // aspect_c can reach target_with_prov_c because it inherits the required_providers of aspect_a
+ // and aspect_b
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
+ assertThat(aspectA).isNotNull();
+ StarlarkProvider.Key collectorProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "CollectorProv");
+ StructImpl collectorProvider = (StructImpl) aspectA.get(collectorProv);
+ assertThat((Sequence<?>) collectorProvider.getValue("result"))
+ .containsExactly(
+ "aspect_c run on target //test:target_with_prov_c and value of Prov_C = val_c",
+ "aspect_b run on target //test:target_with_prov_b and value of Prov_B = val_b",
+ "aspect_a run on target //test:target_with_prov_a and value of Prov_A = val_a",
+ "aspect_a run on target //test:main_target and value of Prov_A = main_val_a")
+ .inOrder();
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inspectRequiredAspectActions() throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "BaseAspectProvider = provider()",
+ "def _required_aspect_impl(target, ctx):",
+ " f = ctx.actions.declare_file('dummy.txt')",
+ " ctx.actions.run_shell(outputs = [f], command='echo xxx > $(location f)',",
+ " mnemonic='RequiredAspectAction')",
+ " return struct()",
+ "required_aspect = aspect(",
+ " implementation = _required_aspect_impl,",
+ ")",
+ "",
+ "def _base_aspect_impl(target, ctx):",
+ " required_aspect_action = None",
+ " for action in target.actions:",
+ " if action.mnemonic == 'RequiredAspectAction':",
+ " required_aspect_action = action",
+ " if required_aspect_action:",
+ " return [BaseAspectProvider(result = 'base_aspect can see required_aspect action')]",
+ " else:",
+ " return [BaseAspectProvider(result = 'base_aspect cannot see required_aspect action')]",
+ "base_aspect = aspect(",
+ " implementation = _base_aspect_impl,",
+ " attr_aspects = ['dep'],",
+ " requires = [required_aspect]",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " pass",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
+ assertThat(baseAspect).isNotNull();
+ StarlarkProvider.Key baseAspectProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "BaseAspectProvider");
+ StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
+ assertThat(baseAspectProvider.getValue("result"))
+ .isEqualTo("base_aspect can see required_aspect action");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_inspectRequiredAspectGeneratedFiles()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "BaseAspectProvider = provider()",
+ "def _required_aspect_impl(target, ctx):",
+ " file = ctx.actions.declare_file('required_aspect_file')",
+ " ctx.actions.write(file, 'data')",
+ " return [OutputGroupInfo(out = [file])]",
+ "required_aspect = aspect(",
+ " implementation = _required_aspect_impl,",
+ ")",
+ "",
+ "def _base_aspect_impl(target, ctx):",
+ " files = ['base_aspect can see file ' + f.path.split('/')[-1] ",
+ " for f in target[OutputGroupInfo].out.to_list()]",
+ " return [BaseAspectProvider(my_files = files)]",
+ "base_aspect = aspect(",
+ " implementation = _base_aspect_impl,",
+ " attr_aspects = ['dep'],",
+ " requires = [required_aspect]",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " pass",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(ImmutableList.of("test/defs.bzl%base_aspect"), "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect baseAspect = getConfiguredAspect(configuredAspects, "base_aspect");
+ assertThat(baseAspect).isNotNull();
+ StarlarkProvider.Key baseAspectProv =
+ new StarlarkProvider.Key(
+ Label.parseAbsolute("//test:defs.bzl", ImmutableMap.of()), "BaseAspectProvider");
+ StructImpl baseAspectProvider = (StructImpl) baseAspect.get(baseAspectProv);
+ assertThat((Sequence<?>) baseAspectProvider.getValue("my_files"))
+ .containsExactly("base_aspect can see file required_aspect_file");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_withRequiredAspectProvidersSatisfied()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "prov_a = provider()",
+ "prov_b = provider()",
+ "prov_b_forwarded = provider()",
+ "",
+ "def _aspect_b_impl(target, ctx):",
+ " result = 'aspect_b on target {} '.format(target.label)",
+ " if prov_b in target:",
+ " result += 'found prov_b = {}'.format(target[prov_b].val)",
+ " return struct(aspect_b_result = result,",
+ " providers = [prov_b_forwarded(val = target[prov_b].val)])",
+ " else:",
+ " result += 'cannot find prov_b'",
+ " return struct(aspect_b_result = result)",
+ "aspect_b = aspect(",
+ " implementation = _aspect_b_impl,",
+ " required_aspect_providers = [prov_b]",
+ ")",
+ "",
+ "def _aspect_a_impl(target, ctx):",
+ " result = 'aspect_a on target {} '.format(target.label)",
+ " if prov_a in target:",
+ " result += 'found prov_a = {}'.format(target[prov_a].val)",
+ " else:",
+ " result += 'cannot find prov_a'",
+ " if prov_b_forwarded in target:",
+ " result += ' and found prov_b = {}'.format(target[prov_b_forwarded].val)",
+ " else:",
+ " result += ' but cannot find prov_b'",
+ " return struct(aspect_a_result = result)",
+ "",
+ "aspect_a = aspect(",
+ " implementation = _aspect_a_impl,",
+ " required_aspect_providers = [prov_a],",
+ " attr_aspects = ['dep'],",
+ " requires = [aspect_b]",
+ ")",
+ "",
+ "def _aspect_with_prov_a_impl(target, ctx):",
+ " return [prov_a(val = 'a1')]",
+ "aspect_with_prov_a = aspect(",
+ " implementation = _aspect_with_prov_a_impl,",
+ " provides = [prov_a],",
+ " attr_aspects = ['dep'],",
+ ")",
+ "",
+ "def _aspect_with_prov_b_impl(target, ctx):",
+ " return [prov_b(val = 'b1')]",
+ "aspect_with_prov_b = aspect(",
+ " implementation = _aspect_with_prov_b_impl,",
+ " provides = [prov_b],",
+ " attr_aspects = ['dep'],",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " pass",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of(
+ "test/defs.bzl%aspect_with_prov_a",
+ "test/defs.bzl%aspect_with_prov_b", "test/defs.bzl%aspect_a"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
+ assertThat(aspectA).isNotNull();
+ String aspectAResult = (String) aspectA.get("aspect_a_result");
+ assertThat(aspectAResult)
+ .isEqualTo("aspect_a on target //test:main_target found prov_a = a1 and found prov_b = b1");
+
+ ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ String aspectBResult = (String) aspectB.get("aspect_b_result");
+ assertThat(aspectBResult).isEqualTo("aspect_b on target //test:main_target found prov_b = b1");
+ }
+
+ @Test
+ public void testTopLevelAspectRequiresAspect_withRequiredAspectProvidersNotFound()
+ throws Exception {
+ useConfiguration("--experimental_required_aspects");
+ scratch.file(
+ "test/defs.bzl",
+ "prov_a = provider()",
+ "prov_b = provider()",
+ "",
+ "def _aspect_b_impl(target, ctx):",
+ " result = 'aspect_b on target {} '.format(target.label)",
+ " if prov_b in target:",
+ " result += 'found prov_b = {}'.format(target[prov_b].val)",
+ " else:",
+ " result += 'cannot find prov_b'",
+ " return struct(aspect_b_result = result)",
+ "aspect_b = aspect(",
+ " implementation = _aspect_b_impl,",
+ " required_aspect_providers = [prov_b]",
+ ")",
+ "",
+ "def _aspect_a_impl(target, ctx):",
+ " result = 'aspect_a on target {} '.format(target.label)",
+ " if prov_a in target:",
+ " result += 'found prov_a = {}'.format(target[prov_a].val)",
+ " else:",
+ " result += 'cannot find prov_a'",
+ " return struct(aspect_a_result = result)",
+ "",
+ "aspect_a = aspect(",
+ " implementation = _aspect_a_impl,",
+ " required_aspect_providers = [prov_a],",
+ " attr_aspects = ['dep'],",
+ " requires = [aspect_b]",
+ ")",
+ "",
+ "def _aspect_with_prov_a_impl(target, ctx):",
+ " return [prov_a(val = 'a1')]",
+ "aspect_with_prov_a = aspect(",
+ " implementation = _aspect_with_prov_a_impl,",
+ " provides = [prov_a],",
+ " attr_aspects = ['dep'],",
+ ")",
+ "",
+ "def _my_rule_impl(ctx):",
+ " pass",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ ")");
+ scratch.file(
+ "test/BUILD",
+ "load('//test:defs.bzl', 'my_rule')",
+ "my_rule(",
+ " name = 'main_target',",
+ ")");
+
+ AnalysisResult analysisResult =
+ update(
+ ImmutableList.of("test/defs.bzl%aspect_with_prov_a", "test/defs.bzl%aspect_a"),
+ "//test:main_target");
+
+ Map<AspectKey, ConfiguredAspect> configuredAspects = analysisResult.getAspectsMap();
+ ConfiguredAspect aspectA = getConfiguredAspect(configuredAspects, "aspect_a");
+ assertThat(aspectA).isNotNull();
+ String aspectAResult = (String) aspectA.get("aspect_a_result");
+ assertThat(aspectAResult).isEqualTo("aspect_a on target //test:main_target found prov_a = a1");
+
+ ConfiguredAspect aspectB = getConfiguredAspect(configuredAspects, "aspect_b");
+ assertThat(aspectB).isNotNull();
+ String aspectBResult = (String) aspectB.get("aspect_b_result");
+ assertThat(aspectBResult).isEqualTo("aspect_b on target //test:main_target cannot find prov_b");
+ }
+
+ private ConfiguredAspect getConfiguredAspect(
+ Map<AspectKey, ConfiguredAspect> aspectsMap, String aspectName) {
+ for (Map.Entry<AspectKey, ConfiguredAspect> entry : aspectsMap.entrySet()) {
+ String aspectExportedName =
+ ((StarlarkAspectClass) entry.getKey().getAspectClass()).getExportedName();
+ if (aspectExportedName.equals(aspectName)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private ConfiguredAspect getConfiguredAspect(
+ Map<AspectKey, ConfiguredAspect> aspectsMap, String aspectName, String targetName) {
+ for (Map.Entry<AspectKey, ConfiguredAspect> entry : aspectsMap.entrySet()) {
+ String aspectExportedName =
+ ((StarlarkAspectClass) entry.getKey().getAspectClass()).getExportedName();
+ String target = entry.getKey().getLabel().getName();
+ if (aspectExportedName.equals(aspectName) && target.equals(targetName)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private AspectKey getAspectKey(Map<AspectKey, ConfiguredAspect> aspectsMap, String aspectName) {
+ for (Map.Entry<AspectKey, ConfiguredAspect> entry : aspectsMap.entrySet()) {
+ String aspectExportedName = entry.getKey().getAspectClass().getName();
+ if (aspectExportedName.contains(aspectName)) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ private void exposeNativeAspectToStarlark() throws Exception {
+ ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
+ TestRuleClassProvider.addStandardRules(builder);
+ builder.addStarlarkAccessibleTopLevels(
+ "starlark_native_aspect", TestAspects.STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
+ builder.addStarlarkAccessibleTopLevels(
+ "parametrized_native_aspect",
+ TestAspects.PARAMETRIZED_STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
+ builder.addNativeAspectClass(TestAspects.STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
+ builder.addNativeAspectClass(TestAspects.PARAMETRIZED_STARLARK_NATIVE_ASPECT_WITH_PROVIDER);
+ useRuleClassProvider(builder.build());
+ }
+
/** StarlarkAspectTest with "keep going" flag */
@RunWith(JUnit4.class)
public static final class WithKeepGoing extends StarlarkDefinedAspectsTest {