| // 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; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.devtools.build.lib.analysis.BaseRuleClasses.ACTION_LISTENER; |
| import static com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode.TARGET; |
| import static com.google.devtools.build.lib.analysis.util.TestAspects.EMPTY_LATE_BOUND_LABEL; |
| 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 org.junit.Assert.fail; |
| |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.util.ActionsTestUtil.NullAction; |
| import com.google.devtools.build.lib.analysis.util.AnalysisTestCase; |
| import com.google.devtools.build.lib.analysis.util.TestAspects; |
| import com.google.devtools.build.lib.analysis.util.TestAspects.AspectInfo; |
| import com.google.devtools.build.lib.analysis.util.TestAspects.AspectRequiringRule; |
| import com.google.devtools.build.lib.analysis.util.TestAspects.BaseRule; |
| import com.google.devtools.build.lib.analysis.util.TestAspects.DummyRuleFactory; |
| import com.google.devtools.build.lib.analysis.util.TestAspects.RuleInfo; |
| import com.google.devtools.build.lib.cmdline.Label; |
| 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.events.OutputFilter.RegexOutputFilter; |
| import com.google.devtools.build.lib.packages.AspectDefinition; |
| import com.google.devtools.build.lib.packages.AspectParameters; |
| import com.google.devtools.build.lib.packages.NativeAspectClass; |
| import com.google.devtools.build.lib.packages.RuleClass; |
| import com.google.devtools.build.lib.vfs.ModifiedFileSet; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests for aspect creation and merging with configured targets. |
| * |
| * <p>Uses the complete analysis machinery and depends on custom rules so that behaviors related to |
| * aspects can be tested even if they aren't used by regular rules. |
| */ |
| @RunWith(JUnit4.class) |
| public class AspectTest extends AnalysisTestCase { |
| |
| private void pkg(String name, String... contents) throws Exception { |
| scratch.file("" + name + "/BUILD", contents); |
| } |
| |
| @Test |
| public void testAspectAppliedToAliasWithSelect() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule()); |
| pkg("a", |
| "aspect(name='a', foo=[':b'])", |
| "alias(name='b', actual=select({'//conditions:default': ':c'}))", |
| "base(name='c')"); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:c", "rule //a:a"); |
| } |
| |
| @Test |
| public void testAspectAppliedToChainedAliases() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule()); |
| pkg("a", |
| "aspect(name='a', foo=[':b'])", |
| "alias(name='b', actual=':c')", |
| "alias(name='c', actual=':d')", |
| "alias(name='d', actual=':e')", |
| "base(name='e')"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:e", "rule //a:a"); |
| } |
| |
| @Test |
| public void testAspectAppliedToChainedAliasesAndSelect() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule()); |
| pkg("a", |
| "aspect(name='a', foo=[':b'])", |
| "alias(name='b', actual=select({'//conditions:default': ':c'}))", |
| "alias(name='c', actual=select({'//conditions:default': ':d'}))", |
| "base(name='d')"); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:d", "rule //a:a"); |
| } |
| |
| @Test |
| public void providersOfAspectAreMergedIntoDependency() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule()); |
| pkg("a", |
| "aspect(name='a', foo=[':b'])", |
| "aspect(name='b', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:b", "rule //a:a"); |
| } |
| |
| @Test |
| public void aspectIsNotCreatedIfAdvertisedProviderIsNotPresent() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.LiarRule(), |
| new TestAspects.AspectRequiringProviderRule()); |
| |
| pkg("a", |
| "aspect_requiring_provider(name='a', foo=[':b'])", |
| "liar(name='b', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()).containsExactly("rule //a:a"); |
| } |
| |
| @Test |
| public void aspectCreationWorksThroughBind() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(), |
| new TestAspects.AspectRequiringProviderRule()); |
| |
| pkg("a", |
| "aspect_requiring_provider(name='a', foo=['//external:b'])", |
| "honest(name='b', foo=[])"); |
| |
| scratch.overwriteFile("WORKSPACE", |
| "bind(name='b', actual='//a:b')"); |
| |
| skyframeExecutor.invalidateFilesUnderPathForTesting(reporter, |
| ModifiedFileSet.EVERYTHING_MODIFIED, rootDirectory); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("rule //a:a", "aspect //a:b"); |
| } |
| |
| |
| @Test |
| public void aspectCreatedIfAdvertisedProviderIsPresent() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(), |
| new TestAspects.AspectRequiringProviderRule()); |
| |
| pkg("a", |
| "aspect_requiring_provider(name='a', foo=[':b'])", |
| "honest(name='b', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("rule //a:a", "aspect //a:b"); |
| } |
| |
| @Test |
| public void aspectCreatedIfAtLeastOneSetOfAdvertisedProvidersArePresent() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(), |
| new TestAspects.HonestRule2(), new TestAspects.AspectRequiringProviderSetsRule()); |
| |
| pkg("a", |
| "aspect_requiring_provider_sets(name='a', foo=[':b', ':c'])", |
| "honest(name='b', foo=[])", |
| "honest2(name='c', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("rule //a:a", "aspect //a:b", "aspect //a:c"); |
| } |
| |
| @Test |
| public void aspectWithParametrizedDefinition() throws Exception { |
| setRulesAvailableInTests( |
| new TestAspects.BaseRule(), |
| new TestAspects.HonestRule(), |
| new TestAspects.ParametrizedDefinitionAspectRule()); |
| |
| pkg( |
| "a", |
| "honest(name='q', foo=[])", |
| "parametrized_definition_aspect(name='a', foo=[':b'], baz='//a:q')", |
| "honest(name='c', foo=[])", |
| "honest(name='b', foo=[':c'])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(TestAspects.RuleInfo.class).getData()) |
| .containsExactly( |
| "rule //a:a", |
| "aspect //a:b data //a:q $dep:[ //a:q]", |
| "aspect //a:c data //a:q $dep:[ //a:q]"); |
| } |
| |
| @Test |
| public void aspectInError() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.ErrorAspectRule(), |
| new TestAspects.SimpleRule()); |
| |
| pkg("a", |
| "simple(name='a', foo=[':b'])", |
| "error_aspect(name='b', foo=[':c'])", |
| "simple(name='c')"); |
| |
| reporter.removeHandler(failFastHandler); |
| // getConfiguredTarget() uses a separate code path that does not hit |
| // SkyframeBuildView#configureTargets |
| try { |
| update("//a:a"); |
| fail(); |
| } catch (ViewCreationFailedException e) { |
| // expected |
| } |
| assertContainsEvent("Aspect error"); |
| } |
| |
| @Test |
| public void transitiveAspectInError() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.ErrorAspectRule(), |
| new TestAspects.SimpleRule()); |
| |
| pkg("a", |
| "error_aspect(name='a', foo=[':b'])", |
| "error_aspect(name='b', bar=[':c'])", |
| "error_aspect(name='c', bar=[':d'])", |
| "error_aspect(name='d')"); |
| |
| reporter.removeHandler(failFastHandler); |
| // getConfiguredTarget() uses a separate code path that does not hit |
| // SkyframeBuildView#configureTargets |
| try { |
| update("//a:a"); |
| fail(); |
| } catch (ViewCreationFailedException e) { |
| // expected |
| } |
| assertContainsEvent("Aspect error"); |
| } |
| |
| @Test |
| public void aspectDependenciesDontShowDeprecationWarnings() throws Exception { |
| setRulesAvailableInTests( |
| new TestAspects.BaseRule(), new TestAspects.ExtraAttributeAspectRule()); |
| |
| pkg("extra", "base(name='extra', deprecation='bad aspect')"); |
| |
| pkg("a", |
| "rule_with_extra_deps_aspect(name='a', foo=[':b'])", |
| "base(name='b')"); |
| |
| getConfiguredTarget("//a:a"); |
| assertContainsEventWithFrequency("bad aspect", 0); |
| } |
| |
| @Test |
| public void ruleDependencyDeprecationWarningsAbsentDuringAspectEvaluations() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.AspectRequiringRule()); |
| |
| pkg("a", "aspect(name='a', foo=['//b:b'])"); |
| pkg("b", "aspect(name='b', bar=['//d:d'])"); |
| pkg("d", "base(name='d', deprecation='bad rule')"); |
| |
| getConfiguredTarget("//a:a"); |
| assertContainsEventWithFrequency("bad rule", 1); |
| } |
| |
| @Test |
| public void aspectWarningsFilteredByOutputFiltersForAssociatedRules() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.WarningAspectRule()); |
| pkg("a", "warning_aspect(name='a', foo=['//b:b', '//c:c'])"); |
| pkg("b", "base(name='b')"); |
| pkg("c", "base(name='c')"); |
| |
| reporter.setOutputFilter(RegexOutputFilter.forPattern(Pattern.compile("^//b:"))); |
| |
| getConfiguredTarget("//a:a"); |
| assertContainsEventWithFrequency("Aspect warning on //b:b", 1); |
| assertContainsEventWithFrequency("Aspect warning on //c:c", 0); |
| } |
| |
| @Test |
| public void sameTargetInDifferentAttributes() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.AspectRequiringRule(), |
| new TestAspects.SimpleRule()); |
| pkg("a", |
| "aspect(name='a', foo=[':b'], bar=[':b'])", |
| "aspect(name='b', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:b", "rule //a:a"); |
| } |
| |
| @Test |
| public void informationFromBaseRulePassedToAspect() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(), |
| new TestAspects.AspectRequiringProviderRule()); |
| |
| pkg("a", |
| "aspect_requiring_provider(name='a', foo=[':b'], baz='hello')", |
| "honest(name='b', foo=[])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("rule //a:a", "aspect //a:b data hello"); |
| } |
| |
| /** |
| * Rule definitions to be used in emptyAspectAttributesAreAvailableInRuleContext(). |
| */ |
| public static class EmptyAspectAttributesAreAvailableInRuleContext { |
| public static class TestRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) { |
| return builder |
| .add(attr("foo", LABEL_LIST).legacyAllowAnyFileType() |
| .aspect(ASPECT_WITH_EMPTY_LATE_BOUND_ATTRIBUTE)) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder().name("testrule") |
| .factoryClass(DummyRuleFactory.class).ancestors(BaseRule.class).build(); |
| } |
| } |
| |
| public static class AspectWithEmptyLateBoundAttribute extends NativeAspectClass |
| implements ConfiguredAspectFactory { |
| @Override |
| public AspectDefinition getDefinition(AspectParameters params) { |
| return new AspectDefinition.Builder(this) |
| .add(attr(":late", LABEL).value(EMPTY_LATE_BOUND_LABEL)).build(); |
| } |
| |
| @Override |
| public ConfiguredAspect create( |
| ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) |
| throws InterruptedException { |
| Object lateBoundPrereq = ruleContext.getPrerequisite(":late", TARGET); |
| return new ConfiguredAspect.Builder(this, parameters, ruleContext) |
| .addProvider( |
| AspectInfo.class, |
| new AspectInfo( |
| NestedSetBuilder.create( |
| Order.STABLE_ORDER, lateBoundPrereq != null ? "non-empty" : "empty"))) |
| .build(); |
| } |
| } |
| public static final AspectWithEmptyLateBoundAttribute ASPECT_WITH_EMPTY_LATE_BOUND_ATTRIBUTE = |
| new AspectWithEmptyLateBoundAttribute(); |
| } |
| |
| /** |
| * An Aspect has a late-bound attribute with no value (that is, a LateBoundLabel whose |
| * getDefault() returns `null`). |
| * Test that this attribute is available in the RuleContext which is provided to the Aspect's |
| * `create()` method. |
| */ |
| @Test |
| public void emptyAspectAttributesAreAvailableInRuleContext() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), |
| new EmptyAspectAttributesAreAvailableInRuleContext.TestRule()); |
| pkg("a", |
| "testrule(name='a', foo=[':b'])", |
| "testrule(name='b')"); |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| assertThat(a.getProvider(RuleInfo.class).getData()).contains("empty"); |
| } |
| |
| /** |
| * Rule definitions to be used in extraActionsAreEmitted(). |
| */ |
| public static class ExtraActionsAreEmitted { |
| public static class TestRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) { |
| return builder |
| .add(attr("foo", LABEL_LIST).legacyAllowAnyFileType() |
| .aspect(ASPECT_THAT_REGISTERS_ACTION)) |
| .add(attr(":action_listener", LABEL_LIST).cfg(HOST).value(ACTION_LISTENER)) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder().name("testrule") |
| .factoryClass(DummyRuleFactory.class).ancestors(BaseRule.class).build(); |
| } |
| } |
| |
| public static class AspectThatRegistersAction extends NativeAspectClass |
| implements ConfiguredAspectFactory { |
| @Override |
| public AspectDefinition getDefinition(AspectParameters params) { |
| return new AspectDefinition.Builder(this).build(); |
| } |
| |
| |
| |
| @Override |
| public ConfiguredAspect create( |
| ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) |
| throws InterruptedException { |
| ruleContext.registerAction(new NullAction(ruleContext.createOutputArtifact())); |
| return new ConfiguredAspect.Builder(this, parameters, ruleContext).build(); |
| } |
| } |
| private static final AspectThatRegistersAction ASPECT_THAT_REGISTERS_ACTION = |
| new AspectThatRegistersAction(); |
| } |
| |
| /** |
| * Test that actions registered in an Aspect are reported as extra-actions on the attached rule. |
| * AspectThatRegistersAction registers a NullAction, whose mnemonic is "Null". We have an |
| * action_listener that targets that mnemonic, which makes sure the Aspect machinery will expose |
| * an ExtraActionArtifactsProvider. |
| * The rule //a:a doesn't have an aspect, so the only action we get is the one on //a:b |
| * (which does have an aspect). |
| */ |
| @Test |
| public void extraActionsAreEmitted() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), |
| new ExtraActionsAreEmitted.TestRule()); |
| useConfiguration("--experimental_action_listener=//extra_actions:listener"); |
| scratch.file( |
| "extra_actions/BUILD", |
| "extra_action(name='xa', cmd='echo dont-care')", |
| "action_listener(name='listener', mnemonics=['Null'], extra_actions=[':xa'])"); |
| pkg("a", |
| "testrule(name='a', foo=[':b'])", |
| "testrule(name='b')"); |
| update(); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:a"); |
| NestedSet<Artifact> extraActionArtifacts = |
| a.getProvider(ExtraActionArtifactsProvider.class).getTransitiveExtraActionArtifacts(); |
| for (Artifact artifact : extraActionArtifacts) { |
| assertThat(artifact.getOwnerLabel()).isEqualTo(Label.create("@//a", "b")); |
| } |
| } |
| |
| @Test |
| public void aspectPropagatesToAllAttributes() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), |
| new TestAspects.SimpleRule(), |
| new TestAspects.AllAttributesAspectRule()); |
| pkg("a", |
| "simple(name='a', foo=[':b'], foo1=':c', txt='some text')", |
| "simple(name='b', foo=[], txt='some text')", |
| "simple(name='c', foo=[], txt='more text')", |
| "all_attributes_aspect(name='x', foo=[':a'])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:x"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:a", "aspect //a:b", "aspect //a:c", "rule //a:x"); |
| } |
| |
| /** |
| * Tests that when --experimental_extra_action_top_level_only, Blaze reports extra-actions for |
| * actions registered by Aspects injected by a top-level rule. Because we can't know whether an |
| * aspect was injected by a top-level target or one of its children, we approximate it by only |
| * reporting extra-actions from Aspects that the top-level target could have injected. |
| * |
| * <p>Here, injector1() and injector2() inject aspects into their children. null_rule() just |
| * passes the aspects to its children. The test makes sure that actions registered by aspect1 |
| * (injected by injector1()) are reported to the extra-action mechanism. Actions registered by |
| * aspect2 (from injector2) are not reported, because the target under test (//x:a) doesn't inject |
| * aspect2. |
| */ |
| @Test |
| public void extraActionsAreEmitted_topLevel() throws Exception { |
| useConfiguration( |
| "--experimental_action_listener=//pkg1:listener", |
| "--experimental_extra_action_top_level_only"); |
| |
| scratch.file( |
| "x/BUILD", |
| "load(':extension.bzl', 'injector1', 'injector2', 'null_rule')", |
| "injector1(name='a', deps=[':b'])", |
| "null_rule(name='b', deps=[':c'])", |
| "null_rule(name='c', deps=[':d'])", |
| "injector2(name = 'd', extra_deps=[':e'])", |
| "null_rule(name = 'e')"); |
| |
| scratch.file( |
| "x/extension.bzl", |
| "def _aspect_impl(target, ctx):", |
| " ctx.empty_action(mnemonic='Mnemonic')", |
| " return struct()", |
| "aspect1 = aspect(_aspect_impl, attr_aspects=['deps'])", |
| "aspect2 = aspect(_aspect_impl, attr_aspects=['extra_deps'])", |
| "def _rule_impl(ctx):", |
| " return struct()", |
| "injector1 = rule(_rule_impl, attrs = { 'deps' : attr.label_list(aspects = [aspect1]) })", |
| "null_rule = rule(_rule_impl, attrs = { 'deps' : attr.label_list() })", |
| "injector2 = rule(", |
| " _rule_impl, attrs = { 'extra_deps' : attr.label_list(aspects = [aspect2]) })"); |
| |
| scratch.file( |
| "pkg1/BUILD", |
| "extra_action(name='xa', cmd='echo dont-care')", |
| "action_listener(name='listener', mnemonics=['Mnemonic'], extra_actions=[':xa'])"); |
| |
| // Sanity check: //x:d injects an aspect which produces some extra-action. |
| { |
| BuildView.AnalysisResult analysisResult = update("//x:d"); |
| |
| // Get owners of all extra-action artifacts. |
| List<Label> extraArtifactOwners = new ArrayList<>(); |
| for (Artifact artifact : analysisResult.getAdditionalArtifactsToBuild()) { |
| if (artifact.getRootRelativePathString().endsWith(".xa")) { |
| extraArtifactOwners.add(artifact.getOwnerLabel()); |
| } |
| } |
| assertThat(extraArtifactOwners).containsExactly(Label.create("@//x", "e")); |
| } |
| |
| // Actual test: //x:a reports actions registered by the aspect it injects. |
| { |
| BuildView.AnalysisResult analysisResult = update("//x:a"); |
| |
| // Get owners of all extra-action artifacts. |
| List<Label> extraArtifactOwners = new ArrayList<>(); |
| for (Artifact artifact : analysisResult.getAdditionalArtifactsToBuild()) { |
| if (artifact.getRootRelativePathString().endsWith(".xa")) { |
| extraArtifactOwners.add(artifact.getOwnerLabel()); |
| } |
| } |
| assertThat(extraArtifactOwners) |
| .containsExactly( |
| Label.create("@//x", "b"), Label.create("@//x", "c"), Label.create("@//x", "d")); |
| } |
| } |
| |
| @Test |
| public void aspectPropagatesToAllAttributesImplicit() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), |
| new TestAspects.SimpleRule(), |
| new TestAspects.ImplicitDepRule(), |
| new TestAspects.AllAttributesAspectRule()); |
| |
| scratch.file( |
| "extra/BUILD", |
| "simple(name ='extra')" |
| ); |
| pkg("a", |
| "simple(name='a', foo=[':b'], foo1=':c', txt='some text')", |
| "simple(name='b', foo=[], txt='some text')", |
| "implicit_dep(name='c')", |
| "all_attributes_aspect(name='x', foo=[':a'])"); |
| update(); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:x"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly( |
| "aspect //a:a", |
| "aspect //a:b", |
| "aspect //a:c", |
| "aspect //extra:extra", |
| "rule //a:x"); |
| } |
| |
| |
| @Test |
| public void aspectPropagatesToAllAttributesLateBound() throws Exception { |
| setRulesAvailableInTests(new TestAspects.BaseRule(), |
| new TestAspects.SimpleRule(), |
| new TestAspects.LateBoundDepRule(), |
| new TestAspects.AllAttributesAspectRule()); |
| |
| scratch.file( |
| "extra/BUILD", |
| "simple(name ='extra')" |
| ); |
| pkg("a", |
| "simple(name='a', foo=[':b'], foo1=':c', txt='some text')", |
| "simple(name='b', foo=[], txt='some text')", |
| "late_bound_dep(name='c')", |
| "all_attributes_aspect(name='x', foo=[':a'])"); |
| useConfiguration("--plugin=//extra:extra"); |
| update(); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:x"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly( |
| "aspect //a:a", |
| "aspect //a:b", |
| "aspect //a:c", |
| "aspect //extra:extra", |
| "rule //a:x"); |
| } |
| |
| /** |
| * Ensures an aspect with attr = '*' doesn't try to propagate to its own implicit attributes. |
| * Doing so leads to a dependency cycle. |
| */ |
| @Test |
| public void aspectWithAllAttributesDoesNotPropagateToOwnImplicitAttributes() throws Exception { |
| setRulesAvailableInTests( |
| new TestAspects.BaseRule(), |
| new TestAspects.SimpleRule(), |
| new TestAspects.AllAttributesWithToolAspectRule()); |
| pkg( |
| "a", |
| "simple(name='tool')", |
| "simple(name='a')", |
| "all_attributes_with_tool_aspect(name='x', foo=[':a'])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:x"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly("aspect //a:a", "rule //a:x"); |
| } |
| |
| /** |
| * Makes sure the aspect *will* propagate to its implicit attributes if there is a "regular" |
| * dependency path to it (i.e. not through its own implicit attributes). |
| */ |
| @Test |
| public void aspectWithAllAttributesPropagatesToItsToolIfThereIsPath() throws Exception { |
| setRulesAvailableInTests( |
| new TestAspects.BaseRule(), |
| new TestAspects.SimpleRule(), |
| new TestAspects.AllAttributesWithToolAspectRule()); |
| pkg( |
| "a", |
| "simple(name='tool')", |
| "simple(name='a', foo=[':b'], foo1=':c', txt='some text')", |
| "simple(name='b', foo=[], txt='some text')", |
| "simple(name='c', foo=[':tool'], txt='more text')", |
| "all_attributes_with_tool_aspect(name='x', foo=[':a'])"); |
| |
| ConfiguredTarget a = getConfiguredTarget("//a:x"); |
| assertThat(a.getProvider(RuleInfo.class).getData()) |
| .containsExactly( |
| "aspect //a:a", "aspect //a:b", "aspect //a:c", "aspect //a:tool", "rule //a:x"); |
| } |
| } |