Skip to content

Commit ed25118

Browse files
mai93copybara-github
authored andcommitted
Add incompatible flag to guard top-level aspects dependencies
This CL introduces `incompatible_top_level_aspects_dependency` flag to enable aspect-on-aspect and requiring aspects for command line aspects. This can be needed because the new behavior can break builds where a relation between top-level aspects existed (based on required_aspect_providers and provides) but it was never actually applied as it was not supported. One example of these cases: blaze build //:main --aspects=/tools:my_def.bzl%a1,/tools:my_def.bzl%a2 If aspect a1 provides a1p provider, aspect a2 requires a1p provider and the rule of target `main` also provides a1p. Once top-level aspect-on-aspect is enabled this build will fail because a1p will be provided twice to a2 (from a1 applied on `main` and from `main` target rule). Previously the relation between a1 and a2 was not detected and a2 used to get only the value of a1p from `main`'s rule. PiperOrigin-RevId: 390587290
1 parent 055e40d commit ed25118

File tree

7 files changed

+290
-67
lines changed

7 files changed

+290
-67
lines changed

src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -166,19 +166,4 @@ public class AnalysisOptions extends OptionsBase {
166166
+ " be used. Example value: \"HOST_CPUS*0.5\".",
167167
converter = CpuResourceConverter.class)
168168
public int oomSensitiveSkyFunctionsSemaphoreSize;
169-
170-
@Option(
171-
name = "incompatible_ignore_duplicate_top_level_aspects",
172-
defaultValue = "true",
173-
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
174-
metadataTags = {
175-
OptionMetadataTag.INCOMPATIBLE_CHANGE,
176-
OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
177-
},
178-
effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
179-
help =
180-
"If true, remove duplicates from --aspects list by keeping only the first occurrence of"
181-
+ " every aspect. Otherwise, throw validation error if duplicate aspects are"
182-
+ " encountered.")
183-
public boolean ignoreDuplicateTopLevelAspects;
184169
}

src/main/java/com/google/devtools/build/lib/analysis/BuildView.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,6 @@ public AnalysisResult update(
275275
.collect(Collectors.toList());
276276

277277
ImmutableList.Builder<AspectClass> aspectClassesBuilder = ImmutableList.builder();
278-
if (viewOptions.ignoreDuplicateTopLevelAspects) {
279-
// remove duplicates from aspects list
280-
aspects = ImmutableSet.copyOf(aspects).asList();
281-
}
282278
for (String aspect : aspects) {
283279
// Syntax: label%aspect
284280
int delimiterPosition = aspect.indexOf('%');

src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,18 @@ public class BuildRequestOptions extends OptionsBase {
306306
allowMultiple = true,
307307
help =
308308
"Comma-separated list of aspects to be applied to top-level targets. All aspects are"
309-
+ " applied to all top-level targets. If aspect <code>some_aspect</code> specifies"
310-
+ " required aspect providers via <code>required_aspect_providers</code>,"
311-
+ " <code>some_aspect</code> will run after every aspect that was mentioned before it"
312-
+ " in the aspects list and whose advertised providers satisfy"
313-
+ " <code>some_aspect</code> required aspect providers. <code>some_aspect</code> will"
314-
+ " then have access to the values of those aspects' providers. Aspects that do not"
315-
+ " have such dependency will run independently on the top-level targets."
316-
+ ""
317-
+ " Aspects are specified in the form <bzl-file-label>%<aspect_name>, for example"
318-
+ " '//tools:my_def.bzl%my_aspect', where 'my_aspect' is a top-level value from a"
319-
+ " file tools/my_def.bzl")
309+
+ " applied independently to all top-level targets except if"
310+
+ " <code>incompatible_top_level_aspects_dependency</code> is used. In this case, if"
311+
+ " aspect <code>some_aspect</code> specifies required aspect providers via"
312+
+ " <code>required_aspect_providers</code>, <code>some_aspect</code> will run after"
313+
+ " every aspect that was mentioned before it in the aspects list whose advertised"
314+
+ " providers satisfy <code>some_aspect</code> required aspect providers. Moreover,"
315+
+ " <code>some_aspect</code> will run after all its required aspects specified by"
316+
+ " <code>requires</code> attribute which otherwise will be ignored."
317+
+ " <code>some_aspect</code> will then have access to the values of those aspects'"
318+
+ " providers."
319+
+ " <bzl-file-label>%<aspect_name>, for example '//tools:my_def.bzl%my_aspect', where"
320+
+ " 'my_aspect' is a top-level value from a file tools/my_def.bzl")
320321
public List<String> aspects;
321322

322323
public BuildRequestOptions() throws OptionsParsingException {}

src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,22 @@ public class BuildLanguageOptions extends OptionsBase implements Serializable {
619619
+ " providers of the aspect.")
620620
public boolean incompatibleTopLevelAspectsRequireProviders;
621621

622+
@Option(
623+
name = "incompatible_top_level_aspects_dependency",
624+
defaultValue = "false",
625+
documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
626+
metadataTags = {
627+
OptionMetadataTag.INCOMPATIBLE_CHANGE,
628+
OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
629+
},
630+
effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
631+
help =
632+
"If set to true, a dependency between the top level aspects will be built based on their"
633+
+ " required aspect providers, advertised providers and required aspects. Otherwise,"
634+
+ " each aspect in the list will run independently and its required aspects will be"
635+
+ " ignored.")
636+
public boolean incompatibleTopLevelAspectsDependOnAspects;
637+
622638
@Option(
623639
name = "experimental_required_aspects",
624640
defaultValue = "false",
@@ -698,6 +714,9 @@ public StarlarkSemantics toStarlarkSemantics() {
698714
.setBool(
699715
INCOMPATIBLE_TOP_LEVEL_ASPECTS_REQUIRE_PROVIDERS,
700716
incompatibleTopLevelAspectsRequireProviders)
717+
.setBool(
718+
INCOMPATIBLE_TOP_LEVEL_ASPECTS_DEPENDENCY,
719+
incompatibleTopLevelAspectsDependOnAspects)
701720
.setBool(EXPERIMENTAL_REQUIRED_ASPECTS, experimentalRequiredAspects)
702721
.build();
703722
return INTERNER.intern(semantics);
@@ -770,6 +789,8 @@ public StarlarkSemantics toStarlarkSemantics() {
770789
"-incompatible_visibility_private_attributes_at_definition";
771790
public static final String INCOMPATIBLE_TOP_LEVEL_ASPECTS_REQUIRE_PROVIDERS =
772791
"-incompatible_top_level_aspects_require_providers";
792+
public static final String INCOMPATIBLE_TOP_LEVEL_ASPECTS_DEPENDENCY =
793+
"-incompatible_top_level_aspects_dependency";
773794
public static final String EXPERIMENTAL_REQUIRED_ASPECTS = "-experimental_required_aspects";
774795

775796
// non-booleans

src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import com.google.common.base.Objects;
1818
import com.google.common.collect.ImmutableList;
19+
import com.google.common.collect.ImmutableSet;
1920
import com.google.common.collect.Interner;
2021
import com.google.devtools.build.lib.analysis.AspectCollection;
2122
import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException;
@@ -28,6 +29,7 @@
2829
import com.google.devtools.build.lib.packages.NativeAspectClass;
2930
import com.google.devtools.build.lib.packages.StarlarkAspect;
3031
import com.google.devtools.build.lib.packages.StarlarkAspectClass;
32+
import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
3133
import com.google.devtools.build.lib.server.FailureDetails.Analysis;
3234
import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
3335
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
@@ -46,6 +48,7 @@
4648
import java.util.Map;
4749
import javax.annotation.Nullable;
4850
import net.starlark.java.eval.EvalException;
51+
import net.starlark.java.eval.StarlarkSemantics;
4952

5053
/**
5154
* SkyFunction to load top level aspects, build the dependency relation between them based on the
@@ -68,11 +71,33 @@ public class BuildTopLevelAspectsDetailsFunction implements SkyFunction {
6871
@Override
6972
public SkyValue compute(SkyKey skyKey, Environment env)
7073
throws BuildTopLevelAspectsDetailsFunctionException, InterruptedException {
74+
7175
BuildTopLevelAspectsDetailsKey topLevelAspectsDetailsKey =
7276
(BuildTopLevelAspectsDetailsKey) skyKey.argument();
77+
ImmutableList<AspectClass> topLevelAspectsClasses =
78+
topLevelAspectsDetailsKey.getTopLevelAspectsClasses();
79+
80+
StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
81+
if (starlarkSemantics == null) {
82+
return null;
83+
}
84+
boolean buildTopLevelAspectsDependency =
85+
starlarkSemantics.getBool(BuildLanguageOptions.INCOMPATIBLE_TOP_LEVEL_ASPECTS_DEPENDENCY);
86+
if (!buildTopLevelAspectsDependency) {
87+
// If building a relation between top-level aspects is not required, then we can remove
88+
// duplicate aspects by keeping the first occurrence of each aspect.
89+
topLevelAspectsClasses = ImmutableSet.copyOf(topLevelAspectsClasses).asList();
90+
91+
// Then return a list of indenpendent aspects to be applied on the top-level targets
92+
ImmutableList<AspectDetails> aspectsDetailsList =
93+
getIndependentTopLevelAspects(env, topLevelAspectsClasses);
94+
if (aspectsDetailsList == null) {
95+
return null; // some aspects are not loaded
96+
}
97+
return new BuildTopLevelAspectsDetailsValue(aspectsDetailsList);
98+
}
7399

74-
ImmutableList<Aspect> topLevelAspects =
75-
getTopLevelAspects(env, topLevelAspectsDetailsKey.getTopLevelAspectsClasses());
100+
ImmutableList<Aspect> topLevelAspects = getTopLevelAspects(env, topLevelAspectsClasses);
76101

77102
if (topLevelAspects == null) {
78103
return null; // some aspects are not loaded
@@ -98,11 +123,9 @@ public String extractTag(SkyKey skyKey) {
98123
}
99124

100125
@Nullable
101-
private static ImmutableList<Aspect> getTopLevelAspects(
126+
private static Map<SkyKey, SkyValue> loadAspects(
102127
Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
103-
throws InterruptedException, BuildTopLevelAspectsDetailsFunctionException {
104-
AspectsListBuilder aspectsList = new AspectsListBuilder();
105-
128+
throws InterruptedException {
106129
ImmutableList.Builder<StarlarkAspectLoadingKey> aspectLoadingKeys = ImmutableList.builder();
107130
for (AspectClass aspectClass : topLevelAspectsClasses) {
108131
if (aspectClass instanceof StarlarkAspectClass) {
@@ -116,6 +139,47 @@ private static ImmutableList<Aspect> getTopLevelAspects(
116139
if (env.valuesMissing()) {
117140
return null;
118141
}
142+
return loadedAspects;
143+
}
144+
145+
@Nullable
146+
private static ImmutableList<AspectDetails> getIndependentTopLevelAspects(
147+
Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
148+
throws InterruptedException {
149+
Map<SkyKey, SkyValue> loadedAspects = loadAspects(env, topLevelAspectsClasses);
150+
if (loadedAspects == null) {
151+
return null;
152+
}
153+
ImmutableList.Builder<AspectDetails> aspectDetailsList = ImmutableList.builder();
154+
155+
for (AspectClass aspectClass : topLevelAspectsClasses) {
156+
if (aspectClass instanceof StarlarkAspectClass) {
157+
StarlarkAspectLoadingValue aspectLoadingValue =
158+
(StarlarkAspectLoadingValue)
159+
loadedAspects.get(
160+
LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey(
161+
(StarlarkAspectClass) aspectClass));
162+
StarlarkAspect starlarkAspect = aspectLoadingValue.getAspect();
163+
aspectDetailsList.add(
164+
new AspectDetails(
165+
ImmutableList.of(), new AspectDescriptor(starlarkAspect.getAspectClass())));
166+
} else {
167+
aspectDetailsList.add(
168+
new AspectDetails(ImmutableList.of(), new AspectDescriptor(aspectClass)));
169+
}
170+
}
171+
return aspectDetailsList.build();
172+
}
173+
174+
@Nullable
175+
private static ImmutableList<Aspect> getTopLevelAspects(
176+
Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
177+
throws InterruptedException, BuildTopLevelAspectsDetailsFunctionException {
178+
AspectsListBuilder aspectsList = new AspectsListBuilder();
179+
Map<SkyKey, SkyValue> loadedAspects = loadAspects(env, topLevelAspectsClasses);
180+
if (loadedAspects == null) {
181+
return null;
182+
}
119183

120184
for (AspectClass aspectClass : topLevelAspectsClasses) {
121185
if (aspectClass instanceof StarlarkAspectClass) {

src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ public void duplicateTopLevelAspects_duplicateAspectsNotAllowed() throws Excepti
904904
AspectApplyingToFiles aspectApplyingToFiles = new AspectApplyingToFiles();
905905
setRulesAndAspectsAvailableInTests(ImmutableList.of(aspectApplyingToFiles), ImmutableList.of());
906906
pkg("a", "java_binary(name = 'x', main_class = 'x.FooBar', srcs = ['x.java'])");
907-
useConfiguration("--incompatible_ignore_duplicate_top_level_aspects=false");
907+
useConfiguration("--incompatible_top_level_aspects_dependency");
908908
reporter.removeHandler(failFastHandler);
909909

910910
assertThrows(

0 commit comments

Comments
 (0)