Fix implementation of ComputedDefault attributes on aspects, not to throw ClassCast exception.
Before the fix as soon as a ComputedDefault attribute was added to an aspect ClassCast exception was thrown.
RELNOTES: None.
PiperOrigin-RevId: 314797027
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
index b81feff..c9612dd 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/DependencyResolver.java
@@ -42,6 +42,7 @@
import com.google.devtools.build.lib.packages.AspectClass;
import com.google.devtools.build.lib.packages.AspectDescriptor;
import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.AttributeTransitionData;
@@ -504,6 +505,9 @@
dependencyKind.getOwningAspect() == null
? attributeMap.get(attribute.getName(), attribute.getType())
: attribute.getDefaultValue(rule);
+ if (attributeValue instanceof ComputedDefault) {
+ attributeValue = ((ComputedDefault) attributeValue).getDefault(attributeMap);
+ }
} else if (attribute.isLateBound()) {
attributeValue =
resolveLateBoundDefault(rule, attributeMap, attribute, ruleConfig, hostConfig);
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleClassFunctions.java
index 4687d90..8087f53 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/StarlarkRuleClassFunctions.java
@@ -47,6 +47,7 @@
import com.google.devtools.build.lib.cmdline.LabelValidator;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.Attribute.StarlarkComputedDefaultTemplate;
import com.google.devtools.build.lib.packages.AttributeContainer;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.AttributeValueSource;
@@ -549,6 +550,20 @@
throw new EvalException(
null, String.format("Aspect attribute '%s' has no default value.", starlarkName));
}
+ if (attribute.getDefaultValueUnchecked() instanceof StarlarkComputedDefaultTemplate) {
+ // Attributes specifying dependencies using computed value are currently not supported.
+ // The limitation is in place because:
+ // - blaze query requires that all possible values are knowable without BuildConguration
+ // - aspects can attach to any rule
+ // Current logic in StarlarkComputedDefault is not enough,
+ // however {Conservative,Precise}AspectResolver can probably be improved to make that work.
+ String starlarkName = "_" + nativeName.substring(1);
+ throw new EvalException(
+ null,
+ String.format(
+ "Aspect attribute '%s' (%s) with computed default value is unsupported.",
+ starlarkName, attribute.getType()));
+ }
attributes.add(attribute);
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
index abf181e..5aa17b3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
@@ -25,6 +25,7 @@
import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy;
import com.google.devtools.build.lib.packages.Type.LabelClass;
import com.google.devtools.build.lib.packages.Type.LabelVisitor;
@@ -370,16 +371,42 @@
/**
* Adds an attribute to the aspect.
*
- * <p>Since aspects do not appear in BUILD files, the attribute must be either implicit
- * (not available in the BUILD file, starting with '$') or late-bound (determined after the
+ * <p>Since aspects do not appear in BUILD files, the attribute must be either implicit (not
+ * available in the BUILD file, starting with '$') or late-bound (determined after the
* configuration is available, starting with ':')
+ *
+ * <p>Aspect definition currently cannot handle {@link ComputedDefault} dependencies (type LABEL
+ * or LABEL_LIST), because all the dependencies are resolved from the aspect definition and the
+ * defining rule.
*/
public Builder add(Attribute attribute) {
- Preconditions.checkArgument(attribute.isImplicit() || attribute.isLateBound()
- || (attribute.getType() == Type.STRING && attribute.checkAllowedValues()),
- "Invalid attribute '%s' (%s)", attribute.getName(), attribute.getType());
- Preconditions.checkArgument(!attributes.containsKey(attribute.getName()),
- "An attribute with the name '%s' already exists.", attribute.getName());
+ Preconditions.checkArgument(
+ attribute.isImplicit()
+ || attribute.isLateBound()
+ || (attribute.getType() == Type.STRING && attribute.checkAllowedValues()),
+ "%s: Invalid attribute '%s' (%s)",
+ aspectClass.getName(),
+ attribute.getName(),
+ attribute.getType());
+
+ // Attributes specifying dependencies using ComputedDefault value are currently not supported.
+ // The limitation is in place because:
+ // - blaze query requires that all possible values are knowable without BuildConguration
+ // - aspects can attach to any rule
+ // Current logic in #forEachLabelDepFromAllAttributesOfAspect is not enough,
+ // however {Conservative,Precise}AspectResolver can probably be improved to make that work.
+ Preconditions.checkArgument(
+ !(attribute.getType().getLabelClass() == LabelClass.DEPENDENCY
+ && (attribute.getDefaultValueUnchecked() instanceof ComputedDefault)),
+ "%s: Invalid attribute '%s' (%s) with computed default dependencies",
+ aspectClass.getName(),
+ attribute.getName(),
+ attribute.getType());
+ Preconditions.checkArgument(
+ !attributes.containsKey(attribute.getName()),
+ "%s: An attribute with the name '%s' already exists.",
+ aspectClass.getName(),
+ attribute.getName());
attributes.put(attribute.getName(), attribute);
return this;
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
index 60164a6..2df81ba 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AspectTest.java
@@ -327,6 +327,15 @@
}
@Test
+ public void aspectWithComputedAttribute() throws Exception {
+ setRulesAvailableInTests(TestAspects.BASE_RULE, TestAspects.COMPUTED_ATTRIBUTE_ASPECT_RULE);
+
+ pkg("a", "rule_with_computed_deps_aspect(name='a', foo=[':b'])", "base(name='b')");
+
+ getConfiguredTarget("//a:a");
+ }
+
+ @Test
public void ruleDependencyDeprecationWarningsAbsentDuringAspectEvaluations() throws Exception {
setRulesAvailableInTests(TestAspects.BASE_RULE, TestAspects.ASPECT_REQUIRING_RULE);
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
index d15b30b..709d697 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
@@ -623,6 +623,7 @@
TestAspects.ALL_ATTRIBUTES_WITH_TOOL_ASPECT,
TestAspects.BAR_PROVIDER_ASPECT,
TestAspects.EXTRA_ATTRIBUTE_ASPECT,
+ TestAspects.COMPUTED_ATTRIBUTE_ASPECT,
TestAspects.FOO_PROVIDER_ASPECT,
TestAspects.ASPECT_REQUIRING_PROVIDER_SETS,
TestAspects.WARNING_ASPECT,
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
index dec5353..f324777 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/TestAspects.java
@@ -17,6 +17,7 @@
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.packages.Type.STRING;
+import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableCollection;
@@ -45,7 +46,9 @@
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.packages.AspectDefinition;
import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.Attribute.ComputedDefault;
import com.google.devtools.build.lib.packages.Attribute.LabelListLateBoundDefault;
+import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.NativeAspectClass;
import com.google.devtools.build.lib.packages.Rule;
@@ -335,6 +338,29 @@
}
}
+ public static final ComputedAttributeAspect COMPUTED_ATTRIBUTE_ASPECT =
+ new ComputedAttributeAspect();
+ private static final AspectDefinition COMPUTED_ATTRIBUTE_ASPECT_DEFINITION =
+ new AspectDefinition.Builder(COMPUTED_ATTRIBUTE_ASPECT)
+ .add(
+ attr("$default_copts", STRING_LIST)
+ .value(
+ new ComputedDefault() {
+ @Override
+ public Object getDefault(AttributeMap rule) {
+ return rule.getPackageDefaultCopts();
+ }
+ }))
+ .build();
+
+ /** An aspect that defines its own computed default attribute. */
+ public static class ComputedAttributeAspect extends BaseAspect {
+ @Override
+ public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+ return COMPUTED_ATTRIBUTE_ASPECT_DEFINITION;
+ }
+ }
+
public static final AttributeAspect ATTRIBUTE_ASPECT = new AttributeAspect();
private static final AspectDefinition ATTRIBUTE_ASPECT_DEFINITION =
new AspectDefinition.Builder(ATTRIBUTE_ASPECT)
@@ -648,6 +674,17 @@
attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)
.aspect(EXTRA_ATTRIBUTE_ASPECT));
+ /** A rule that defines an {@link ComputedAttributeAspect} on one of its attributes. */
+ public static final MockRule COMPUTED_ATTRIBUTE_ASPECT_RULE =
+ () ->
+ MockRule.ancestor(BASE_RULE.getClass())
+ .factory(DummyRuleFactory.class)
+ .define(
+ "rule_with_computed_deps_aspect",
+ attr("foo", LABEL_LIST)
+ .allowedFileTypes(FileTypeSet.ANY_FILE)
+ .aspect(COMPUTED_ATTRIBUTE_ASPECT));
+
/**
* A rule that defines an {@link ParametrizedDefinitionAspect} on one of its attributes.
*/
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkDefinedAspectsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkDefinedAspectsTest.java
index d73eaff..5530aca 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkDefinedAspectsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkDefinedAspectsTest.java
@@ -1358,6 +1358,60 @@
}
@Test
+ public void aspectParametersConfigurationField() throws Exception {
+ scratch.file(
+ "test/aspect.bzl",
+ "def _impl(target, ctx):",
+ " return struct()",
+ "def _rule_impl(ctx):",
+ " return struct()",
+ "MyAspect = aspect(",
+ " implementation=_impl,",
+ " attrs = { '_my_attr' : attr.label(default=",
+ " configuration_field(fragment='cpp', name = 'cc_toolchain')) },",
+ ")",
+ "my_rule = rule(",
+ " implementation=_rule_impl,",
+ " attrs = { 'deps' : attr.label_list(aspects=[MyAspect]) },",
+ ")");
+ scratch.file("test/BUILD", "load('//test:aspect.bzl', 'my_rule')", "my_rule(name = 'xxx')");
+
+ AnalysisResult result = update(ImmutableList.<String>of(), "//test:xxx");
+ assertThat(result.hasError()).isFalse();
+ }
+
+ @Test
+ public void aspectParameterComputedDefault() throws Exception {
+ scratch.file(
+ "test/aspect.bzl",
+ "def _impl(target, ctx):",
+ " return struct()",
+ "def _rule_impl(ctx):",
+ " return struct()",
+ "def _defattr():",
+ " return Label('//foo/bar:baz')",
+ "MyAspect = aspect(",
+ " implementation=_impl,",
+ " attrs = { '_extra' : attr.label(default = _defattr) }",
+ ")",
+ "my_rule = rule(",
+ " implementation=_rule_impl,",
+ " attrs = { 'deps' : attr.label_list(aspects=[MyAspect]) },",
+ ")");
+ scratch.file("test/BUILD", "load('//test:aspect.bzl', 'my_rule')", "my_rule(name = 'xxx')");
+ reporter.removeHandler(failFastHandler);
+
+ if (keepGoing()) {
+ AnalysisResult result = update("//test:xxx");
+ assertThat(result.hasError()).isTrue();
+ } else {
+ assertThrows(TargetParsingException.class, () -> update("//test:xxx"));
+ }
+ assertContainsEvent(
+ "Aspect attribute '_extra' (label) with computed default value is unsupported.");
+ }
+
+ @Test
public void aspectParametersOptional() throws Exception {
scratch.file(
"test/aspect.bzl",