blob: 76d24e09d4d9aac6de69df7f02b55ce3d868181b [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Ulf Adams89179252015-04-23 18:48:39 +00002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.analysis;
15
16import static com.google.common.truth.Truth.assertThat;
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +000017import static com.google.devtools.build.lib.analysis.BaseRuleClasses.ACTION_LISTENER;
Carmi Grushko06f65f72015-11-02 22:42:24 +000018import static com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode.TARGET;
19import static com.google.devtools.build.lib.analysis.util.TestAspects.EMPTY_LATE_BOUND_LABEL;
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +000020import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
Carmi Grushko06f65f72015-11-02 22:42:24 +000021import static com.google.devtools.build.lib.packages.Attribute.attr;
22import static com.google.devtools.build.lib.packages.BuildType.LABEL;
23import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
Florian Weikertfd735f32015-11-27 17:32:23 +000024import static org.junit.Assert.fail;
Ulf Adams89179252015-04-23 18:48:39 +000025
Carmi Grushkodf9e5e12016-11-08 23:07:57 +000026import com.google.devtools.build.lib.actions.Artifact;
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +000027import com.google.devtools.build.lib.actions.util.ActionsTestUtil.NullAction;
Florian Weikertcca703a2015-12-07 09:56:38 +000028import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
Ulf Adams89179252015-04-23 18:48:39 +000029import com.google.devtools.build.lib.analysis.util.TestAspects;
Carmi Grushko06f65f72015-11-02 22:42:24 +000030import com.google.devtools.build.lib.analysis.util.TestAspects.AspectInfo;
Ulf Adams89179252015-04-23 18:48:39 +000031import com.google.devtools.build.lib.analysis.util.TestAspects.AspectRequiringRule;
Carmi Grushko06f65f72015-11-02 22:42:24 +000032import com.google.devtools.build.lib.analysis.util.TestAspects.BaseRule;
33import com.google.devtools.build.lib.analysis.util.TestAspects.DummyRuleFactory;
34import com.google.devtools.build.lib.analysis.util.TestAspects.RuleInfo;
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +000035import com.google.devtools.build.lib.cmdline.Label;
36import com.google.devtools.build.lib.collect.nestedset.NestedSet;
Carmi Grushko06f65f72015-11-02 22:42:24 +000037import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
38import com.google.devtools.build.lib.collect.nestedset.Order;
Michael Staib2707a882016-09-16 21:06:40 +000039import com.google.devtools.build.lib.events.OutputFilter.RegexOutputFilter;
Carmi Grushko06f65f72015-11-02 22:42:24 +000040import com.google.devtools.build.lib.packages.AspectDefinition;
41import com.google.devtools.build.lib.packages.AspectParameters;
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +000042import com.google.devtools.build.lib.packages.NativeAspectClass;
Carmi Grushko06f65f72015-11-02 22:42:24 +000043import com.google.devtools.build.lib.packages.RuleClass;
Lukacs Berki75250e62016-04-12 13:48:24 +000044import com.google.devtools.build.lib.vfs.ModifiedFileSet;
Carmi Grushkodf9e5e12016-11-08 23:07:57 +000045import java.util.ArrayList;
46import java.util.List;
Michael Staib2707a882016-09-16 21:06:40 +000047import java.util.regex.Pattern;
Ulf Adams89179252015-04-23 18:48:39 +000048import org.junit.Test;
49import org.junit.runner.RunWith;
50import org.junit.runners.JUnit4;
51
52/**
53 * Tests for aspect creation and merging with configured targets.
54 *
55 * <p>Uses the complete analysis machinery and depends on custom rules so that behaviors related to
56 * aspects can be tested even if they aren't used by regular rules.
57 */
58@RunWith(JUnit4.class)
Florian Weikertcca703a2015-12-07 09:56:38 +000059public class AspectTest extends AnalysisTestCase {
Ulf Adams89179252015-04-23 18:48:39 +000060
Ulf Adams89179252015-04-23 18:48:39 +000061 private void pkg(String name, String... contents) throws Exception {
Ulf Adams01f1b462015-04-27 09:52:33 +000062 scratch.file("" + name + "/BUILD", contents);
Ulf Adams89179252015-04-23 18:48:39 +000063 }
64
65 @Test
Lukacs Berkiea988b62016-08-30 12:26:18 +000066 public void testAspectAppliedToAliasWithSelect() throws Exception {
67 setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule());
68 pkg("a",
69 "aspect(name='a', foo=[':b'])",
70 "alias(name='b', actual=select({'//conditions:default': ':c'}))",
71 "base(name='c')");
72 ConfiguredTarget a = getConfiguredTarget("//a:a");
73 assertThat(a.getProvider(RuleInfo.class).getData())
74 .containsExactly("aspect //a:c", "rule //a:a");
75 }
76
77 @Test
78 public void testAspectAppliedToChainedAliases() throws Exception {
79 setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule());
80 pkg("a",
81 "aspect(name='a', foo=[':b'])",
82 "alias(name='b', actual=':c')",
83 "alias(name='c', actual=':d')",
84 "alias(name='d', actual=':e')",
85 "base(name='e')");
86
87 ConfiguredTarget a = getConfiguredTarget("//a:a");
88 assertThat(a.getProvider(RuleInfo.class).getData())
89 .containsExactly("aspect //a:e", "rule //a:a");
90 }
91
92 @Test
93 public void testAspectAppliedToChainedAliasesAndSelect() throws Exception {
94 setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule());
95 pkg("a",
96 "aspect(name='a', foo=[':b'])",
97 "alias(name='b', actual=select({'//conditions:default': ':c'}))",
98 "alias(name='c', actual=select({'//conditions:default': ':d'}))",
99 "base(name='d')");
100 ConfiguredTarget a = getConfiguredTarget("//a:a");
101 assertThat(a.getProvider(RuleInfo.class).getData())
102 .containsExactly("aspect //a:d", "rule //a:a");
103 }
104
105 @Test
Ulf Adams89179252015-04-23 18:48:39 +0000106 public void providersOfAspectAreMergedIntoDependency() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000107 setRulesAvailableInTests(new TestAspects.BaseRule(), new AspectRequiringRule());
Ulf Adams89179252015-04-23 18:48:39 +0000108 pkg("a",
109 "aspect(name='a', foo=[':b'])",
110 "aspect(name='b', foo=[])");
111
112 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushko06f65f72015-11-02 22:42:24 +0000113 assertThat(a.getProvider(RuleInfo.class).getData())
Ulf Adams89179252015-04-23 18:48:39 +0000114 .containsExactly("aspect //a:b", "rule //a:a");
115 }
116
117 @Test
118 public void aspectIsNotCreatedIfAdvertisedProviderIsNotPresent() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000119 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.LiarRule(),
Ulf Adams89179252015-04-23 18:48:39 +0000120 new TestAspects.AspectRequiringProviderRule());
121
122 pkg("a",
123 "aspect_requiring_provider(name='a', foo=[':b'])",
124 "liar(name='b', foo=[])");
125
126 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushko06f65f72015-11-02 22:42:24 +0000127 assertThat(a.getProvider(RuleInfo.class).getData()).containsExactly("rule //a:a");
Ulf Adams89179252015-04-23 18:48:39 +0000128 }
129
Lukacs Berki549bfce2016-04-22 15:29:12 +0000130 @Test
Lukacs Berki75250e62016-04-12 13:48:24 +0000131 public void aspectCreationWorksThroughBind() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000132 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
Lukacs Berki75250e62016-04-12 13:48:24 +0000133 new TestAspects.AspectRequiringProviderRule());
134
135 pkg("a",
136 "aspect_requiring_provider(name='a', foo=['//external:b'])",
137 "honest(name='b', foo=[])");
138
139 scratch.overwriteFile("WORKSPACE",
140 "bind(name='b', actual='//a:b')");
141
142 skyframeExecutor.invalidateFilesUnderPathForTesting(reporter,
143 ModifiedFileSet.EVERYTHING_MODIFIED, rootDirectory);
144
145 ConfiguredTarget a = getConfiguredTarget("//a:a");
146 assertThat(a.getProvider(RuleInfo.class).getData())
147 .containsExactly("rule //a:a", "aspect //a:b");
148 }
149
150
Ulf Adams89179252015-04-23 18:48:39 +0000151 @Test
152 public void aspectCreatedIfAdvertisedProviderIsPresent() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000153 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
Ulf Adams89179252015-04-23 18:48:39 +0000154 new TestAspects.AspectRequiringProviderRule());
155
156 pkg("a",
157 "aspect_requiring_provider(name='a', foo=[':b'])",
158 "honest(name='b', foo=[])");
159
160 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushko06f65f72015-11-02 22:42:24 +0000161 assertThat(a.getProvider(RuleInfo.class).getData())
Ulf Adams89179252015-04-23 18:48:39 +0000162 .containsExactly("rule //a:a", "aspect //a:b");
163 }
164
165 @Test
Rumou Duan6f8393f2016-11-30 16:03:10 +0000166 public void aspectCreatedIfAtLeastOneSetOfAdvertisedProvidersArePresent() throws Exception {
167 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
168 new TestAspects.HonestRule2(), new TestAspects.AspectRequiringProviderSetsRule());
169
170 pkg("a",
171 "aspect_requiring_provider_sets(name='a', foo=[':b', ':c'])",
172 "honest(name='b', foo=[])",
173 "honest2(name='c', foo=[])");
174
175 ConfiguredTarget a = getConfiguredTarget("//a:a");
176 assertThat(a.getProvider(RuleInfo.class).getData())
177 .containsExactly("rule //a:a", "aspect //a:b", "aspect //a:c");
178 }
179
180 @Test
Dmitry Lomov6231d082015-11-02 17:17:20 +0000181 public void aspectWithParametrizedDefinition() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000182 setRulesAvailableInTests(
Dmitry Lomov6231d082015-11-02 17:17:20 +0000183 new TestAspects.BaseRule(),
184 new TestAspects.HonestRule(),
185 new TestAspects.ParametrizedDefinitionAspectRule());
186
187 pkg(
188 "a",
189 "honest(name='q', foo=[])",
190 "parametrized_definition_aspect(name='a', foo=[':b'], baz='//a:q')",
191 "honest(name='c', foo=[])",
192 "honest(name='b', foo=[':c'])");
193
194 ConfiguredTarget a = getConfiguredTarget("//a:a");
195 assertThat(a.getProvider(TestAspects.RuleInfo.class).getData())
196 .containsExactly(
197 "rule //a:a",
198 "aspect //a:b data //a:q $dep:[ //a:q]",
199 "aspect //a:c data //a:q $dep:[ //a:q]");
200 }
201
202 @Test
Ulf Adams89179252015-04-23 18:48:39 +0000203 public void aspectInError() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000204 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.ErrorAspectRule(),
Ulf Adams89179252015-04-23 18:48:39 +0000205 new TestAspects.SimpleRule());
206
207 pkg("a",
208 "simple(name='a', foo=[':b'])",
209 "error_aspect(name='b', foo=[':c'])",
210 "simple(name='c')");
211
212 reporter.removeHandler(failFastHandler);
213 // getConfiguredTarget() uses a separate code path that does not hit
214 // SkyframeBuildView#configureTargets
215 try {
216 update("//a:a");
217 fail();
218 } catch (ViewCreationFailedException e) {
219 // expected
220 }
221 assertContainsEvent("Aspect error");
222 }
Marian Lobur2099da02015-05-12 14:42:01 +0000223
224 @Test
Marian Loburfc567b32015-09-14 08:44:25 +0000225 public void transitiveAspectInError() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000226 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.ErrorAspectRule(),
Marian Loburfc567b32015-09-14 08:44:25 +0000227 new TestAspects.SimpleRule());
228
229 pkg("a",
230 "error_aspect(name='a', foo=[':b'])",
231 "error_aspect(name='b', bar=[':c'])",
232 "error_aspect(name='c', bar=[':d'])",
233 "error_aspect(name='d')");
234
235 reporter.removeHandler(failFastHandler);
236 // getConfiguredTarget() uses a separate code path that does not hit
237 // SkyframeBuildView#configureTargets
238 try {
239 update("//a:a");
240 fail();
241 } catch (ViewCreationFailedException e) {
242 // expected
243 }
244 assertContainsEvent("Aspect error");
245 }
246
247 @Test
Michael Staib2707a882016-09-16 21:06:40 +0000248 public void aspectDependenciesDontShowDeprecationWarnings() throws Exception {
249 setRulesAvailableInTests(
250 new TestAspects.BaseRule(), new TestAspects.ExtraAttributeAspectRule());
251
252 pkg("extra", "base(name='extra', deprecation='bad aspect')");
253
254 pkg("a",
255 "rule_with_extra_deps_aspect(name='a', foo=[':b'])",
256 "base(name='b')");
257
258 getConfiguredTarget("//a:a");
259 assertContainsEventWithFrequency("bad aspect", 0);
260 }
261
262 @Test
263 public void ruleDependencyDeprecationWarningsAbsentDuringAspectEvaluations() throws Exception {
264 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.AspectRequiringRule());
265
266 pkg("a", "aspect(name='a', foo=['//b:b'])");
267 pkg("b", "aspect(name='b', bar=['//d:d'])");
268 pkg("d", "base(name='d', deprecation='bad rule')");
269
270 getConfiguredTarget("//a:a");
271 assertContainsEventWithFrequency("bad rule", 1);
272 }
273
274 @Test
275 public void aspectWarningsFilteredByOutputFiltersForAssociatedRules() throws Exception {
276 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.WarningAspectRule());
277 pkg("a", "warning_aspect(name='a', foo=['//b:b', '//c:c'])");
278 pkg("b", "base(name='b')");
279 pkg("c", "base(name='c')");
280
281 reporter.setOutputFilter(RegexOutputFilter.forPattern(Pattern.compile("^//b:")));
282
283 getConfiguredTarget("//a:a");
284 assertContainsEventWithFrequency("Aspect warning on //b:b", 1);
285 assertContainsEventWithFrequency("Aspect warning on //c:c", 0);
286 }
287
288 @Test
Marian Lobur2099da02015-05-12 14:42:01 +0000289 public void sameTargetInDifferentAttributes() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000290 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.AspectRequiringRule(),
Marian Lobur2099da02015-05-12 14:42:01 +0000291 new TestAspects.SimpleRule());
292 pkg("a",
293 "aspect(name='a', foo=[':b'], bar=[':b'])",
294 "aspect(name='b', foo=[])");
295
296 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushko06f65f72015-11-02 22:42:24 +0000297 assertThat(a.getProvider(RuleInfo.class).getData())
Marian Lobur2099da02015-05-12 14:42:01 +0000298 .containsExactly("aspect //a:b", "rule //a:a");
299 }
Marian Lobur702cad72015-09-02 09:53:58 +0000300
301 @Test
302 public void informationFromBaseRulePassedToAspect() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000303 setRulesAvailableInTests(new TestAspects.BaseRule(), new TestAspects.HonestRule(),
Marian Lobur702cad72015-09-02 09:53:58 +0000304 new TestAspects.AspectRequiringProviderRule());
305
306 pkg("a",
307 "aspect_requiring_provider(name='a', foo=[':b'], baz='hello')",
308 "honest(name='b', foo=[])");
309
310 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushko06f65f72015-11-02 22:42:24 +0000311 assertThat(a.getProvider(RuleInfo.class).getData())
Marian Lobur702cad72015-09-02 09:53:58 +0000312 .containsExactly("rule //a:a", "aspect //a:b data hello");
313 }
Marian Lobur86bd4fd2015-09-16 10:01:38 +0000314
Carmi Grushko06f65f72015-11-02 22:42:24 +0000315 /**
316 * Rule definitions to be used in emptyAspectAttributesAreAvailableInRuleContext().
317 */
318 public static class EmptyAspectAttributesAreAvailableInRuleContext {
319 public static class TestRule implements RuleDefinition {
320 @Override
321 public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
322 return builder
323 .add(attr("foo", LABEL_LIST).legacyAllowAnyFileType()
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000324 .aspect(ASPECT_WITH_EMPTY_LATE_BOUND_ATTRIBUTE))
Carmi Grushko06f65f72015-11-02 22:42:24 +0000325 .build();
326 }
327
328 @Override
329 public Metadata getMetadata() {
330 return RuleDefinition.Metadata.builder().name("testrule")
331 .factoryClass(DummyRuleFactory.class).ancestors(BaseRule.class).build();
332 }
333 }
334
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000335 public static class AspectWithEmptyLateBoundAttribute extends NativeAspectClass
336 implements ConfiguredAspectFactory {
Carmi Grushko06f65f72015-11-02 22:42:24 +0000337 @Override
338 public AspectDefinition getDefinition(AspectParameters params) {
Dmitry Lomovdce01702016-11-28 15:51:32 +0000339 return new AspectDefinition.Builder(this)
Carmi Grushko06f65f72015-11-02 22:42:24 +0000340 .add(attr(":late", LABEL).value(EMPTY_LATE_BOUND_LABEL)).build();
341 }
342
343 @Override
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000344 public ConfiguredAspect create(
345 ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters)
346 throws InterruptedException {
Carmi Grushko06f65f72015-11-02 22:42:24 +0000347 Object lateBoundPrereq = ruleContext.getPrerequisite(":late", TARGET);
Dmitry Lomovdce01702016-11-28 15:51:32 +0000348 return new ConfiguredAspect.Builder(this, parameters, ruleContext)
Carmi Grushko06f65f72015-11-02 22:42:24 +0000349 .addProvider(
Carmi Grushko261f5bb2015-12-09 19:38:38 +0000350 AspectInfo.class,
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000351 new AspectInfo(
352 NestedSetBuilder.create(
353 Order.STABLE_ORDER, lateBoundPrereq != null ? "non-empty" : "empty")))
Carmi Grushko06f65f72015-11-02 22:42:24 +0000354 .build();
355 }
356 }
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000357 public static final AspectWithEmptyLateBoundAttribute ASPECT_WITH_EMPTY_LATE_BOUND_ATTRIBUTE =
358 new AspectWithEmptyLateBoundAttribute();
Carmi Grushko06f65f72015-11-02 22:42:24 +0000359 }
360
361 /**
362 * An Aspect has a late-bound attribute with no value (that is, a LateBoundLabel whose
363 * getDefault() returns `null`).
364 * Test that this attribute is available in the RuleContext which is provided to the Aspect's
365 * `create()` method.
366 */
367 @Test
368 public void emptyAspectAttributesAreAvailableInRuleContext() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000369 setRulesAvailableInTests(new TestAspects.BaseRule(),
Carmi Grushko06f65f72015-11-02 22:42:24 +0000370 new EmptyAspectAttributesAreAvailableInRuleContext.TestRule());
371 pkg("a",
372 "testrule(name='a', foo=[':b'])",
373 "testrule(name='b')");
374 ConfiguredTarget a = getConfiguredTarget("//a:a");
375 assertThat(a.getProvider(RuleInfo.class).getData()).contains("empty");
376 }
377
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000378 /**
379 * Rule definitions to be used in extraActionsAreEmitted().
380 */
381 public static class ExtraActionsAreEmitted {
382 public static class TestRule implements RuleDefinition {
383 @Override
384 public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
385 return builder
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000386 .add(attr("foo", LABEL_LIST).legacyAllowAnyFileType()
387 .aspect(ASPECT_THAT_REGISTERS_ACTION))
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000388 .add(attr(":action_listener", LABEL_LIST).cfg(HOST).value(ACTION_LISTENER))
389 .build();
390 }
391
392 @Override
393 public Metadata getMetadata() {
394 return RuleDefinition.Metadata.builder().name("testrule")
395 .factoryClass(DummyRuleFactory.class).ancestors(BaseRule.class).build();
396 }
397 }
398
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000399 public static class AspectThatRegistersAction extends NativeAspectClass
400 implements ConfiguredAspectFactory {
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000401 @Override
402 public AspectDefinition getDefinition(AspectParameters params) {
Dmitry Lomovdce01702016-11-28 15:51:32 +0000403 return new AspectDefinition.Builder(this).build();
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000404 }
405
Dmitry Lomovdce01702016-11-28 15:51:32 +0000406
407
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000408 @Override
409 public ConfiguredAspect create(
410 ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters)
411 throws InterruptedException {
412 ruleContext.registerAction(new NullAction(ruleContext.createOutputArtifact()));
Dmitry Lomovdce01702016-11-28 15:51:32 +0000413 return new ConfiguredAspect.Builder(this, parameters, ruleContext).build();
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000414 }
415 }
Luis Fernando Pino Duquee82713d2016-04-26 16:22:38 +0000416 private static final AspectThatRegistersAction ASPECT_THAT_REGISTERS_ACTION =
417 new AspectThatRegistersAction();
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000418 }
419
420 /**
421 * Test that actions registered in an Aspect are reported as extra-actions on the attached rule.
422 * AspectThatRegistersAction registers a NullAction, whose mnemonic is "Null". We have an
423 * action_listener that targets that mnemonic, which makes sure the Aspect machinery will expose
424 * an ExtraActionArtifactsProvider.
425 * The rule //a:a doesn't have an aspect, so the only action we get is the one on //a:b
426 * (which does have an aspect).
427 */
428 @Test
429 public void extraActionsAreEmitted() throws Exception {
Dmitry Lomovd1260742016-05-03 18:37:11 +0000430 setRulesAvailableInTests(new TestAspects.BaseRule(),
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000431 new ExtraActionsAreEmitted.TestRule());
432 useConfiguration("--experimental_action_listener=//extra_actions:listener");
433 scratch.file(
434 "extra_actions/BUILD",
435 "extra_action(name='xa', cmd='echo dont-care')",
436 "action_listener(name='listener', mnemonics=['Null'], extra_actions=[':xa'])");
437 pkg("a",
438 "testrule(name='a', foo=[':b'])",
439 "testrule(name='b')");
440 update();
441
442 ConfiguredTarget a = getConfiguredTarget("//a:a");
Carmi Grushkobabd4852016-11-18 17:58:09 +0000443 NestedSet<Artifact> extraActionArtifacts =
444 a.getProvider(ExtraActionArtifactsProvider.class).getTransitiveExtraActionArtifacts();
445 for (Artifact artifact : extraActionArtifacts) {
446 assertThat(artifact.getOwnerLabel()).isEqualTo(Label.create("@//a", "b"));
447 }
Carmi Grushkoeaaa9d0d2015-11-17 01:54:45 +0000448 }
Dmitry Lomovbb5901b2016-09-27 08:49:26 +0000449
450 @Test
451 public void aspectPropagatesToAllAttributes() throws Exception {
452 setRulesAvailableInTests(new TestAspects.BaseRule(),
453 new TestAspects.SimpleRule(),
454 new TestAspects.AllAttributesAspectRule());
455 pkg("a",
456 "simple(name='a', foo=[':b'], foo1=':c', txt='some text')",
457 "simple(name='b', foo=[], txt='some text')",
458 "simple(name='c', foo=[], txt='more text')",
459 "all_attributes_aspect(name='x', foo=[':a'])");
460
461 ConfiguredTarget a = getConfiguredTarget("//a:x");
462 assertThat(a.getProvider(RuleInfo.class).getData())
463 .containsExactly("aspect //a:a", "aspect //a:b", "aspect //a:c", "rule //a:x");
464 }
465
Carmi Grushkodf9e5e12016-11-08 23:07:57 +0000466 /**
467 * Tests that when --experimental_extra_action_top_level_only, Blaze reports extra-actions for
468 * actions registered by Aspects injected by a top-level rule. Because we can't know whether an
469 * aspect was injected by a top-level target or one of its children, we approximate it by only
470 * reporting extra-actions from Aspects that the top-level target could have injected.
471 *
472 * <p>Here, injector1() and injector2() inject aspects into their children. null_rule() just
473 * passes the aspects to its children. The test makes sure that actions registered by aspect1
474 * (injected by injector1()) are reported to the extra-action mechanism. Actions registered by
475 * aspect2 (from injector2) are not reported, because the target under test (//x:a) doesn't inject
476 * aspect2.
477 */
478 @Test
479 public void extraActionsAreEmitted_topLevel() throws Exception {
480 useConfiguration(
481 "--experimental_action_listener=//pkg1:listener",
482 "--experimental_extra_action_top_level_only");
483
484 scratch.file(
485 "x/BUILD",
486 "load(':extension.bzl', 'injector1', 'injector2', 'null_rule')",
487 "injector1(name='a', deps=[':b'])",
488 "null_rule(name='b', deps=[':c'])",
489 "null_rule(name='c', deps=[':d'])",
490 "injector2(name = 'd', extra_deps=[':e'])",
491 "null_rule(name = 'e')");
492
493 scratch.file(
494 "x/extension.bzl",
495 "def _aspect_impl(target, ctx):",
496 " ctx.empty_action(mnemonic='Mnemonic')",
497 " return struct()",
498 "aspect1 = aspect(_aspect_impl, attr_aspects=['deps'])",
499 "aspect2 = aspect(_aspect_impl, attr_aspects=['extra_deps'])",
500 "def _rule_impl(ctx):",
501 " return struct()",
502 "injector1 = rule(_rule_impl, attrs = { 'deps' : attr.label_list(aspects = [aspect1]) })",
503 "null_rule = rule(_rule_impl, attrs = { 'deps' : attr.label_list() })",
504 "injector2 = rule(",
505 " _rule_impl, attrs = { 'extra_deps' : attr.label_list(aspects = [aspect2]) })");
506
507 scratch.file(
508 "pkg1/BUILD",
509 "extra_action(name='xa', cmd='echo dont-care')",
510 "action_listener(name='listener', mnemonics=['Mnemonic'], extra_actions=[':xa'])");
511
512 // Sanity check: //x:d injects an aspect which produces some extra-action.
513 {
514 BuildView.AnalysisResult analysisResult = update("//x:d");
515
516 // Get owners of all extra-action artifacts.
517 List<Label> extraArtifactOwners = new ArrayList<>();
518 for (Artifact artifact : analysisResult.getAdditionalArtifactsToBuild()) {
519 if (artifact.getRootRelativePathString().endsWith(".xa")) {
520 extraArtifactOwners.add(artifact.getOwnerLabel());
521 }
522 }
523 assertThat(extraArtifactOwners).containsExactly(Label.create("@//x", "e"));
524 }
525
526 // Actual test: //x:a reports actions registered by the aspect it injects.
527 {
528 BuildView.AnalysisResult analysisResult = update("//x:a");
529
530 // Get owners of all extra-action artifacts.
531 List<Label> extraArtifactOwners = new ArrayList<>();
532 for (Artifact artifact : analysisResult.getAdditionalArtifactsToBuild()) {
533 if (artifact.getRootRelativePathString().endsWith(".xa")) {
534 extraArtifactOwners.add(artifact.getOwnerLabel());
535 }
536 }
537 assertThat(extraArtifactOwners)
538 .containsExactly(
539 Label.create("@//x", "b"), Label.create("@//x", "c"), Label.create("@//x", "d"));
540 }
541 }
542
Dmitry Lomovbb5901b2016-09-27 08:49:26 +0000543 @Test
Carmi Grushko905a29d2016-12-09 20:42:29 +0000544 public void extraActionsFromDifferentAspectsDontConflict() throws Exception {
545 useConfiguration(
546 "--experimental_action_listener=//pkg1:listener",
547 "--experimental_extra_action_top_level_only");
548
549 scratch.file(
550 "x/BUILD",
551 "load(':extension.bzl', 'injector1', 'injector2', 'null_rule')",
Dmitry Lomov15756522016-12-16 16:52:37 +0000552 "injector2(name='i2_a', deps = [':i1_a'])",
Carmi Grushko905a29d2016-12-09 20:42:29 +0000553 "injector1(name='i1_a', deps=[':n'], param = 'a')",
554 "injector1(name='i1_b', deps=[':n'], param = 'b')",
555 "injector2(name='i2', deps=[':n'])",
556 "null_rule(name = 'n')");
557
558 scratch.file(
559 "x/extension.bzl",
560 "def _aspect_impl(target, ctx):",
561 " ctx.empty_action(mnemonic='Mnemonic')",
562 " return struct()",
563 "aspect1 = aspect(_aspect_impl, attr_aspects=['deps'], attrs =",
564 " {'param': attr.string(values = ['a', 'b'])})",
565 "aspect2 = aspect(_aspect_impl, attr_aspects=['deps'])",
566 "def _rule_impl(ctx):",
567 " return struct()",
568 "injector1 = rule(_rule_impl, attrs =",
569 " { 'deps' : attr.label_list(aspects = [aspect1]), 'param' : attr.string() })",
570 "injector2 = rule(_rule_impl, attrs = { 'deps' : attr.label_list(aspects = [aspect2]) })",
571 "null_rule = rule(_rule_impl, attrs = { 'deps' : attr.label_list() })"
572 );
573
574 scratch.file(
575 "pkg1/BUILD",
576 "extra_action(name='xa', cmd='echo dont-care')",
577 "action_listener(name='listener', mnemonics=['Mnemonic'], extra_actions=[':xa'])");
578
Dmitry Lomov15756522016-12-16 16:52:37 +0000579 update("//x:i1_a", "//x:i1_b", "//x:i2", "//x:i2_a");
Carmi Grushko905a29d2016-12-09 20:42:29 +0000580
581 // Implicitly check that update() didn't throw an exception because of two actions producing
582 // the same outputs.
583 }
584
585 @Test
Dmitry Lomovbb5901b2016-09-27 08:49:26 +0000586 public void aspectPropagatesToAllAttributesImplicit() throws Exception {
587 setRulesAvailableInTests(new TestAspects.BaseRule(),
588 new TestAspects.SimpleRule(),
589 new TestAspects.ImplicitDepRule(),
590 new TestAspects.AllAttributesAspectRule());
591
592 scratch.file(
593 "extra/BUILD",
594 "simple(name ='extra')"
595 );
596 pkg("a",
597 "simple(name='a', foo=[':b'], foo1=':c', txt='some text')",
598 "simple(name='b', foo=[], txt='some text')",
599 "implicit_dep(name='c')",
600 "all_attributes_aspect(name='x', foo=[':a'])");
601 update();
602
603 ConfiguredTarget a = getConfiguredTarget("//a:x");
604 assertThat(a.getProvider(RuleInfo.class).getData())
605 .containsExactly(
606 "aspect //a:a",
607 "aspect //a:b",
608 "aspect //a:c",
609 "aspect //extra:extra",
610 "rule //a:x");
611 }
612
613
614 @Test
615 public void aspectPropagatesToAllAttributesLateBound() throws Exception {
616 setRulesAvailableInTests(new TestAspects.BaseRule(),
617 new TestAspects.SimpleRule(),
618 new TestAspects.LateBoundDepRule(),
619 new TestAspects.AllAttributesAspectRule());
620
621 scratch.file(
622 "extra/BUILD",
623 "simple(name ='extra')"
624 );
625 pkg("a",
626 "simple(name='a', foo=[':b'], foo1=':c', txt='some text')",
627 "simple(name='b', foo=[], txt='some text')",
628 "late_bound_dep(name='c')",
629 "all_attributes_aspect(name='x', foo=[':a'])");
630 useConfiguration("--plugin=//extra:extra");
631 update();
632
633 ConfiguredTarget a = getConfiguredTarget("//a:x");
634 assertThat(a.getProvider(RuleInfo.class).getData())
635 .containsExactly(
636 "aspect //a:a",
637 "aspect //a:b",
638 "aspect //a:c",
639 "aspect //extra:extra",
640 "rule //a:x");
641 }
642
Googler909910c2016-11-04 15:50:51 +0000643 /**
644 * Ensures an aspect with attr = '*' doesn't try to propagate to its own implicit attributes.
645 * Doing so leads to a dependency cycle.
646 */
647 @Test
648 public void aspectWithAllAttributesDoesNotPropagateToOwnImplicitAttributes() throws Exception {
649 setRulesAvailableInTests(
650 new TestAspects.BaseRule(),
651 new TestAspects.SimpleRule(),
652 new TestAspects.AllAttributesWithToolAspectRule());
653 pkg(
654 "a",
655 "simple(name='tool')",
656 "simple(name='a')",
657 "all_attributes_with_tool_aspect(name='x', foo=[':a'])");
658
659 ConfiguredTarget a = getConfiguredTarget("//a:x");
660 assertThat(a.getProvider(RuleInfo.class).getData())
661 .containsExactly("aspect //a:a", "rule //a:x");
662 }
663
664 /**
665 * Makes sure the aspect *will* propagate to its implicit attributes if there is a "regular"
666 * dependency path to it (i.e. not through its own implicit attributes).
667 */
668 @Test
669 public void aspectWithAllAttributesPropagatesToItsToolIfThereIsPath() throws Exception {
670 setRulesAvailableInTests(
671 new TestAspects.BaseRule(),
672 new TestAspects.SimpleRule(),
673 new TestAspects.AllAttributesWithToolAspectRule());
674 pkg(
675 "a",
676 "simple(name='tool')",
677 "simple(name='a', foo=[':b'], foo1=':c', txt='some text')",
678 "simple(name='b', foo=[], txt='some text')",
679 "simple(name='c', foo=[':tool'], txt='more text')",
680 "all_attributes_with_tool_aspect(name='x', foo=[':a'])");
681
682 ConfiguredTarget a = getConfiguredTarget("//a:x");
683 assertThat(a.getProvider(RuleInfo.class).getData())
684 .containsExactly(
685 "aspect //a:a", "aspect //a:b", "aspect //a:c", "aspect //a:tool", "rule //a:x");
686 }
Dmitry Lomov65fde002017-02-07 17:24:04 +0000687
688 @Test
689 public void aspectTruthInAdvertisement() throws Exception {
690 reporter.removeHandler(failFastHandler); // expect errors
691 setRulesAvailableInTests(
692 new TestAspects.BaseRule(),
693 new TestAspects.SimpleRule(),
694 new TestAspects.FalseAdvertisementAspectRule());
695 pkg(
696 "a",
697 "simple(name = 's')",
698 "false_advertisement_aspect(name = 'x', deps = [':s'])"
699 );
700 try {
701 update("//a:x");
702 } catch (ViewCreationFailedException e) {
703 // expected.
704 }
705 assertContainsEvent(
706 "Aspect 'FalseAdvertisementAspect', applied to '//a:s',"
707 + " does not provide advertised provider 'RequiredProvider'");
708 assertContainsEvent(
709 "Aspect 'FalseAdvertisementAspect', applied to '//a:s',"
710 + " does not provide advertised provider 'advertised_provider'");
711 }
Ulf Adams89179252015-04-23 18:48:39 +0000712}