From 943c83aa58731c4f9561d79c458f254427a8f24c Mon Sep 17 00:00:00 2001 From: messa Date: Mon, 28 Jun 2021 07:54:28 -0700 Subject: [PATCH] Command line aspect-on-aspect This CL 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. PiperOrigin-RevId: 381862973 --- .../lib/analysis/AnalysisFailureEvent.java | 10 +- .../build/lib/analysis/BuildView.java | 59 +- .../lib/buildtool/BuildRequestOptions.java | 17 +- .../lib/packages/StarlarkAspectClass.java | 5 + .../build/lib/skyframe/AspectValueKey.java | 168 +-- .../google/devtools/build/lib/skyframe/BUILD | 1 + .../skyframe/LoadStarlarkAspectFunction.java | 169 +++ .../build/lib/skyframe/SkyFunctions.java | 2 + .../build/lib/skyframe/SkyframeBuildView.java | 51 +- .../build/lib/skyframe/SkyframeExecutor.java | 10 +- .../lib/skyframe/TargetCycleReporter.java | 4 +- .../ToplevelStarlarkAspectFunction.java | 194 ++- .../lib/skyframe/TargetCycleReporterTest.java | 11 +- .../starlark/StarlarkDefinedAspectsTest.java | 1285 +++++++++++++++++ 14 files changed, 1753 insertions(+), 233 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java 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 1e8a0ef29f5cf0..2a8445a5035157 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 rootCauses; @@ -48,12 +48,12 @@ public class AnalysisFailureEvent implements BuildEvent { public AnalysisFailureEvent( ActionLookupKey failedTarget, BuildEventId configuration, NestedSet 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( } public AnalysisFailureEvent( - AspectValueKey failedAspect, BuildEventId configuration, NestedSet rootCauses) { + AspectKey failedAspect, BuildEventId configuration, NestedSet rootCauses) { this.failedAspect = failedAspect; this.failedTarget = failedAspect.getBaseConfiguredTargetKey(); if (configuration != null) { 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 bec43a1036d6bf..f835927ef56fa0 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,7 @@ public AnalysisResult update( .map(TargetAndConfiguration::getConfiguredTargetKey) .collect(Collectors.toList()); - Multimap, BuildConfiguration> aspectConfigurations = - ArrayListMultimap.create(); - - List aspectKeys = new ArrayList<>(); + ImmutableList.Builder aspectClassesBuilder = ImmutableList.builder(); for (String aspect : aspects) { // Syntax: label%aspect int delimiterPosition = aspect.indexOf('%'); @@ -318,38 +314,14 @@ public AnalysisResult update( 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 +330,25 @@ public AnalysisResult update( } } + Multimap, BuildConfiguration> aspectConfigurations = + ArrayListMultimap.create(); + ImmutableList aspectClasses = aspectClassesBuilder.build(); + ImmutableList.Builder 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 target : aspectConfigurations.keys()) { eventBus.post( new AspectConfiguredEvent( @@ -382,7 +373,7 @@ public AnalysisResult update( 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 5e1c1ce01c556c..d39d604e15228c 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 @@ -297,11 +297,18 @@ public class BuildRequestOptions extends OptionsBase { 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 %, " - + "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 some_aspect specifies" + + " required aspect providers via required_aspect_providers," + + " some_aspect will run after every aspect that was mentioned before it" + + " in the aspects list and whose advertised providers satisfy" + + " some_aspect required aspect providers. some_aspect 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 %, 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 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 cb0678d990754b..9bde8305dbed4d 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 final boolean equals(Object o) { public final int hashCode() { return Objects.hash(getExtensionLabel(), getExportedName()); } + + @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 f6ecb9c43940d5..fddca3fa88e5e7 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 aspectKeyInterner = BlazeInterners.newWeakInterner(); - private static final Interner starlarkAspectKeyInterner = + private static final Interner 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. - * - *

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, @@ -66,6 +51,15 @@ public static AspectKey createAspectKey( aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration)); } + public static AspectKey createAspectKey( + AspectDescriptor aspectDescriptor, + ImmutableList baseKeys, + BuildConfigurationValue.Key aspectConfigurationKey, + ConfiguredTargetKey baseConfiguredTargetKey) { + return AspectKey.createAspectKey( + baseConfiguredTargetKey, baseKeys, aspectDescriptor, aspectConfigurationKey); + } + public static AspectKey createAspectKey( Label label, @Nullable BuildConfiguration baseConfiguration, @@ -78,28 +72,24 @@ public static AspectKey createAspectKey( aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration)); } - public static StarlarkAspectLoadingKey createStarlarkAspectKey( + public static TopLevelAspectsKey createTopLevelAspectsKey( + ImmutableList 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()); } // Specific subtypes of aspect keys. /** Represents an aspect applied to a particular target. */ @AutoCodec - public static final class AspectKey extends AspectValueKey { + public static final class AspectKey implements ActionLookupKey { private final ConfiguredTargetKey baseConfiguredTargetKey; private final ImmutableList baseKeys; @Nullable private final BuildConfigurationValue.Key aspectConfigurationKey; @@ -141,7 +131,12 @@ public SkyFunctionName functionName() { 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. + * + *

Only needed for reporting errors in BEP when the key's AspectValue fails evaluation. + */ public String getAspectName() { return aspectDescriptor.getDescription(); } @@ -178,11 +173,9 @@ ImmutableList getBaseKeys() { return baseKeys; } - @Override public String getDescription() { if (baseKeys.isEmpty()) { - return String.format("%s of %s", - aspectDescriptor.getAspectClass().getName(), getLabel()); + return String.format("%s of %s", aspectDescriptor.getAspectClass().getName(), getLabel()); } else { return String.format( "%s on top of %s", aspectDescriptor.getAspectClass().getName(), baseKeys); @@ -207,13 +200,11 @@ public String getDescription() { * 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; } @@ -280,70 +271,46 @@ AspectKey withLabel(Label label) { } } - /** 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 implements ActionLookupKey { + private final ImmutableList 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 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 topLevelAspectsClasses, Label targetLabel, - BuildConfigurationValue.Key aspectConfigurationKey, ConfiguredTargetKey baseConfiguredTargetKey, - Label starlarkFileLabel, - String starlarkValueName, int 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 getTopLevelAspectsClasses() { + return topLevelAspectsClasses; } @Override @@ -351,22 +318,12 @@ public Label getLabel() { return targetLabel; } - @Override - public String getDescription() { - // Starlark aspects are referred to on command line with % - return String.format("%s%%%s of %s", starlarkFileLabel, starlarkValueName, targetLabel); - } - - @Nullable - @Override - BuildConfigurationValue.Key getAspectConfigurationKey() { - return aspectConfigurationKey; + ConfiguredTargetKey getBaseConfiguredTargetKey() { + return baseConfiguredTargetKey; } - /** Returns the key for the base configured target for this aspect. */ - @Override - public ConfiguredTargetKey getBaseConfiguredTargetKey() { - return baseConfiguredTargetKey; + String getDescription() { + return topLevelAspectsClasses + " on " + getLabel(); } @Override @@ -379,35 +336,14 @@ public boolean equals(Object o) { if (o == this) { return true; } - if (!(o instanceof StarlarkAspectLoadingKey)) { + if (!(o instanceof TopLevelAspectsKey)) { return false; } - StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o; + 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(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 33006ba4d79d6c..52c40f2ecf1e34 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD @@ -37,6 +37,7 @@ java_library( "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/LoadStarlarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java new file mode 100644 index 00000000000000..4d81910f6e500e --- /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. + * + *

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 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 cb07796b366192..46443e4d3dc9a0 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,8 @@ public final class SkyFunctions { 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"); 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 7b4007d6054781..c6d559b4dfcd59 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 void clearAnalysisCache( public SkyframeAnalysisResult configureTargets( ExtendedEventHandler eventHandler, List ctKeys, - List aspectKeys, + ImmutableList topLevelAspectsKey, Supplier> configurationLookupSupplier, TopLevelArtifactContext topLevelArtifactContextForConflictPruning, EventBus eventBus, @@ -397,7 +399,7 @@ public SkyframeAnalysisResult configureTargets( skyframeExecutor.configureTargets( eventHandler, ctKeys, - aspectKeys, + topLevelAspectsKey, keepGoing, numThreads, cpuHeavySkyKeysThreadPoolSize); @@ -405,21 +407,33 @@ public SkyframeAnalysisResult configureTargets( enableAnalysis(false); } - Map aspects = Maps.newHashMapWithExpectedSize(aspectKeys.size()); + int numOfAspects = 0; + if (!topLevelAspectsKey.isEmpty()) { + numOfAspects = + topLevelAspectsKey.size() * topLevelAspectsKey.get(0).getTopLevelAspectsClasses().size(); + } + Map aspects = Maps.newHashMapWithExpectedSize(numOfAspects); Root singleSourceRoot = skyframeExecutor.getForcedSingleSourceRootIfNoExecrootSymlinkCreation(); NestedSetBuilder packages = singleSourceRoot == null ? NestedSetBuilder.stableOrder() : null; - for (AspectValueKey aspectKey : aspectKeys) { - AspectValue value = (AspectValue) result.get(aspectKey); + ImmutableList.Builder 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 aspectKeys = aspectKeysBuilder.build(); Collection cts = Lists.newArrayListWithCapacity(ctKeys.size()); for (ConfiguredTargetKey value : ctKeys) { @@ -561,7 +575,7 @@ public SkyframeAnalysisResult configureTargets( BuildConfigurationValue.Key configKey = ctKey instanceof ConfiguredTargetKey ? ((ConfiguredTargetKey) ctKey).getConfigurationKey() - : ((AspectValueKey) ctKey).getAspectConfigurationKey(); + : ((AspectKey) ctKey).getAspectConfigurationKey(); eventBus.post( new AnalysisFailureEvent( ctKey, @@ -597,13 +611,9 @@ public SkyframeAnalysisResult configureTargets( .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( @@ -744,15 +754,14 @@ static Pair processErrors( .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(); 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; @@ -773,7 +782,7 @@ static Pair processErrors( } 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 3ef335b887bdde..8219b726513c9e 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 @@ -159,6 +159,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; @@ -565,7 +566,8 @@ private ImmutableMap skyFunctions() { 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.ACTION_LOOKUP_CONFLICT_FINDING, new ActionLookupConflictFindingFunction()); map.put( SkyFunctions.TOP_LEVEL_ACTION_LOOKUP_CONFLICT_FINDING, @@ -2319,7 +2321,7 @@ protected abstract void invalidateFilesUnderPathForTestingImpl( EvaluationResult configureTargets( ExtendedEventHandler eventHandler, List values, - List aspectKeys, + ImmutableList aspectKeys, boolean keepGoing, int numThreads, int cpuHeavySkyKeysThreadPoolSize) @@ -3054,7 +3056,7 @@ protected ExecutionFinishedEvent.Builder createExecutionFinishedEventInternal() } final AnalysisTraversalResult getActionLookupValuesInBuild( - List topLevelCtKeys, List aspectKeys) + List topLevelCtKeys, ImmutableList aspectKeys) throws InterruptedException { AnalysisTraversalResult result = new AnalysisTraversalResult(); if (!isAnalysisIncremental()) { @@ -3074,7 +3076,7 @@ final AnalysisTraversalResult getActionLookupValuesInBuild( 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 cf8f8285f1bb0b..693bde758f4f64 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 @@ class TargetCycleReporter extends AbstractLabelCycleReporter { 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 @@ public String prettyPrint(SkyKey key) { 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 a858e35e351abd..d230637110caf6 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,37 @@ 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.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.analysis.AspectCollection; +import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException; +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.AspectParameters; +import com.google.devtools.build.lib.packages.NativeAspectClass; 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.devtools.build.lib.packages.StarlarkAspectClass; +import com.google.devtools.build.lib.packages.StarlarkDefinedAspect; +import com.google.devtools.build.lib.packages.StarlarkNativeAspect; +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.LoadStarlarkAspectFunction.StarlarkAspectLoadingKey; +import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingValue; 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.HashMap; +import java.util.Map; import javax.annotation.Nullable; /** - * SkyFunction to load aspects from Starlark extensions and calculate their values. + * SkyFunction to load top level aspects, build the dependency relation between them based on the + * providers they advertise and provide using {@link AspectCollection} and runs the obtained aspects + * path on the top level target. * *

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 +56,33 @@ public class ToplevelStarlarkAspectFunction implements SkyFunction { @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; + ImmutableList topLevelAspects = + getTopLevelAspects(env, topLevelAspectsKey.getTopLevelAspectsClasses()); + if (topLevelAspects == null) { + return null; // some aspects are not loaded + } + + AspectCollection aspectCollection; 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); + aspectCollection = AspectCollection.create(topLevelAspects); + } catch (AspectCycleOnPathException e) { + env.getListener().handle(Event.error(e.getMessage())); + throw new TopLevelStarlarkAspectFunctionException( + new AspectCreationException(e.getMessage(), topLevelAspectsKey.getLabel())); } - SkyKey aspectKey = aspectLoadingKey.toAspectKey(starlarkAspect.getAspectClass()); - return env.getValue(aspectKey); + ImmutableList aspectKeys = + getTopLevelAspectsKeys(aspectCollection, topLevelAspectsKey.getBaseConfiguredTargetKey()); + + Map result = env.getValues(aspectKeys); + if (env.valuesMissing()) { + return null; // some aspects keys are not evaluated + } + + return new TopLevelAspectsValue(result); } @Nullable @@ -77,18 +91,118 @@ 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()); + @Nullable + private static ImmutableList getTopLevelAspects( + Environment env, ImmutableList topLevelAspectsClasses) + throws InterruptedException { + ImmutableList.Builder topLevelAspects = ImmutableList.builder(); + + ImmutableList.Builder aspectLoadingKeys = ImmutableList.builder(); + for (AspectClass aspectClass : topLevelAspectsClasses) { + if (aspectClass instanceof StarlarkAspectClass) { + aspectLoadingKeys.add( + LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey( + (StarlarkAspectClass) aspectClass)); + } + } + + Map 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(); + if (starlarkAspect instanceof StarlarkDefinedAspect) { + StarlarkDefinedAspect starlarkDefinedAspect = (StarlarkDefinedAspect) starlarkAspect; + topLevelAspects.add( + Aspect.forStarlark( + starlarkDefinedAspect.getAspectClass(), + starlarkDefinedAspect.getDefinition(AspectParameters.EMPTY), + AspectParameters.EMPTY, + /** inheritedRequiredProviders = */ + null, + /** inheritedAttributeAspects = */ + null)); + } else { + topLevelAspects.add(Aspect.forNative(((StarlarkNativeAspect) starlarkAspect))); + } + } else { + topLevelAspects.add(Aspect.forNative((NativeAspectClass) aspectClass)); + } + } + + return topLevelAspects.build(); + } + + private static ImmutableList getTopLevelAspectsKeys( + AspectCollection aspectCollection, ConfiguredTargetKey topLevelTargetKey) { + Map result = new HashMap<>(); + for (AspectCollection.AspectDeps aspectDeps : aspectCollection.getUsedAspects()) { + buildAspectKey(aspectDeps, result, topLevelTargetKey); + } + return ImmutableList.copyOf(result.values()); + } + + private static AspectKey buildAspectKey( + AspectCollection.AspectDeps aspectDeps, + Map result, + ConfiguredTargetKey topLevelTargetKey) { + if (result.containsKey(aspectDeps.getAspect())) { + return result.get(aspectDeps.getAspect()); + } + + ImmutableList.Builder dependentAspects = ImmutableList.builder(); + for (AspectCollection.AspectDeps path : aspectDeps.getUsedAspects()) { + dependentAspects.add(buildAspectKey(path, result, topLevelTargetKey)); + } + + AspectKey aspectKey = + AspectValueKey.createAspectKey( + aspectDeps.getAspect(), + 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 Map topLevelAspectsMap; + + public TopLevelAspectsValue(Map topLevelAspectsMap) { + this.topLevelAspectsMap = topLevelAspectsMap; + } + + public ImmutableList getTopLevelAspectsValues() { + return ImmutableList.copyOf(topLevelAspectsMap.values()); + } + + public SkyValue get(SkyKey skyKey) { + return topLevelAspectsMap.get(skyKey); + } + + @Override + public ImmutableList getActions() { + // return topLevelAspectsMap.values().stream(). + return ImmutableList.of(); + } + } } 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 17ec74063c813f..f491beac037b4e 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 @@ public void loadingPhaseCycleWithDifferentTopLevelKeyTypes() throws Exception { + "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 e74bb4d67e0ca2..44c0059d9b2714 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 @@ -40,6 +40,7 @@ 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.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; @@ -52,6 +53,7 @@ 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; @@ -4195,6 +4197,1289 @@ public void testAspectRequiresAspect_withRequiredAspectProvidersNotFound() throw 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 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 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 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 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 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 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 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. + * + *

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 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"); + } + + @Test + public void testTopLevelAspectOnAspect_invalidAspectsCycle() 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',", + ")"); + 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 is applied twice, both before and after aspect" + + " //test:defs.bzl%a2"); + } + + /** + * --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 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 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 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 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"); + } + + private ConfiguredAspect getConfiguredAspect( + Map aspectsMap, String aspectName) { + for (Map.Entry entry : aspectsMap.entrySet()) { + String aspectExportedName = + ((StarlarkAspectClass) entry.getKey().getAspectClass()).getExportedName(); + if (aspectExportedName.equals(aspectName)) { + return entry.getValue(); + } + } + return null; + } + + private ConfiguredAspect getConfiguredAspect( + Map aspectsMap, String aspectName, String targetName) { + for (Map.Entry 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; + } + /** StarlarkAspectTest with "keep going" flag */ @RunWith(JUnit4.class) public static final class WithKeepGoing extends StarlarkDefinedAspectsTest {