Allow aspects to adveritise providers they provide.
--
PiperOrigin-RevId: 146794883
MOS_MIGRATED_REVID=146794883
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index 11d03fa..e2fbee9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -35,6 +35,7 @@
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.AdvertisedProviderSet;
import com.google.devtools.build.lib.packages.Aspect;
import com.google.devtools.build.lib.packages.AspectDescriptor;
import com.google.devtools.build.lib.packages.Attribute;
@@ -352,7 +353,49 @@
return null;
}
- return aspectFactory.create(associatedTarget, ruleContext, aspect.getParameters());
+ ConfiguredAspect configuredAspect = aspectFactory
+ .create(associatedTarget, ruleContext, aspect.getParameters());
+ validateAdvertisedProviders(
+ configuredAspect, aspect.getDefinition().getAdvertisedProviders(),
+ associatedTarget.getTarget(),
+ env.getEventHandler()
+ );
+ return configuredAspect;
+ }
+
+ private void validateAdvertisedProviders(
+ ConfiguredAspect configuredAspect,
+ AdvertisedProviderSet advertisedProviders, Target target,
+ EventHandler eventHandler) {
+ if (advertisedProviders.canHaveAnyProvider()) {
+ return;
+ }
+ for (Class<?> aClass : advertisedProviders.getNativeProviders()) {
+ if (configuredAspect.getProvider(aClass.asSubclass(TransitiveInfoProvider.class)) == null) {
+ eventHandler.handle(Event.error(
+ target.getLocation(),
+ String.format(
+ "Aspect '%s', applied to '%s', does not provide advertised provider '%s'",
+ configuredAspect.getName(),
+ target.getLabel(),
+ aClass.getSimpleName()
+ )));
+ }
+ }
+
+ for (String providerName : advertisedProviders.getSkylarkProviders()) {
+ SkylarkProviders skylarkProviders = configuredAspect.getProvider(SkylarkProviders.class);
+ if (skylarkProviders == null || skylarkProviders.getValue(providerName) == null) {
+ eventHandler.handle(Event.error(
+ target.getLocation(),
+ String.format(
+ "Aspect '%s', applied to '%s', does not provide advertised provider '%s'",
+ configuredAspect.getName(),
+ target.getLabel(),
+ providerName
+ )));
+ }
+ }
}
/**
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 55b0616..c7e11c0 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
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.packages;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
@@ -52,6 +53,7 @@
@Immutable
public final class AspectDefinition {
private final AspectClass aspectClass;
+ private final AdvertisedProviderSet advertisedProviders;
private final RequiredProviders requiredProviders;
private final RequiredProviders requiredProvidersForAspects;
private final ImmutableMap<String, Attribute> attributes;
@@ -66,14 +68,21 @@
@Nullable private final ImmutableSet<String> restrictToAttributes;
@Nullable private final ConfigurationFragmentPolicy configurationFragmentPolicy;
+ public AdvertisedProviderSet getAdvertisedProviders() {
+ return advertisedProviders;
+ }
+
+
private AspectDefinition(
AspectClass aspectClass,
+ AdvertisedProviderSet advertisedProviders,
RequiredProviders requiredProviders,
RequiredProviders requiredAspectProviders,
ImmutableMap<String, Attribute> attributes,
@Nullable ImmutableSet<String> restrictToAttributes,
@Nullable ConfigurationFragmentPolicy configurationFragmentPolicy) {
this.aspectClass = aspectClass;
+ this.advertisedProviders = advertisedProviders;
this.requiredProviders = requiredProviders;
this.requiredProvidersForAspects = requiredAspectProviders;
@@ -217,6 +226,8 @@
public static final class Builder {
private final AspectClass aspectClass;
private final Map<String, Attribute> attributes = new LinkedHashMap<>();
+ private final AdvertisedProviderSet.Builder advertisedProviders =
+ AdvertisedProviderSet.builder();
private RequiredProviders.Builder requiredProviders = RequiredProviders.acceptAnyBuilder();
private RequiredProviders.Builder requiredAspectProviders =
RequiredProviders.acceptNoneBuilder();
@@ -267,6 +278,29 @@
return this;
}
+ /**
+ * State that the aspect being built provides given providers.
+ */
+ public Builder advertiseProvider(Class<?>... providers) {
+ for (Class<?> provider : providers) {
+ advertisedProviders.addNative(provider);
+ }
+ return this;
+ }
+
+ /**
+ * State that the aspect being built provides given providers.
+ */
+ public Builder advertiseProvider(ImmutableList<SkylarkProviderIdentifier> providers) {
+ for (SkylarkProviderIdentifier provider : providers) {
+ // todo(dslomov,vladmos): support declared providers
+ Preconditions.checkState(provider.isLegacy());
+ advertisedProviders.addSkylark(provider.getLegacyId());
+ }
+ return this;
+ }
+
+
/**
* Declares that this aspect propagates along an {@code attribute} on the target
@@ -402,6 +436,7 @@
*/
public AspectDefinition build() {
return new AspectDefinition(aspectClass,
+ advertisedProviders.build(),
requiredProviders.build(),
requiredAspectProviders.build(),
ImmutableMap.copyOf(attributes),
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
index 389c2d0..2288a76 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java
@@ -1014,6 +1014,9 @@
return this;
}
+ /**
+ * Should only be used for deserialization.
+ */
public Builder<TYPE> aspect(final Aspect aspect) {
PredefinedRuleAspect predefinedRuleAspect = new PredefinedRuleAspect(aspect);
RuleAspect<?> oldAspect =
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 c3748fe..76d24e0 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
@@ -684,4 +684,29 @@
.containsExactly(
"aspect //a:a", "aspect //a:b", "aspect //a:c", "aspect //a:tool", "rule //a:x");
}
+
+ @Test
+ public void aspectTruthInAdvertisement() throws Exception {
+ reporter.removeHandler(failFastHandler); // expect errors
+ setRulesAvailableInTests(
+ new TestAspects.BaseRule(),
+ new TestAspects.SimpleRule(),
+ new TestAspects.FalseAdvertisementAspectRule());
+ pkg(
+ "a",
+ "simple(name = 's')",
+ "false_advertisement_aspect(name = 'x', deps = [':s'])"
+ );
+ try {
+ update("//a:x");
+ } catch (ViewCreationFailedException e) {
+ // expected.
+ }
+ assertContainsEvent(
+ "Aspect 'FalseAdvertisementAspect', applied to '//a:s',"
+ + " does not provide advertised provider 'RequiredProvider'");
+ assertContainsEvent(
+ "Aspect 'FalseAdvertisementAspect', applied to '//a:s',"
+ + " does not provide advertised provider 'advertised_provider'");
+ }
}
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 3a74a59..0566c43 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
@@ -61,6 +61,7 @@
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder;
+import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
@@ -442,6 +443,33 @@
.build();
/**
+ * An aspect that advertises but fails to provide providers.
+ */
+ public static class FalseAdvertisementAspect extends NativeAspectClass
+ implements ConfiguredAspectFactory {
+
+ @Override
+ public AspectDefinition getDefinition(AspectParameters aspectParameters) {
+ return FALSE_ADVERTISEMENT_DEFINITION;
+ }
+
+ @Override
+ public ConfiguredAspect create(ConfiguredTarget base, RuleContext context,
+ AspectParameters parameters) throws InterruptedException {
+ return new ConfiguredAspect.Builder(this, parameters, context).build();
+ }
+ }
+ public static final FalseAdvertisementAspect FALSE_ADVERTISEMENT_ASPECT
+ = new FalseAdvertisementAspect();
+ private static final AspectDefinition FALSE_ADVERTISEMENT_DEFINITION =
+ new AspectDefinition.Builder(FALSE_ADVERTISEMENT_ASPECT)
+ .advertiseProvider(RequiredProvider.class)
+ .advertiseProvider(
+ ImmutableList.of(SkylarkProviderIdentifier.forLegacy("advertised_provider")))
+ .build();
+
+
+ /**
* A common base rule for mock rules in this class to reduce boilerplate.
*
* <p>It has a few common attributes because internal Blaze machinery assumes the presence of
@@ -967,6 +995,28 @@
}
/**
+ * Rule with {@link FalseAdvertisementAspect}
+ */
+ public static final class FalseAdvertisementAspectRule implements RuleDefinition {
+
+ @Override
+ public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+ return builder
+ .add(attr("deps", LABEL_LIST).allowedFileTypes().aspect(FALSE_ADVERTISEMENT_ASPECT))
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("false_advertisement_aspect")
+ .factoryClass(DummyRuleFactory.class)
+ .ancestors(BaseRule.class)
+ .build();
+ }
+ }
+
+ /**
* Rule with rule class configuration transition.
*/
public static class RuleClassTransitionRule implements RuleDefinition {