// Copyright 2015 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.analysis.util; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; 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.BuildType.NODEP_LABEL_LIST; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; import static com.google.devtools.build.lib.syntax.Type.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import com.google.common.base.Function; import com.google.common.base.Strings; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.ConfiguredAspect; import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Options; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.PatchTransition; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; 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.LateBoundLabel; import com.google.devtools.build.lib.packages.Attribute.LateBoundLabelList; import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.packages.Attribute.Transition; 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.NonconfigurableAttributeMapper; 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.RuleTransitionFactory; 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; import java.util.List; /** * Various rule and aspect classes that aid in testing the aspect machinery. * * <p>These are mostly used in {@link com.google.devtools.build.lib.analysis.DependencyResolverTest} * and {@link com.google.devtools.build.lib.analysis.AspectTest}. */ public class TestAspects { public static final LateBoundLabel EMPTY_LATE_BOUND_LABEL = new LateBoundLabel<Object>() { @Override public Label resolve(Rule rule, AttributeMap attributes, Object configuration) { return null; } }; /** * A transitive info provider for collecting aspects in the transitive closure. Created by * aspects. */ @Immutable public static final class AspectInfo implements TransitiveInfoProvider { private final NestedSet<String> data; public AspectInfo(NestedSet<String> data) { this.data = data; } public NestedSet<String> getData() { return data; } } /** * A transitive info provider for collecting aspects in the transitive closure. Created by * rules. */ @Immutable public static final class RuleInfo implements TransitiveInfoProvider { private final NestedSet<String> data; public RuleInfo(NestedSet<String> data) { this.data = data; } public NestedSet<String> getData() { return data; } } /** * A very simple provider used in tests that check whether the logic that attaches aspects * depending on whether a configured target has a provider works or not. */ @Immutable public static final class RequiredProvider implements TransitiveInfoProvider { } /** * Another very simple provider used in tests that check whether the logic that attaches aspects * depending on whether a configured target has a provider works or not. */ @Immutable public static final class RequiredProvider2 implements TransitiveInfoProvider { } private static NestedSet<String> collectAspectData(String me, RuleContext ruleContext) { NestedSetBuilder<String> result = new NestedSetBuilder<>(Order.STABLE_ORDER); result.add(me); Iterable<String> attributeNames = ruleContext.attributes().getAttributeNames(); for (String attributeName : attributeNames) { Type<?> attributeType = ruleContext.attributes().getAttributeType(attributeName); if (!LABEL.equals(attributeType) && !LABEL_LIST.equals(attributeType)) { continue; } Iterable<AspectInfo> prerequisites = ruleContext .getPrerequisites(attributeName, Mode.DONT_CHECK, AspectInfo.class); for (AspectInfo prerequisite : prerequisites) { result.addTransitive(prerequisite.getData()); } } return result.build(); } /** * A simple rule configured target factory that is used in all the mock rules in this class. */ public static class DummyRuleFactory implements RuleConfiguredTargetFactory { @Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext) .addProvider( new RuleInfo(collectAspectData("rule " + ruleContext.getLabel(), ruleContext))) .setFilesToBuild(NestedSetBuilder.<Artifact>create(Order.STABLE_ORDER)) .setRunfilesSupport(null, null) .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY)); if (ruleContext.getRule().getRuleClassObject().getName().equals("honest")) { builder.addProvider(new RequiredProvider()); } return builder.build(); } } /** * A simple rule configured target factory that exports provider {@link RequiredProvider2}. */ public static class DummyRuleFactory2 implements RuleConfiguredTargetFactory { @Override public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { return new RuleConfiguredTargetBuilder(ruleContext) .addProvider( new RuleInfo(collectAspectData("rule " + ruleContext.getLabel(), ruleContext))) .setFilesToBuild(NestedSetBuilder.<Artifact>create(Order.STABLE_ORDER)) .setRunfilesSupport(null, null) .add(RunfilesProvider.class, RunfilesProvider.simple(Runfiles.EMPTY)) .addProvider(new RequiredProvider()) .addProvider(new RequiredProvider2()) .build(); } } /** * A base class for mock aspects to reduce boilerplate. */ public abstract static class BaseAspect extends NativeAspectClass implements ConfiguredAspectFactory { @Override public ConfiguredAspect create( ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) { String information = parameters.isEmpty() ? "" : " data " + Iterables.getFirst(parameters.getAttribute("baz"), null); return new ConfiguredAspect.Builder(this, parameters, ruleContext) .addProvider( new AspectInfo( collectAspectData("aspect " + ruleContext.getLabel() + information, ruleContext))) .build(); } } public static final SimpleAspect SIMPLE_ASPECT = new SimpleAspect(); private static final AspectDefinition SIMPLE_ASPECT_DEFINITION = new AspectDefinition.Builder(SIMPLE_ASPECT).build(); /** * A very simple aspect. */ public static class SimpleAspect extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return SIMPLE_ASPECT_DEFINITION; } } public static final ExtraAttributeAspect EXTRA_ATTRIBUTE_ASPECT = new ExtraAttributeAspect(); private static final AspectDefinition EXTRA_ATTRIBUTE_ASPECT_DEFINITION = new AspectDefinition.Builder(EXTRA_ATTRIBUTE_ASPECT) .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra"))) .build(); private static final ExtraAttributeAspectRequiringProvider EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER = new ExtraAttributeAspectRequiringProvider(); private static final AspectDefinition EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER_DEFINITION = new AspectDefinition.Builder(EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER) .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra"))) .requireProviders(RequiredProvider.class) .build(); /** * An aspect that defines its own implicit attribute. */ public static class ExtraAttributeAspect extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return EXTRA_ATTRIBUTE_ASPECT_DEFINITION; } } public static final AttributeAspect ATTRIBUTE_ASPECT = new AttributeAspect(); private static final AspectDefinition ATTRIBUTE_ASPECT_DEFINITION = new AspectDefinition.Builder(ATTRIBUTE_ASPECT) .propagateAlongAttribute("foo") .build(); /** * An aspect that propagates along all attributes. */ public static class AllAttributesAspect extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ALL_ATTRIBUTES_ASPECT_DEFINITION; } } public static final NativeAspectClass ALL_ATTRIBUTES_ASPECT = new AllAttributesAspect(); private static final AspectDefinition ALL_ATTRIBUTES_ASPECT_DEFINITION = new AspectDefinition.Builder(ALL_ATTRIBUTES_ASPECT) .propagateAlongAllAttributes() .build(); /** An aspect that propagates along all attributes and has a tool dependency. */ public static class AllAttributesWithToolAspect extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ALL_ATTRIBUTES_WITH_TOOL_ASPECT_DEFINITION; } } public static final NativeAspectClass ALL_ATTRIBUTES_WITH_TOOL_ASPECT = new AllAttributesWithToolAspect(); private static final AspectDefinition ALL_ATTRIBUTES_WITH_TOOL_ASPECT_DEFINITION = new AspectDefinition.Builder(ALL_ATTRIBUTES_WITH_TOOL_ASPECT) .propagateAlongAllAttributes() .add( attr("$tool", BuildType.LABEL) .allowedFileTypes(FileTypeSet.ANY_FILE) .value(Label.parseAbsoluteUnchecked("//a:tool"))) .build(); /** * An aspect that requires aspects on the attributes of rules it attaches to. */ public static class AttributeAspect extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ATTRIBUTE_ASPECT_DEFINITION; } } /** * An aspect that defines its own implicit attribute and requires provider. */ public static class ExtraAttributeAspectRequiringProvider extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER_DEFINITION; } } public static class AspectRequiringProvider extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ASPECT_REQUIRING_PROVIDER_DEFINITION; } } /** * An aspect that requires provider sets {{@link RequiredProvider}} and * {{@link RequiredProvider2}}. */ public static class AspectRequiringProviderSets extends BaseAspect { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ASPECT_REQUIRING_PROVIDER_SETS_DEFINITION; } } /** * An aspect that has a definition depending on parameters provided by originating rule. */ public static class ParametrizedDefinitionAspect extends NativeAspectClass implements ConfiguredAspectFactory { @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { AspectDefinition.Builder builder = new AspectDefinition.Builder(PARAMETRIZED_DEFINITION_ASPECT) .propagateAlongAttribute("foo"); ImmutableCollection<String> baz = aspectParameters.getAttribute("baz"); if (baz != null) { try { builder.add(attr("$dep", LABEL).value(Label.parseAbsolute(baz.iterator().next()))); } catch (LabelSyntaxException e) { throw new IllegalStateException(); } } return builder.build(); } @Override public ConfiguredAspect create( ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) { StringBuilder information = new StringBuilder("aspect " + ruleContext.getLabel()); if (!parameters.isEmpty()) { information.append(" data " + Iterables.getFirst(parameters.getAttribute("baz"), null)); information.append(" "); } List<? extends TransitiveInfoCollection> deps = ruleContext.getPrerequisites("$dep", Mode.TARGET); information.append("$dep:["); for (TransitiveInfoCollection dep : deps) { information.append(" "); information.append(dep.getLabel()); } information.append("]"); return new ConfiguredAspect.Builder(this, parameters, ruleContext) .addProvider(new AspectInfo(collectAspectData(information.toString(), ruleContext))) .build(); } } private static final ParametrizedDefinitionAspect PARAMETRIZED_DEFINITION_ASPECT = new ParametrizedDefinitionAspect(); private static final AspectRequiringProvider ASPECT_REQUIRING_PROVIDER = new AspectRequiringProvider(); private static final AspectRequiringProviderSets ASPECT_REQUIRING_PROVIDER_SETS = new AspectRequiringProviderSets(); private static final AspectDefinition ASPECT_REQUIRING_PROVIDER_DEFINITION = new AspectDefinition.Builder(ASPECT_REQUIRING_PROVIDER) .requireProviders(RequiredProvider.class) .propagateAlongAttribute("foo") .build(); private static final AspectDefinition ASPECT_REQUIRING_PROVIDER_SETS_DEFINITION = new AspectDefinition.Builder(ASPECT_REQUIRING_PROVIDER_SETS) .requireProviderSets( ImmutableList.of( ImmutableSet.<Class<?>>of(RequiredProvider.class), ImmutableSet.<Class<?>>of(RequiredProvider2.class))) .build(); /** * An aspect that prints a warning. */ public static class WarningAspect extends NativeAspectClass implements ConfiguredAspectFactory { @Override public ConfiguredAspect create( ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) { ruleContext.ruleWarning("Aspect warning on " + base.getTarget().getLabel()); return new ConfiguredAspect.Builder(this, parameters, ruleContext).build(); } @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return WARNING_ASPECT_DEFINITION; } } public static final WarningAspect WARNING_ASPECT = new WarningAspect(); private static final AspectDefinition WARNING_ASPECT_DEFINITION = new AspectDefinition.Builder(WARNING_ASPECT) .propagateAlongAttribute("bar") .build(); /** * An aspect that raises an error. */ public static class ErrorAspect extends NativeAspectClass implements ConfiguredAspectFactory { @Override public ConfiguredAspect create( ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) { ruleContext.ruleError("Aspect error"); return null; } @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return ERROR_ASPECT_DEFINITION; } } public static final ErrorAspect ERROR_ASPECT = new ErrorAspect(); private static final AspectDefinition ERROR_ASPECT_DEFINITION = new AspectDefinition.Builder(ERROR_ASPECT) .propagateAlongAttribute("bar") .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 * these. */ public static class BaseRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("testonly", BOOLEAN).nonconfigurable("test").value(false)) .add(attr("deprecation", STRING).nonconfigurable("test").value((String) null)) .add(attr("tags", STRING_LIST)) .add(attr("visibility", NODEP_LABEL_LIST).orderIndependent().cfg(HOST) .nonconfigurable("test")) .add(attr(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST) .allowedFileTypes(FileTypeSet.NO_FILE)) .add(attr(RuleClass.RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST) .allowedFileTypes(FileTypeSet.NO_FILE)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("base") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRuleClasses.RootRule.class) .build(); } } /** * A rule that defines an aspect on one of its attributes. */ public static class AspectRequiringRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(SIMPLE_ASPECT)) .add(attr("bar", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(SIMPLE_ASPECT)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link AspectRequiringProvider} on one of its attributes. */ public static class AspectRequiringProviderRule implements RuleDefinition { private static final class TestAspectParametersExtractor implements Function<Rule, AspectParameters> { @Override public AspectParameters apply(Rule rule) { if (rule.isAttrDefined("baz", STRING)) { String value = rule.getAttributeContainer().getAttr("baz").toString(); if (!value.equals("")) { return new AspectParameters.Builder().addAttribute("baz", value).build(); } } return AspectParameters.EMPTY; } } @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(ASPECT_REQUIRING_PROVIDER, new TestAspectParametersExtractor())) .add(attr("baz", STRING)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("aspect_requiring_provider") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link AspectRequiringProviderSets} on one of its attributes. */ public static class AspectRequiringProviderSetsRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(ASPECT_REQUIRING_PROVIDER_SETS)) .add(attr("baz", STRING)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("aspect_requiring_provider_sets") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link ExtraAttributeAspect} on one of its attributes. */ public static class ExtraAttributeAspectRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(EXTRA_ATTRIBUTE_ASPECT)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("rule_with_extra_deps_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link ParametrizedDefinitionAspect} on one of its attributes. */ public static class ParametrizedDefinitionAspectRule implements RuleDefinition { private static final class TestAspectParametersExtractor implements Function<Rule, AspectParameters> { @Override public AspectParameters apply(Rule rule) { if (rule.isAttrDefined("baz", STRING)) { String value = rule.getAttributeContainer().getAttr("baz").toString(); if (!value.equals("")) { return new AspectParameters.Builder().addAttribute("baz", value).build(); } } return AspectParameters.EMPTY; } } @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add( attr("foo", LABEL_LIST) .allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(PARAMETRIZED_DEFINITION_ASPECT, new TestAspectParametersExtractor())) .add(attr("baz", STRING)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("parametrized_definition_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link ExtraAttributeAspectRequiringProvider} on one of its attributes. */ public static class ExtraAttributeAspectRequiringProviderRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(EXTRA_ATTRIBUTE_ASPECT_REQUIRING_PROVIDER)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("extra_attribute_aspect_requiring_provider") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link AllAttributesAspect} on one of its attributes. */ public static class AllAttributesAspectRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(ALL_ATTRIBUTES_ASPECT)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("all_attributes_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** A rule that defines an {@link AllAttributesWithToolAspect} on one of its attributes. */ public static class AllAttributesWithToolAspectRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add( attr("foo", LABEL_LIST) .allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(ALL_ATTRIBUTES_WITH_TOOL_ASPECT)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("all_attributes_with_tool_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines a {@link WarningAspect} on one of its attributes. */ public static class WarningAspectRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(WARNING_ASPECT)) .add(attr("bar", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("warning_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that defines an {@link ErrorAspect} on one of its attributes. */ public static class ErrorAspectRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE) .aspect(ERROR_ASPECT)) .add(attr("bar", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("error_aspect") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A simple rule that has an attribute. */ public static class SimpleRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .add(attr("foo1", LABEL).allowedFileTypes(FileTypeSet.ANY_FILE)) .add(attr("txt", STRING)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("simple") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that advertises a provider but doesn't implement it. */ public static class LiarRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .advertiseProvider(RequiredProvider.class) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("liar") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that advertises a provider and implements it. */ public static class HonestRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .advertiseProvider(RequiredProvider.class) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("honest") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * A rule that advertises another, different provider and implements it. */ public static class HonestRule2 implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("foo", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)) .advertiseProvider(RequiredProvider2.class) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("honest2") .factoryClass(DummyRuleFactory2.class) .ancestors(BaseRule.class) .build(); } } /** * Rule with an implcit dependency. */ public static class ImplicitDepRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("$dep", LABEL).value(Label.parseAbsoluteUnchecked("//extra:extra"))) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("implicit_dep") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * Rule with a late-bound dependency. */ public static class LateBoundDepRule implements RuleDefinition { private static final LateBoundLabelList<BuildConfiguration> PLUGINS_LABEL_LIST = new LateBoundLabelList<BuildConfiguration>() { @Override public List<Label> resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return configuration.getPlugins(); } }; @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr(":plugins", LABEL_LIST).value(PLUGINS_LABEL_LIST)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("late_bound_dep") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } private static class SetsCpuPatchTransition implements PatchTransition { @Override public BuildOptions apply(BuildOptions options) { BuildOptions result = options.clone(); result.get(Options.class).cpu = "SET BY PATCH"; return result; } @Override public boolean defaultsToSelf() { return true; } } private static class SetsCpuSplitTransition implements SplitTransition<BuildOptions> { @Override public List<BuildOptions> split(BuildOptions buildOptions) { BuildOptions result = buildOptions.clone(); result.get(Options.class).cpu = "SET BY SPLIT"; return ImmutableList.of(result); } @Override public boolean defaultsToSelf() { return true; } } private static class SetsHostCpuSplitTransition implements SplitTransition<BuildOptions> { @Override public List<BuildOptions> split(BuildOptions buildOptions) { BuildOptions result = buildOptions.clone(); result.get(Options.class).hostCpu = "SET BY SPLIT"; return ImmutableList.of(result); } @Override public boolean defaultsToSelf() { return true; } } private static class EmptySplitTransition implements SplitTransition<BuildOptions> { @Override public List<BuildOptions> split(BuildOptions buildOptions) { return ImmutableList.of(); } @Override public boolean defaultsToSelf() { return true; } } /** * Rule with a split transition on an attribute. */ public static class AttributeTransitionRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add(attr("without_transition", LABEL).allowedFileTypes(FileTypeSet.ANY_FILE)) .add(attr("with_cpu_transition", LABEL) .allowedFileTypes(FileTypeSet.ANY_FILE) .cfg(new SetsCpuSplitTransition())) .add(attr("with_host_cpu_transition", LABEL) .allowedFileTypes(FileTypeSet.ANY_FILE) .cfg(new SetsHostCpuSplitTransition())) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("attribute_transition") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** * 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 { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .cfg(new SetsCpuPatchTransition()) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("rule_class_transition") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } private static class SetsTestFilterFromAttributePatchTransition implements PatchTransition { private final String value; public SetsTestFilterFromAttributePatchTransition(String value) { this.value = value; } @Override public BuildOptions apply(BuildOptions options) { BuildOptions result = options.clone(); result.get(Options.class).testFilter = "SET BY PATCH FACTORY: " + value; return result; } @Override public boolean defaultsToSelf() { return true; } } private static class SetsTestFilterFromAttributeTransitionFactory implements RuleTransitionFactory { @Override public Transition buildTransitionFor(Rule rule) { NonconfigurableAttributeMapper attributes = NonconfigurableAttributeMapper.of(rule); String value = attributes.get("sets_test_filter_to", STRING); if (Strings.isNullOrEmpty(value)) { return null; } else { return new SetsTestFilterFromAttributePatchTransition(value); } } } /** * Rule with a RuleTransitionFactory which sets the --test_filter flag according to its attribute. */ public static class UsesRuleTransitionFactoryRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .cfg(new SetsTestFilterFromAttributeTransitionFactory()) .add( attr("sets_test_filter_to", STRING) .nonconfigurable("used in RuleTransitionFactory") .value("")) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("uses_rule_transition_factory") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** A rule with an empty split transition on an attribute. */ public static class EmptySplitRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .add( attr("with_empty_transition", LABEL) .allowedFileTypes(FileTypeSet.ANY_FILE) .cfg(new EmptySplitTransition())) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("empty_split") .factoryClass(DummyRuleFactory.class) .ancestors(BaseRule.class) .build(); } } /** Aspect that propagates over rule outputs. */ public static class AspectApplyingToFiles extends NativeAspectClass implements ConfiguredAspectFactory { /** Simple provider for testing */ @Immutable public static final class Provider implements TransitiveInfoProvider { private final Label label; private Provider(Label label) { this.label = label; } public Label getLabel() { return label; } } @Override public AspectDefinition getDefinition(AspectParameters aspectParameters) { return AspectDefinition.builder(this).applyToFiles(true).build(); } @Override public ConfiguredAspect create(ConfiguredTarget base, RuleContext context, AspectParameters parameters) throws InterruptedException { return ConfiguredAspect.builder(this, parameters, context) .addProvider(Provider.class, new Provider(base.getLabel())) .build(); } } }