blob: 25d833c2c21c8ccca1b47a77030c9d79f7c62152 [file] [log] [blame]
Dmitry Lomov021a3652015-11-23 14:55:13 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
2//
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.
14
15package com.google.devtools.build.lib.analysis;
16
17import static com.google.common.truth.Truth.assertThat;
18import static com.google.devtools.build.lib.testutil.MoreAsserts.assertEventCount;
Janak Ramakrishnan2655e6d2016-08-18 18:20:53 +000019import static com.google.devtools.build.lib.testutil.MoreAsserts.assertEventCountAtLeast;
Florian Weikert79bd2372015-11-26 15:42:24 +000020import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertFalse;
22import static org.junit.Assert.assertNotNull;
23import static org.junit.Assert.assertNull;
24import static org.junit.Assert.assertSame;
25import static org.junit.Assert.assertTrue;
26import static org.junit.Assert.fail;
Dmitry Lomov021a3652015-11-23 14:55:13 +000027
28import com.google.common.base.Function;
29import com.google.common.collect.ImmutableList;
30import com.google.common.collect.ImmutableSet;
31import com.google.common.collect.Iterables;
32import com.google.common.collect.Lists;
33import com.google.common.collect.Sets;
34import com.google.common.eventbus.EventBus;
35import com.google.common.truth.Truth;
36import com.google.devtools.build.lib.actions.Action;
37import com.google.devtools.build.lib.actions.Actions;
38import com.google.devtools.build.lib.actions.Artifact;
39import com.google.devtools.build.lib.actions.FailAction;
Ulf Adams58581a32016-01-25 15:54:15 +000040import com.google.devtools.build.lib.analysis.BuildView.AnalysisResult;
Greg Estren373e3e22016-08-09 22:36:51 +000041import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
Dmitry Lomov021a3652015-11-23 14:55:13 +000042import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
Ulf Adams3e34a112016-02-09 16:31:06 +000043import com.google.devtools.build.lib.analysis.util.AnalysisMock;
Dmitry Lomov021a3652015-11-23 14:55:13 +000044import com.google.devtools.build.lib.analysis.util.BuildViewTestBase;
Greg Estren42c49352016-06-08 20:12:40 +000045import com.google.devtools.build.lib.analysis.util.ExpectedDynamicConfigurationErrors;
Dmitry Lomov021a3652015-11-23 14:55:13 +000046import com.google.devtools.build.lib.cmdline.Label;
Dmitry Lomov021a3652015-11-23 14:55:13 +000047import com.google.devtools.build.lib.packages.Attribute;
48import com.google.devtools.build.lib.packages.Rule;
Dmitry Lomov021a3652015-11-23 14:55:13 +000049import com.google.devtools.build.lib.skyframe.SkyFunctions;
50import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey;
Greg Estren247ac162016-08-10 20:50:09 +000051import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
Dmitry Lomov021a3652015-11-23 14:55:13 +000052import com.google.devtools.build.lib.testutil.Suite;
53import com.google.devtools.build.lib.testutil.TestSpec;
54import com.google.devtools.build.lib.testutil.TestUtils;
55import com.google.devtools.build.lib.util.Pair;
56import com.google.devtools.build.lib.vfs.Path;
57import com.google.devtools.build.lib.vfs.PathFragment;
Janak Ramakrishnancc7712f2016-07-08 17:38:27 +000058import com.google.devtools.build.skyframe.NotifyingHelper.EventType;
59import com.google.devtools.build.skyframe.NotifyingHelper.Listener;
60import com.google.devtools.build.skyframe.NotifyingHelper.Order;
Dmitry Lomov021a3652015-11-23 14:55:13 +000061import com.google.devtools.build.skyframe.SkyKey;
62import com.google.devtools.build.skyframe.TrackingAwaiter;
Dmitry Lomov021a3652015-11-23 14:55:13 +000063import java.util.Collection;
64import java.util.LinkedHashSet;
65import java.util.concurrent.CountDownLatch;
66import java.util.concurrent.TimeUnit;
67import java.util.regex.Pattern;
Lukacs Berki120b8632016-07-28 10:18:21 +000068import org.junit.Test;
69import org.junit.runner.RunWith;
70import org.junit.runners.JUnit4;
Dmitry Lomov021a3652015-11-23 14:55:13 +000071
72/**
73 * Tests for the {@link BuildView}.
74 */
75@TestSpec(size = Suite.SMALL_TESTS)
Florian Weikert79bd2372015-11-26 15:42:24 +000076@RunWith(JUnit4.class)
Ulf Adams2ac20962016-02-01 13:04:54 +000077public class BuildViewTest extends BuildViewTestBase {
Ulf Adams47838c92016-02-02 18:12:44 +000078 private static final Function<AnalysisFailureEvent, Pair<String, String>>
79 ANALYSIS_EVENT_TO_STRING_PAIR = new Function<AnalysisFailureEvent, Pair<String, String>>() {
80 @Override
81 public Pair<String, String> apply(AnalysisFailureEvent event) {
82 return Pair.of(
83 event.getFailedTarget().getLabel().toString(), event.getFailureReason().toString());
84 }
85 };
Dmitry Lomov021a3652015-11-23 14:55:13 +000086
Florian Weikert79bd2372015-11-26 15:42:24 +000087 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +000088 public void testRuleConfiguredTarget() throws Exception {
89 scratch.file("pkg/BUILD",
90 "genrule(name='foo', ",
91 " cmd = '',",
92 " srcs=['a.src'],",
93 " outs=['a.out'])");
94 update("//pkg:foo");
95 Rule ruleTarget = (Rule) getTarget("//pkg:foo");
96 assertEquals("genrule", ruleTarget.getRuleClass());
97
98 ConfiguredTarget ruleCT = getConfiguredTarget("//pkg:foo");
99
100 assertSame(ruleTarget, ruleCT.getTarget());
101 }
102
Florian Weikert79bd2372015-11-26 15:42:24 +0000103 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000104 public void testFilterByTargets() throws Exception {
105 scratch.file("tests/BUILD",
106 "sh_test(name = 'small_test_1',",
107 " srcs = ['small_test_1.sh'],",
108 " data = [':xUnit'],",
109 " size = 'small',",
110 " tags = ['tag1'])",
111 "",
112 "sh_test(name = 'small_test_2',",
113 " srcs = ['small_test_2.sh'],",
114 " size = 'small',",
115 " tags = ['tag2'])",
116 "",
117 "",
118 "test_suite( name = 'smallTests', tags=['small'])");
119 //scratch.file("tests/small_test_1.py");
120
121 update("//tests:smallTests");
122
123 ConfiguredTarget test1 = getConfiguredTarget("//tests:small_test_1");
124 ConfiguredTarget test2 = getConfiguredTarget("//tests:small_test_2");
125 ConfiguredTarget suite = getConfiguredTarget("//tests:smallTests");
126 assertNoEvents(); // start from a clean slate
127
128
129 Collection<ConfiguredTarget> targets =
130 new LinkedHashSet<>(ImmutableList.of(test1, test2, suite));
131 targets = Lists.newArrayList(
132 BuildView.filterTestsByTargets(targets,
133 Sets.newHashSet(test1.getTarget(), suite.getTarget())));
134 assertThat(targets).containsExactlyElementsIn(Sets.newHashSet(test1, suite));
135 }
136
Florian Weikert79bd2372015-11-26 15:42:24 +0000137 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000138 public void testSourceArtifact() throws Exception {
139 setupDummyRule();
140 update("//pkg:a.src");
141 InputFileConfiguredTarget inputCT = getInputFileConfiguredTarget("//pkg:a.src");
142 Artifact inputArtifact = inputCT.getArtifact();
143 assertNull(getGeneratingAction(inputArtifact));
144 assertEquals("pkg/a.src", inputArtifact.getExecPathString());
145 }
146
Florian Weikert79bd2372015-11-26 15:42:24 +0000147 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000148 public void testGeneratedArtifact() throws Exception {
149 setupDummyRule();
150 update("//pkg:a.out");
151 OutputFileConfiguredTarget outputCT = (OutputFileConfiguredTarget)
152 getConfiguredTarget("//pkg:a.out");
153 Artifact outputArtifact = outputCT.getArtifact();
Kristina Chodorowf8a1ae62016-08-11 14:44:40 +0000154 assertEquals(
155 outputCT.getConfiguration().getBinDirectory(
156 outputCT.getTarget().getLabel().getPackageIdentifier().getRepository()),
157 outputArtifact.getRoot());
Greg Estren7971e672016-06-01 23:22:48 +0000158 assertEquals(outputCT.getConfiguration().getBinFragment().getRelative("pkg/a.out"),
Dmitry Lomov021a3652015-11-23 14:55:13 +0000159 outputArtifact.getExecPath());
160 assertEquals(new PathFragment("pkg/a.out"), outputArtifact.getRootRelativePath());
161
162 Action action = getGeneratingAction(outputArtifact);
163 assertSame(FailAction.class, action.getClass());
164 }
165
Florian Weikertfd735f32015-11-27 17:32:23 +0000166 @Test
Ulf Adamsf8ff07a2016-01-22 14:56:19 +0000167 public void testReportsAnalysisRootCauses() throws Exception {
168 scratch.file("private/BUILD",
169 "genrule(",
170 " name='private',",
171 " outs=['private.out'],",
172 " cmd='',",
173 " visibility=['//visibility:private'])");
174 scratch.file("foo/BUILD",
175 "genrule(",
176 " name='foo',",
177 " tools=[':bar'],",
178 " outs=['foo.out'],",
179 " cmd='')",
180 "genrule(",
181 " name='bar',",
182 " tools=['//private'],",
183 " outs=['bar.out'],",
184 " cmd='')");
Dmitry Lomov021a3652015-11-23 14:55:13 +0000185
186 reporter.removeHandler(failFastHandler);
187 EventBus eventBus = new EventBus();
188 AnalysisFailureRecorder recorder = new AnalysisFailureRecorder();
189 eventBus.register(recorder);
Ulf Adams58581a32016-01-25 15:54:15 +0000190 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//foo");
191 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000192 assertThat(recorder.events).hasSize(1);
193 AnalysisFailureEvent event = recorder.events.get(0);
Ulf Adamsf8ff07a2016-01-22 14:56:19 +0000194 assertEquals("//foo:bar", event.getFailureReason().toString());
195 assertEquals("//foo:foo", event.getFailedTarget().getLabel().toString());
Dmitry Lomov021a3652015-11-23 14:55:13 +0000196 }
197
Florian Weikert79bd2372015-11-26 15:42:24 +0000198 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000199 public void testReportsLoadingRootCauses() throws Exception {
Ulf Adams47838c92016-02-02 18:12:44 +0000200 // This test checks that two simultaneous errors are both reported:
201 // - missing outs attribute,
202 // - package referenced in tools does not exist
Dmitry Lomov021a3652015-11-23 14:55:13 +0000203 scratch.file("pkg/BUILD",
204 "genrule(name='foo',",
205 " tools=['//nopackage:missing'],",
206 " cmd='')");
207
208 reporter.removeHandler(failFastHandler);
209 EventBus eventBus = new EventBus();
210 LoadingFailureRecorder recorder = new LoadingFailureRecorder();
211 eventBus.register(recorder);
212 // Note: no need to run analysis for a loading failure.
Ulf Adams58581a32016-01-25 15:54:15 +0000213 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//pkg:foo");
214 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000215 assertThat(recorder.events)
216 .contains(
217 Pair.of(Label.parseAbsolute("//pkg:foo"), Label.parseAbsolute("//nopackage:missing")));
218 assertContainsEvent("missing value for mandatory attribute 'outs'");
219 assertContainsEvent("no such package 'nopackage'");
220 // Skyframe correctly reports the other root cause as the genrule itself (since it is
221 // missing attributes).
222 assertThat(recorder.events).hasSize(2);
223 assertThat(recorder.events)
224 .contains(Pair.of(Label.parseAbsolute("//pkg:foo"), Label.parseAbsolute("//pkg:foo")));
225 }
226
Florian Weikert79bd2372015-11-26 15:42:24 +0000227 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000228 public void testConvolutedLoadRootCauseAnalysis() throws Exception {
229 // You need license declarations in third_party. We use this constraint to
230 // create targets that are loadable, but are in error.
231 scratch.file("third_party/first/BUILD",
232 "sh_library(name='first', deps=['//third_party/second'], licenses=['notice'])");
233 scratch.file("third_party/second/BUILD",
234 "sh_library(name='second', deps=['//third_party/third'], licenses=['notice'])");
235 scratch.file("third_party/third/BUILD",
236 "sh_library(name='third', deps=['//third_party/fourth'], licenses=['notice'])");
237 scratch.file("third_party/fourth/BUILD",
238 "sh_library(name='fourth', deps=['//third_party/fifth'])");
239 scratch.file("third_party/fifth/BUILD",
240 "sh_library(name='fifth', licenses=['notice'])");
241 reporter.removeHandler(failFastHandler);
242 EventBus eventBus = new EventBus();
243 LoadingFailureRecorder recorder = new LoadingFailureRecorder();
244 eventBus.register(recorder);
245 // Note: no need to run analysis for a loading failure.
Ulf Adams58581a32016-01-25 15:54:15 +0000246 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING),
Dmitry Lomov021a3652015-11-23 14:55:13 +0000247 "//third_party/first", "//third_party/third");
Ulf Adams58581a32016-01-25 15:54:15 +0000248 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000249 assertThat(recorder.events).hasSize(2);
250 assertTrue(recorder.events.toString(), recorder.events.contains(
251 Pair.of(Label.parseAbsolute("//third_party/first"),
252 Label.parseAbsolute("//third_party/fourth"))));
253 assertThat(recorder.events)
254 .contains(Pair.of(
255 Label.parseAbsolute("//third_party/third"),
256 Label.parseAbsolute("//third_party/fourth")));
257 }
258
Florian Weikert79bd2372015-11-26 15:42:24 +0000259 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000260 public void testMultipleRootCauseReporting() throws Exception {
261 scratch.file("gp/BUILD",
262 "sh_library(name = 'gp', deps = ['//p:p'])");
263 scratch.file("p/BUILD",
264 "sh_library(name = 'p', deps = ['//c1:not', '//c2:not'])");
265 scratch.file("c1/BUILD");
266 scratch.file("c2/BUILD");
267 reporter.removeHandler(failFastHandler);
268 EventBus eventBus = new EventBus();
269 LoadingFailureRecorder recorder = new LoadingFailureRecorder();
270 eventBus.register(recorder);
Ulf Adams58581a32016-01-25 15:54:15 +0000271 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//gp");
272 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000273 assertThat(recorder.events).hasSize(2);
274 assertTrue(recorder.events.toString(), recorder.events.contains(
275 Pair.of(Label.parseAbsolute("//gp"),
276 Label.parseAbsolute("//c1:not"))));
277 assertThat(recorder.events)
278 .contains(Pair.of(Label.parseAbsolute("//gp"), Label.parseAbsolute("//c2:not")));
279 }
280
281 /**
282 * Regression test for: "Package group includes are broken"
283 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000284 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000285 public void testTopLevelPackageGroup() throws Exception {
286 scratch.file("tropical/BUILD",
287 "package_group(name='guava', includes=[':mango'])",
288 "package_group(name='mango')");
289
290 // If the analysis phase results in an error, this will throw an exception
291 update("//tropical:guava");
292
293 // Check if the included package group also got analyzed
294 assertNotNull(getConfiguredTarget("//tropical:mango", null));
295 }
296
Florian Weikert79bd2372015-11-26 15:42:24 +0000297 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000298 public void testTopLevelInputFile() throws Exception {
299 scratch.file("tropical/BUILD",
300 "exports_files(['file.txt'])");
301 update("//tropical:file.txt");
302 assertNotNull(getConfiguredTarget("//tropical:file.txt", null));
303 }
304
Florian Weikert79bd2372015-11-26 15:42:24 +0000305 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000306 public void testGetDirectPrerequisites() throws Exception {
307 scratch.file(
308 "package/BUILD",
309 "filegroup(name='top', srcs=[':inner', 'file'])",
310 "sh_binary(name='inner', srcs=['script.sh'])");
311 update("//package:top");
312 ConfiguredTarget top = getConfiguredTarget("//package:top", getTargetConfiguration());
313 Iterable<ConfiguredTarget> targets = getView().getDirectPrerequisitesForTesting(
314 reporter, top, getBuildConfigurationCollection());
315 Iterable<Label> labels =
316 Iterables.transform(
317 targets,
318 new Function<ConfiguredTarget, Label>() {
319 @Override
320 public Label apply(ConfiguredTarget target) {
321 return target.getLabel();
322 }
323 });
324 assertThat(labels)
325 .containsExactly(
326 Label.parseAbsolute("//package:inner"), Label.parseAbsolute("//package:file"));
327 }
328
Florian Weikert79bd2372015-11-26 15:42:24 +0000329 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000330 public void testGetDirectPrerequisiteDependencies() throws Exception {
331 scratch.file(
332 "package/BUILD",
333 "filegroup(name='top', srcs=[':inner', 'file'])",
334 "sh_binary(name='inner', srcs=['script.sh'])");
335 update("//package:top");
336 ConfiguredTarget top = getConfiguredTarget("//package:top", getTargetConfiguration());
337 Iterable<Dependency> targets = getView().getDirectPrerequisiteDependenciesForTesting(
Ulf Adamsa4ca6372016-01-11 14:20:28 +0000338 reporter, top, getBuildConfigurationCollection()).values();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000339
340 Dependency innerDependency;
341 Dependency fileDependency;
342 if (top.getConfiguration().useDynamicConfigurations()) {
343 innerDependency =
Michael Staib5e573fd2016-01-27 00:33:29 +0000344 Dependency.withTransitionAndAspects(
Dmitry Lomov021a3652015-11-23 14:55:13 +0000345 Label.parseAbsolute("//package:inner"),
346 Attribute.ConfigurationTransition.NONE,
Dmitry Lomove8040172016-04-06 14:53:43 +0000347 ImmutableSet.<AspectDescriptor>of());
Dmitry Lomov021a3652015-11-23 14:55:13 +0000348 } else {
349 innerDependency =
Michael Staib5e573fd2016-01-27 00:33:29 +0000350 Dependency.withConfiguration(
Dmitry Lomov021a3652015-11-23 14:55:13 +0000351 Label.parseAbsolute("//package:inner"),
Michael Staib5e573fd2016-01-27 00:33:29 +0000352 getTargetConfiguration());
Dmitry Lomov021a3652015-11-23 14:55:13 +0000353 }
Greg Estren9e26f0f2016-09-29 01:01:57 +0000354 fileDependency =
355 Dependency.withNullConfiguration(
356 Label.parseAbsolute("//package:file"));
Dmitry Lomov021a3652015-11-23 14:55:13 +0000357
358 assertThat(targets).containsExactly(innerDependency, fileDependency);
359 }
360
361 /**
362 * Tests that the {@code --configuration short name} option cannot be used on
363 * the command line.
364 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000365 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000366 public void testConfigurationShortName() throws Exception {
367 useConfiguration("--output directory name=foo");
368 reporter.removeHandler(failFastHandler);
369 try {
370 update(defaultFlags());
371 fail();
372 } catch (InvalidConfigurationException e) {
373 assertThat(e).hasMessage("Build options are invalid");
374 assertContainsEvent(
375 "The internal '--output directory name' option cannot be used on the command line");
376 }
377 }
378
Florian Weikert79bd2372015-11-26 15:42:24 +0000379 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000380 public void testFileTranslations() throws Exception {
381 scratch.file("foo/file");
382 scratch.file("foo/BUILD",
383 "exports_files(['file'])");
384 useConfiguration("--message_translations=//foo:file");
385 scratch.file("bar/BUILD",
386 "sh_library(name = 'bar')");
387 update("//bar");
388 }
389
390 // Regression test: "output_filter broken (but in a different way)"
Florian Weikert79bd2372015-11-26 15:42:24 +0000391 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000392 public void testOutputFilterSeeWarning() throws Exception {
393 runAnalysisWithOutputFilter(Pattern.compile(".*"));
394 assertContainsEvent("please do not import '//java/a:A.java'");
395 }
396
397 // Regression test: "output_filter broken (but in a different way)"
Florian Weikert79bd2372015-11-26 15:42:24 +0000398 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000399 public void testOutputFilter() throws Exception {
400 runAnalysisWithOutputFilter(Pattern.compile("^//java/c"));
401 assertNoEvents();
402 }
403
Florian Weikert79bd2372015-11-26 15:42:24 +0000404 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000405 public void testAnalysisErrorMessageWithKeepGoing() throws Exception {
406 scratch.file("a/BUILD", "sh_binary(name='a', srcs=['a1.sh', 'a2.sh'])");
407 reporter.removeHandler(failFastHandler);
Ulf Adams58581a32016-01-25 15:54:15 +0000408 AnalysisResult result = update(defaultFlags().with(Flag.KEEP_GOING), "//a");
409 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000410 assertContainsEvent("errors encountered while analyzing target '//a:a'");
411 }
412
413 /**
414 * Regression test: Exception in ConfiguredTargetGraph.checkForCycles()
415 * when multiple top-level targets depend on the same cycle.
416 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000417 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000418 public void testCircularDependencyBelowTwoTargets() throws Exception {
419 scratch.file("foo/BUILD",
420 "sh_library(name = 'top1', srcs = ['top1.sh'], deps = [':rec1'])",
421 "sh_library(name = 'top2', srcs = ['top2.sh'], deps = [':rec1'])",
422 "sh_library(name = 'rec1', srcs = ['rec1.sh'], deps = [':rec2'])",
423 "sh_library(name = 'rec2', srcs = ['rec2.sh'], deps = [':rec1'])"
424 );
425 reporter.removeHandler(failFastHandler);
Ulf Adams58581a32016-01-25 15:54:15 +0000426 AnalysisResult result =
427 update(defaultFlags().with(Flag.KEEP_GOING), "//foo:top1", "//foo:top2");
428 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000429 assertContainsEvent("in sh_library rule //foo:rec1: cycle in dependency graph:\n");
430 assertContainsEvent("in sh_library rule //foo:top");
431 }
432
433 // Regression test: cycle node depends on error.
Florian Weikert79bd2372015-11-26 15:42:24 +0000434 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000435 public void testErrorBelowCycle() throws Exception {
436 scratch.file("foo/BUILD",
437 "sh_library(name = 'top', deps = ['mid'])",
438 "sh_library(name = 'mid', deps = ['bad', 'cycle1'])",
439 "sh_library(name = 'bad', srcs = ['//badbuild:isweird'])",
440 "sh_library(name = 'cycle1', deps = ['cycle2', 'mid'])",
441 "sh_library(name = 'cycle2', deps = ['cycle1'])");
442 scratch.file("badbuild/BUILD", "");
443 reporter.removeHandler(failFastHandler);
Janak Ramakrishnan2655e6d2016-08-18 18:20:53 +0000444 injectGraphListenerForTesting(
445 new Listener() {
446 @Override
447 public void accept(SkyKey key, EventType type, Order order, Object context) {}
448 },
449 /*deterministic=*/ true);
Dmitry Lomov021a3652015-11-23 14:55:13 +0000450 try {
451 update("//foo:top");
452 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +0000453 } catch (ViewCreationFailedException e) {
Dmitry Lomov021a3652015-11-23 14:55:13 +0000454 // Expected.
455 }
456 assertContainsEvent("no such target '//badbuild:isweird': target 'isweird' not declared in "
457 + "package 'badbuild'");
458 assertContainsEvent("and referenced by '//foo:bad'");
Janak Ramakrishnan2655e6d2016-08-18 18:20:53 +0000459 assertContainsEvent("in sh_library rule //foo");
460 assertContainsEvent("cycle in dependency graph");
461 assertEventCountAtLeast(2, eventCollector);
Dmitry Lomov021a3652015-11-23 14:55:13 +0000462 }
463
Florian Weikert79bd2372015-11-26 15:42:24 +0000464 @Test
Ulf Adams47838c92016-02-02 18:12:44 +0000465 public void testErrorBelowCycleKeepGoing() throws Exception {
466 scratch.file("foo/BUILD",
467 "sh_library(name = 'top', deps = ['mid'])",
468 "sh_library(name = 'mid', deps = ['bad', 'cycle1'])",
469 "sh_library(name = 'bad', srcs = ['//badbuild:isweird'])",
470 "sh_library(name = 'cycle1', deps = ['cycle2', 'mid'])",
471 "sh_library(name = 'cycle2', deps = ['cycle1'])");
472 scratch.file("badbuild/BUILD", "");
473 reporter.removeHandler(failFastHandler);
474 update(defaultFlags().with(Flag.KEEP_GOING), "//foo:top");
475 assertContainsEvent("no such target '//badbuild:isweird': target 'isweird' not declared in "
476 + "package 'badbuild'");
477 assertContainsEvent("and referenced by '//foo:bad'");
478 assertContainsEvent("in sh_library rule //foo");
479 assertContainsEvent("cycle in dependency graph");
Greg Estren2f5ca772016-06-17 00:02:06 +0000480 // Dynamic configurations trigger this error both in configuration trimming (which visits
481 // the transitive target closure) and in the normal configured target cycle detection path.
482 // So we get an additional instance of this check (which varies depending on whether Skyframe
483 // loading phase is enabled).
484 // TODO(gregce): refactor away this variation. Note that the duplicate doesn't make it into
485 // real user output (it only affects tests).
486 if (!getTargetConfiguration().useDynamicConfigurations()) {
487 assertEventCount(3, eventCollector);
488 }
Ulf Adams47838c92016-02-02 18:12:44 +0000489 }
490
491 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000492 public void testAnalysisEntryHasActionsEvenWithError() throws Exception {
493 scratch.file("foo/BUILD",
494 "cc_binary(name = 'foo', linkshared = 1, srcs = ['foo.cc'])");
495 reporter.removeHandler(failFastHandler);
496 try {
497 update("//foo:foo");
498 fail(); // Expected ViewCreationFailedException.
499 } catch (ViewCreationFailedException e) {
500 // ok.
501 }
502 }
503
Florian Weikert79bd2372015-11-26 15:42:24 +0000504 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000505 public void testHelpfulErrorForWrongPackageLabels() throws Exception {
506 reporter.removeHandler(failFastHandler);
507
508 scratch.file("x/BUILD",
509 "cc_library(name='x', srcs=['x.cc'])");
510 scratch.file("y/BUILD",
511 "cc_library(name='y', srcs=['y.cc'], deps=['//x:z'])");
512
Ulf Adams58581a32016-01-25 15:54:15 +0000513 AnalysisResult result = update(defaultFlags().with(Flag.KEEP_GOING), "//y:y");
514 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000515 assertContainsEvent("no such target '//x:z': "
516 + "target 'z' not declared in package 'x' "
517 + "defined by /workspace/x/BUILD and referenced by '//y:y'");
518 }
519
Florian Weikert79bd2372015-11-26 15:42:24 +0000520 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000521 public void testNewActionsAreDifferentAndDontConflict() throws Exception {
522 scratch.file("pkg/BUILD",
523 "genrule(name='a', ",
524 " cmd='',",
525 " outs=['a.out'])");
526 update("//pkg:a.out");
527 OutputFileConfiguredTarget outputCT = (OutputFileConfiguredTarget)
528 getConfiguredTarget("//pkg:a.out");
529 Artifact outputArtifact = outputCT.getArtifact();
530 Action action = getGeneratingAction(outputArtifact);
531 assertNotNull(action);
532 scratch.overwriteFile("pkg/BUILD",
533 "genrule(name='a', ",
534 " cmd='false',",
535 " outs=['a.out'])");
536 update("//pkg:a.out");
537 assertFalse("Actions should not be compatible",
538 Actions.canBeShared(action, getGeneratingAction(outputArtifact)));
539 }
540
541 /**
542 * This test exercises the case where we invalidate (mark dirty) a node in one build command
543 * invocation and the revalidation (because it did not change) happens in a subsequent build
544 * command call.
545 *
546 * - In the first update call we construct A.
547 *
548 * - Then we construct B and we make the glob get invalidated. We do that by deleting a file
549 * because it depends on the directory listing. Because of that A gets invalidated.
550 *
551 * - Then we construct A again. The glob gets revalidated because it is still matching just A.java
552 * and A configured target gets revalidated too. At the end of the analysis A java action should
553 * be in the action graph.
554 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000555 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000556 public void testMultiBuildInvalidationRevalidation() throws Exception {
557 scratch.file("java/a/A.java", "bla1");
558 scratch.file("java/a/C.java", "bla2");
559 scratch.file("java/a/BUILD",
560 "java_test(name = 'A',",
561 " srcs = glob(['A*.java']))",
562 "java_test(name = 'B',",
563 " srcs = ['B.java'])");
564 update("//java/a:A");
565 ConfiguredTarget ct = getConfiguredTarget("//java/a:A");
566 scratch.deleteFile("java/a/C.java");
567 update("//java/a:B");
568 update("//java/a:A");
569 assertNotNull(getGeneratingAction(
570 getBinArtifact("A_deploy.jar", ct)));
571 }
572
573 /**
574 * Regression test: ClassCastException in SkyframeLabelVisitor.updateRootCauses.
575 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000576 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000577 public void testDepOnGoodTargetInBadPkgAndTransitivelyBadTarget() throws Exception {
578 reporter.removeHandler(failFastHandler);
579 scratch.file("parent/BUILD",
580 "sh_library(name = 'foo',",
581 " srcs = ['//badpkg1:okay-target', '//okaypkg:transitively-bad-target'])");
582 Path badpkg1BuildFile = scratch.file("badpkg1/BUILD",
583 "exports_files(['okay-target'])",
584 "invalidbuildsyntax");
585 scratch.file("okaypkg/BUILD",
586 "sh_library(name = 'transitively-bad-target',",
587 " srcs = ['//badpkg2:bad-target'])");
588 Path badpkg2BuildFile = scratch.file("badpkg2/BUILD",
589 "sh_library(name = 'bad-target')",
590 "invalidbuildsyntax");
591 update(defaultFlags().with(Flag.KEEP_GOING), "//parent:foo");
592 assertEquals(1, getFrequencyOfErrorsWithLocation(
593 badpkg1BuildFile.asFragment(), eventCollector));
594 assertEquals(1, getFrequencyOfErrorsWithLocation(
595 badpkg2BuildFile.asFragment(), eventCollector));
596 }
597
Florian Weikert79bd2372015-11-26 15:42:24 +0000598 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000599 public void testDepOnGoodTargetInBadPkgAndTransitiveCycle_NotIncremental() throws Exception {
600 runTestDepOnGoodTargetInBadPkgAndTransitiveCycle(/*incremental=*/false);
601 }
602
Florian Weikert79bd2372015-11-26 15:42:24 +0000603 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000604 public void testDepOnGoodTargetInBadPkgAndTransitiveCycle_Incremental() throws Exception {
605 runTestDepOnGoodTargetInBadPkgAndTransitiveCycle(/*incremental=*/true);
606 }
607
608 /**
609 * Regression test: in keep_going mode, cycles in target graph aren't reported
610 * if package is in error.
611 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000612 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000613 public void testCycleReporting_TargetCycleWhenPackageInError() throws Exception {
614 reporter.removeHandler(failFastHandler);
615 scratch.file("cycles/BUILD",
616 "sh_library(name = 'a', deps = [':b'])",
617 "sh_library(name = 'b', deps = [':a'])",
618 "notvalidbuildsyntax");
619 update(defaultFlags().with(Flag.KEEP_GOING), "//cycles:a");
620 assertContainsEvent("'notvalidbuildsyntax'");
621 assertContainsEvent("cycle in dependency graph");
622 }
623
Florian Weikert79bd2372015-11-26 15:42:24 +0000624 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000625 public void testTransitiveLoadingDoesntShortCircuitInKeepGoing() throws Exception {
626 reporter.removeHandler(failFastHandler);
627 scratch.file("parent/BUILD",
628 "sh_library(name = 'a', deps = ['//child:b'])",
629 "parentisbad");
630 scratch.file("child/BUILD",
631 "sh_library(name = 'b')",
632 "childisbad");
633 update(defaultFlags().with(Flag.KEEP_GOING), "//parent:a");
634 assertContainsEventWithFrequency("parentisbad", 1);
635 assertContainsEventWithFrequency("childisbad", 1);
636 assertContainsEventWithFrequency("and referenced by '//parent:a'", 1);
637 }
638
639 /**
640 * Smoke test for the Skyframe code path.
641 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000642 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000643 public void testSkyframe() throws Exception {
644 setupDummyRule();
645 String aoutLabel = "//pkg:a.out";
646
647 update(aoutLabel);
648
649 // However, a ConfiguredTarget was actually produced.
650 ConfiguredTarget target = Iterables.getOnlyElement(getAnalysisResult().getTargetsToBuild());
651 assertEquals(aoutLabel, target.getLabel().toString());
652
653 Artifact aout = Iterables.getOnlyElement(
654 target.getProvider(FileProvider.class).getFilesToBuild());
655 Action action = getGeneratingAction(aout);
656 assertSame(FailAction.class, action.getClass());
657 }
658
659 /**
660 * ConfiguredTargetFunction should not register actions in legacy Blaze ActionGraph unless
661 * the creation of the node is successful.
662 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000663 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000664 public void testActionsNotRegisteredInLegacyWhenError() throws Exception {
665 // First find the artifact we want to make sure is not generated by an action with an error.
666 // Then update the BUILD file and re-analyze.
667 scratch.file("actions_not_registered/BUILD",
668 "cc_binary(name = 'foo', srcs = ['foo.cc'])");
669 update("//actions_not_registered:foo");
670 Artifact fooOut = Iterables.getOnlyElement(
671 getConfiguredTarget("//actions_not_registered:foo")
672 .getProvider(FileProvider.class).getFilesToBuild());
673 assertNotNull(getActionGraph().getGeneratingAction(fooOut));
674 clearAnalysisResult();
675
676 scratch.overwriteFile("actions_not_registered/BUILD",
677 "cc_binary(name = 'foo', linkshared = 1, srcs = ['foo.cc'])");
678
679 reporter.removeHandler(failFastHandler);
680
681 try {
682 update("//actions_not_registered:foo");
683 fail("This build should fail because: 'linkshared' used in non-shared library");
684 } catch (ViewCreationFailedException e) {
685 assertNull(getActionGraph().getGeneratingAction(fooOut));
686 }
687 }
688
689 /**
690 * Regression test:
691 * "skyframe: ArtifactFactory and ConfiguredTargets out of sync".
692 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000693 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000694 public void testSkyframeAnalyzeRuleThenItsOutputFile() throws Exception {
695 scratch.file("pkg/BUILD",
696 "testing_dummy_rule(name='foo', ",
697 " srcs=['a.src'],",
698 " outs=['a.out'])");
699
700 scratch.file("pkg2/BUILD",
701 "testing_dummy_rule(name='foo', ",
702 " srcs=['a.src'],",
703 " outs=['a.out'])");
704 String aoutLabel = "//pkg:a.out";
705
706 update("//pkg2:foo");
707 update("//pkg:foo");
708 scratch.overwriteFile("pkg2/BUILD",
709 "testing_dummy_rule(name='foo', ",
710 " srcs=['a.src'],",
711 " outs=['a.out'])",
712 "# Comment");
713
714 update("//pkg:a.out");
715
716 // However, a ConfiguredTarget was actually produced.
717 ConfiguredTarget target = Iterables.getOnlyElement(getAnalysisResult().getTargetsToBuild());
718 assertEquals(aoutLabel, target.getLabel().toString());
719
720 Artifact aout = Iterables.getOnlyElement(
721 target.getProvider(FileProvider.class).getFilesToBuild());
722 Action action = getGeneratingAction(aout);
723 assertSame(FailAction.class, action.getClass());
724 }
725
726 /**
727 * Tests that skyframe reports the root cause as being the target that depended on the symlink
728 * cycle.
729 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000730 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000731 public void testRootCauseReportingFileSymlinks() throws Exception {
732 scratch.file("gp/BUILD",
733 "sh_library(name = 'gp', deps = ['//p'])");
734 scratch.file("p/BUILD",
735 "sh_library(name = 'p', deps = ['//c'])");
736 scratch.file("c/BUILD",
737 "sh_library(name = 'c', deps = [':c1', ':c2'])",
738 "sh_library(name = 'c1', deps = ['//cycles1'])",
739 "sh_library(name = 'c2', deps = ['//cycles2'])");
740 Path cycles1BuildFilePath = scratch.file("cycles1/BUILD",
741 "sh_library(name = 'cycles1', srcs = glob(['*.sh']))");
742 Path cycles2BuildFilePath = scratch.file("cycles2/BUILD",
743 "sh_library(name = 'cycles2', srcs = glob(['*.sh']))");
744 cycles1BuildFilePath.getParentDirectory().getRelative("cycles1.sh").createSymbolicLink(
745 new PathFragment("cycles1.sh"));
746 cycles2BuildFilePath.getParentDirectory().getRelative("cycles2.sh").createSymbolicLink(
747 new PathFragment("cycles2.sh"));
748 reporter.removeHandler(failFastHandler);
749 EventBus eventBus = new EventBus();
750 LoadingFailureRecorder recorder = new LoadingFailureRecorder();
751 eventBus.register(recorder);
Ulf Adams58581a32016-01-25 15:54:15 +0000752 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//gp");
753 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000754 assertThat(recorder.events).hasSize(2);
755 assertTrue(recorder.events.toString(), recorder.events.contains(
756 Pair.of(Label.parseAbsolute("//gp"), Label.parseAbsolute("//cycles1"))));
757 assertTrue(recorder.events.toString(), recorder.events.contains(
758 Pair.of(Label.parseAbsolute("//gp"), Label.parseAbsolute("//cycles2"))));
759 }
760
761 /**
762 * Regression test for bug when a configured target has missing deps, but also depends
763 * transitively on an error. We build //foo:query, which depends on a valid and an invalid target
764 * pattern. We ensure that by the time it requests its dependent target patterns, the invalid one
765 * is ready, and throws (though not before the request is registered). Then, when bubbling the
766 * invalid target pattern error up, we ensure that it bubbles into //foo:query, which must cope
767 * with the combination of an error and a missing dep.
768 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000769 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000770 public void testGenQueryWithBadTargetAndUnfinishedTarget() throws Exception {
771 // The target //foo:zquery is used to force evaluation of //foo:nosuchtarget before the target
772 // patterns in //foo:query are enqueued for evaluation. That way, //foo:query will depend on one
773 // invalid target pattern and two target patterns that haven't been evaluated yet.
774 // It is important that 'query' come before 'zquery' alphabetically, so that when the error is
775 // bubbling up, it goes to the //foo:query node -- we use a graph implementation in which the
776 // reverse deps of each entry are ordered alphabetically. It is also important that a missing
777 // target pattern is requested before the exception is thrown, so we have both //foo:b and
778 // //foo:z missing from the deps, in the hopes that at least one of them will come before
779 // //foo:nosuchtarget.
780 scratch.file("foo/BUILD",
781 "genquery(name = 'query',",
782 " expression = 'deps(//foo:b) except //foo:nosuchtarget except //foo:z',",
783 " scope = ['//foo:a'])",
784 "genquery(name = 'zquery',",
785 " expression = 'deps(//foo:nosuchtarget)',",
786 " scope = ['//foo:a'])",
787 "sh_library(name = 'a')",
788 "sh_library(name = 'b')",
789 "sh_library(name = 'z')"
790 );
791 Listener listener =
792 new Listener() {
793 private final CountDownLatch errorDone = new CountDownLatch(1);
794 private final CountDownLatch realQueryStarted = new CountDownLatch(1);
795
796 @Override
797 public void accept(SkyKey key, EventType type, Order order, Object context) {
798 if (!key.functionName().equals(SkyFunctions.TARGET_PATTERN)) {
799 return;
800 }
801 String label = ((TargetPatternKey) key.argument()).getPattern();
802 if (label.equals("//foo:nosuchtarget")) {
803 if (type == EventType.SET_VALUE) {
804 // Inform //foo:query-dep-registering thread that it may proceed.
805 errorDone.countDown();
806 // Wait to make sure //foo:query-dep-registering process has started.
807 TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
808 realQueryStarted, "//foo:query did not request dep in time");
809 } else if (type == EventType.ADD_REVERSE_DEP
810 && context.toString().contains("foo:query")) {
811 // Make sure that when foo:query requests foo:nosuchtarget, it's already done.
812 TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
813 errorDone, "//foo:nosuchtarget did not evaluate in time");
814 }
815 } else if ((label.equals("//foo:b") || label.equals("//foo:z"))
816 && type == EventType.CREATE_IF_ABSENT) {
817 // Inform error-evaluating thread that it may throw an exception.
818 realQueryStarted.countDown();
819 TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(
820 errorDone, "//foo:nosuchtarget did not evaluate in time");
821 // Don't let the target pattern //foo:{b,z} get enqueued for evaluation until we
822 // receive an interrupt signal from the threadpool. The interrupt means that
823 // evaluation is shutting down, and so //foo:{b,z} definitely won't get evaluated.
824 CountDownLatch waitForInterrupt = new CountDownLatch(1);
825 try {
826 waitForInterrupt.await(TestUtils.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
827 throw new IllegalStateException("node was not interrupted in time");
828 } catch (InterruptedException e) {
829 // Expected.
830 Thread.currentThread().interrupt();
831 }
832 }
833 }
834 };
Janak Ramakrishnan8be7fd02016-05-10 20:01:40 +0000835 injectGraphListenerForTesting(listener, /*deterministic=*/ true);
Dmitry Lomov021a3652015-11-23 14:55:13 +0000836 reporter.removeHandler(failFastHandler);
837 try {
838 update("//foo:query", "//foo:zquery");
839 fail();
840 } catch (ViewCreationFailedException e) {
841 Truth.assertThat(e.getMessage())
842 .contains("Analysis of target '//foo:query' failed; build aborted");
843 }
844 TrackingAwaiter.INSTANCE.assertNoErrors();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000845 }
846
847 /**
848 * Tests that rules with configurable attributes can be accessed through {@link
849 * com.google.devtools.build.lib.skyframe.PostConfiguredTargetFunction}.
850 * This is a regression test for a Bazel crash.
851 */
Florian Weikert79bd2372015-11-26 15:42:24 +0000852 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000853 public void testPostProcessedConfigurableAttributes() throws Exception {
Ulf Adamsf32a8452016-09-08 09:38:31 +0000854 useConfiguration("--cpu=k8");
Dmitry Lomov021a3652015-11-23 14:55:13 +0000855 reporter.removeHandler(failFastHandler); // Expect errors from action conflicts.
856 scratch.file("conflict/BUILD",
857 "config_setting(name = 'a', values = {'test_arg': 'a'})",
858 "cc_library(name='x', srcs=select({':a': ['a.cc'], '//conditions:default': ['foo.cc']}))",
859 "cc_binary(name='_objs/x/conflict/foo.pic.o', srcs=['bar.cc'])");
Ulf Adams58581a32016-01-25 15:54:15 +0000860 AnalysisResult result = update(
861 defaultFlags().with(Flag.KEEP_GOING),
862 "//conflict:_objs/x/conflict/foo.pic.o",
Dmitry Lomov021a3652015-11-23 14:55:13 +0000863 "//conflict:x");
Ulf Adams2dc95082016-01-27 12:56:15 +0000864 assertThat(result.hasError()).isTrue();
Dmitry Lomov021a3652015-11-23 14:55:13 +0000865 // Expect to reach this line without a Precondition-triggered NullPointerException.
866 assertContainsEvent(
867 "file 'conflict/_objs/x/conflict/foo.pic.o' is generated by these conflicting actions");
868 }
869
Florian Weikert79bd2372015-11-26 15:42:24 +0000870 @Test
Dmitry Lomov021a3652015-11-23 14:55:13 +0000871 public void testCycleDueToJavaLauncherConfiguration() throws Exception {
Greg Estrenb5692bd2016-06-08 21:09:11 +0000872 if (defaultFlags().contains(Flag.DYNAMIC_CONFIGURATIONS)) {
873 // Dynamic configurations don't yet support late-bound attributes. Development testing already
874 // runs all tests with dynamic configurations enabled, so this will still fail for developers
875 // and won't get lost in the fog.
876 return;
877 }
Dmitry Lomov021a3652015-11-23 14:55:13 +0000878 scratch.file("foo/BUILD",
879 "java_binary(name = 'java', srcs = ['DoesntMatter.java'])",
880 "cc_binary(name = 'cpp', data = [':java'])");
881 // Everything is fine - the dependency graph is acyclic.
Ulf Adams2ac20962016-02-01 13:04:54 +0000882 update("//foo:java", "//foo:cpp");
Greg Estren42c49352016-06-08 20:12:40 +0000883 if (getTargetConfiguration().useDynamicConfigurations()) {
884 fail(ExpectedDynamicConfigurationErrors.LATE_BOUND_ATTRIBUTES_UNSUPPORTED);
885 }
Dmitry Lomov021a3652015-11-23 14:55:13 +0000886 // Now there will be an analysis-phase cycle because the java_binary now has an implicit dep on
887 // the cc_binary launcher.
888 useConfiguration("--java_launcher=//foo:cpp");
889 reporter.removeHandler(failFastHandler);
890 try {
Ulf Adams2ac20962016-02-01 13:04:54 +0000891 update("//foo:java", "//foo:cpp");
Dmitry Lomov021a3652015-11-23 14:55:13 +0000892 fail();
893 } catch (ViewCreationFailedException expected) {
894 Truth.assertThat(expected.getMessage())
895 .matches("Analysis of target '//foo:(java|cpp)' failed; build aborted.*");
896 }
897 assertContainsEvent("cycle in dependency graph");
Dmitry Lomov021a3652015-11-23 14:55:13 +0000898 }
Ulf Adams2ac20962016-02-01 13:04:54 +0000899
900 @Test
901 public void testDependsOnBrokenTarget() throws Exception {
902 scratch.file("foo/BUILD",
903 "sh_test(name = 'test', srcs = ['test.sh'], data = ['//bar:data'])");
904 scratch.file("bar/BUILD",
905 "BROKEN BROKEN BROKEN!!!");
906 reporter.removeHandler(failFastHandler);
907 try {
908 update("//foo:test");
909 fail();
Ulf Adams2ac20962016-02-01 13:04:54 +0000910 } catch (ViewCreationFailedException expected) {
911 Truth.assertThat(expected.getMessage())
912 .matches("Analysis of target '//foo:test' failed; build aborted.*");
913 }
914 }
915
Ulf Adams47838c92016-02-02 18:12:44 +0000916 /**
917 * Regression test: IllegalStateException in BuildView.update() on circular dependency instead of
918 * graceful failure.
919 */
920 @Test
921 public void testCircularDependency() throws Exception {
922 scratch.file("cycle/BUILD",
923 "cc_library(name = 'foo', srcs = ['foo.cc'], deps = [':bar'])",
924 "cc_library(name = 'bar', srcs = ['bar.cc'], deps = [':foo'])");
925 reporter.removeHandler(failFastHandler);
926 try {
927 update("//cycle:foo");
928 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +0000929 } catch (ViewCreationFailedException expected) {
Ulf Adams47838c92016-02-02 18:12:44 +0000930 assertContainsEvent("in cc_library rule //cycle:foo: cycle in dependency graph:");
Ulf Adamsab64e592016-09-05 09:40:13 +0000931 assertThat(expected.getMessage())
932 .contains("Analysis of target '//cycle:foo' failed; build aborted");
Ulf Adams47838c92016-02-02 18:12:44 +0000933 }
934 }
935
936 /**
937 * Regression test: IllegalStateException in BuildView.update() on circular dependency instead of
938 * graceful failure.
939 */
940 @Test
941 public void testCircularDependencyWithKeepGoing() throws Exception {
942 scratch.file("cycle/BUILD",
943 "cc_library(name = 'foo', srcs = ['foo.cc'], deps = [':bar'])",
944 "cc_library(name = 'bar', srcs = ['bar.cc'], deps = [':foo'])",
945 "cc_library(name = 'bat', srcs = ['bat.cc'], deps = [':bas'])",
946 "cc_library(name = 'bas', srcs = ['bas.cc'], deps = [':bau'])",
947 "cc_library(name = 'bau', srcs = ['bas.cc'], deps = [':bas'])",
948 "cc_library(name = 'baz', srcs = ['baz.cc'])");
949 reporter.removeHandler(failFastHandler);
950 EventBus eventBus = new EventBus();
951 LoadingFailureRecorder loadingFailureRecorder = new LoadingFailureRecorder();
952 AnalysisFailureRecorder analysisFailureRecorder = new AnalysisFailureRecorder();
953 eventBus.register(loadingFailureRecorder);
954 eventBus.register(analysisFailureRecorder);
955 update(eventBus, defaultFlags().with(Flag.KEEP_GOING),
956 "//cycle:foo", "//cycle:bat", "//cycle:baz");
957 assertContainsEvent("in cc_library rule //cycle:foo: cycle in dependency graph:");
Ulf Adams47838c92016-02-02 18:12:44 +0000958 assertContainsEvent("in cc_library rule //cycle:bas: cycle in dependency graph:");
Ulf Adamsab64e592016-09-05 09:40:13 +0000959 assertContainsEvent(
960 "errors encountered while analyzing target '//cycle:foo': it will not be built");
961 assertContainsEvent(
962 "errors encountered while analyzing target '//cycle:bat': it will not be built");
963 // With interleaved loading and analysis, we can no longer distinguish loading-phase cycles
964 // and analysis-phase cycles. This was previously reported as a loading-phase cycle, as it
965 // happens with any configuration (cycle is hard-coded in the BUILD files). Also see the
966 // test below.
967 assertThat(Iterables.transform(analysisFailureRecorder.events, ANALYSIS_EVENT_TO_STRING_PAIR))
968 .containsExactly(
969 Pair.of("//cycle:foo", "//cycle:foo"), Pair.of("//cycle:bat", "//cycle:bas"));
Ulf Adams47838c92016-02-02 18:12:44 +0000970 }
971
972 @Test
973 public void testCircularDependencyWithLateBoundLabel() throws Exception {
974 scratch.file("cycle/BUILD",
975 "cc_library(name = 'foo', deps = [':bar'])",
976 "cc_library(name = 'bar')");
977 useConfiguration("--experimental_stl=//cycle:foo");
978 reporter.removeHandler(failFastHandler);
979 EventBus eventBus = new EventBus();
980 LoadingFailureRecorder loadingFailureRecorder = new LoadingFailureRecorder();
981 AnalysisFailureRecorder analysisFailureRecorder = new AnalysisFailureRecorder();
982 eventBus.register(loadingFailureRecorder);
983 eventBus.register(analysisFailureRecorder);
984 AnalysisResult result = update(eventBus, defaultFlags().with(Flag.KEEP_GOING), "//cycle:foo");
985 assertThat(result.hasError()).isTrue();
986 assertContainsEvent("in cc_library rule //cycle:foo: cycle in dependency graph:");
987 // This needs to be reported as an anlysis-phase cycle; the cycle only occurs due to the stl
988 // command-line option, which is part of the configuration, and which is used due to the
989 // late-bound label.
990 assertThat(Iterables.transform(analysisFailureRecorder.events, ANALYSIS_EVENT_TO_STRING_PAIR))
991 .containsExactly(Pair.of("//cycle:foo", "//cycle:foo"));
992 assertThat(loadingFailureRecorder.events).isEmpty();
993 }
994
Ulf Adams53abece2016-02-03 08:30:07 +0000995 @Test
996 public void testLoadingErrorReportedCorrectly() throws Exception {
997 scratch.file("a/BUILD", "cc_library(name='a')");
998 scratch.file("b/BUILD", "cc_library(name='b', deps = ['//missing:lib'])");
999
1000 reporter.removeHandler(failFastHandler);
1001 AnalysisResult result = update(defaultFlags().with(Flag.KEEP_GOING), "//a", "//b");
1002 assertThat(result.hasError()).isTrue();
1003 assertThat(result.getError())
Janak Ramakrishnan6f9d7d12016-08-09 08:45:34 +00001004 .contains("command succeeded, but there were loading phase errors");
Ulf Adams53abece2016-02-03 08:30:07 +00001005 }
1006
Ulf Adams5e8e5fe2016-02-04 09:37:35 +00001007 @Test
Ulf Adams3e34a112016-02-09 16:31:06 +00001008 public void testBadLabelInConfiguration() throws Exception {
1009 useConfiguration("--crosstool_top=//third_party/crosstool/v2");
1010 reporter.removeHandler(failFastHandler);
1011 try {
1012 update(defaultFlags().with(Flag.KEEP_GOING));
1013 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001014 } catch (InvalidConfigurationException e) {
Lukacs Berki120b8632016-07-28 10:18:21 +00001015 assertThat(e.getMessage()).contains("third_party/crosstool/v2");
Ulf Adams3e34a112016-02-09 16:31:06 +00001016 }
1017 }
1018
1019 @Test
1020 public void testMissingFdoOptimize() throws Exception {
1021 // The fdo_optimize flag uses a different code path, because it also accepts paths.
1022 useConfiguration("--fdo_optimize=//does/not/exist");
1023 reporter.removeHandler(failFastHandler);
1024 try {
1025 update(defaultFlags().with(Flag.KEEP_GOING));
1026 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001027 } catch (InvalidConfigurationException e) {
Ulf Adams3e34a112016-02-09 16:31:06 +00001028 assertContainsEvent(
1029 "no such package 'does/not/exist': BUILD file not found on package path");
1030 }
1031 }
1032
1033 @Test
1034 public void testMissingJavabase() throws Exception {
1035 // The javabase flag uses yet another code path with its own redirection logic on top of the
1036 // redirect chaser.
1037 scratch.file("jdk/BUILD",
1038 "filegroup(name = 'jdk', srcs = [",
1039 " '//does/not/exist:a-piii', '//does/not/exist:b-k8', '//does/not/exist:c-default'])");
1040 scratch.file("does/not/exist/BUILD");
Ulf Adams5b1be3a2016-07-07 12:58:00 +00001041 useConfigurationFactory(AnalysisMock.get().createConfigurationFactory());
Ulf Adams3e34a112016-02-09 16:31:06 +00001042 useConfiguration("--javabase=//jdk");
1043 reporter.removeHandler(failFastHandler);
1044 try {
1045 update(defaultFlags().with(Flag.KEEP_GOING));
1046 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001047 } catch (InvalidConfigurationException e) {
Lukacs Berki120b8632016-07-28 10:18:21 +00001048 // Expected
Ulf Adams3e34a112016-02-09 16:31:06 +00001049 }
1050 }
1051
1052 @Test
1053 public void testMissingXcodeVersion() throws Exception {
1054 // The xcode_version flag uses yet another code path on top of the redirect chaser.
1055 // Note that the redirect chaser throws if it can't find a package, but doesn't throw if it
1056 // can't find a label in a package - that's why we use an empty package here.
1057 scratch.file("xcode/BUILD");
1058 useConfiguration("--xcode_version=1.2", "--xcode_version_config=//xcode:does_not_exist");
1059 reporter.removeHandler(failFastHandler);
1060 try {
1061 update(defaultFlags().with(Flag.KEEP_GOING));
1062 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001063 } catch (InvalidConfigurationException e) {
Lukacs Berki120b8632016-07-28 10:18:21 +00001064 assertThat(e.getMessage()).contains("//xcode:does_not_exist");
Ulf Adams3e34a112016-02-09 16:31:06 +00001065 }
1066 }
1067
Ulf Adamscaf14772016-02-10 09:50:01 +00001068
1069 @Test
1070 public void testVisibilityReferencesNonexistentPackage() throws Exception {
1071 scratch.file("z/a/BUILD",
1072 "py_library(name='a', visibility=['//nonexistent:nothing'])");
1073 scratch.file("z/b/BUILD",
1074 "py_library(name='b', deps=['//z/a:a'])");
1075 reporter.removeHandler(failFastHandler);
1076 try {
1077 update("//z/b:b");
1078 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001079 } catch (ViewCreationFailedException expected) {
Ulf Adamscaf14772016-02-10 09:50:01 +00001080 assertContainsEvent("no such package 'nonexistent'");
1081 }
1082 }
1083
1084 // regression test ("java.lang.IllegalStateException: cannot happen")
1085 @Test
1086 public void testDefaultVisibilityInNonexistentPackage() throws Exception {
1087 scratch.file("z/a/BUILD",
1088 "package(default_visibility=['//b'])",
1089 "py_library(name='alib')");
1090 scratch.file("z/b/BUILD",
1091 "py_library(name='b', deps=['//z/a:alib'])");
1092 reporter.removeHandler(failFastHandler);
1093 try {
1094 update("//z/b:b");
1095 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001096 } catch (ViewCreationFailedException expected) {
Ulf Adamscaf14772016-02-10 09:50:01 +00001097 assertContainsEvent("no such package 'b'");
1098 }
1099 }
1100
Ulf Adams76f0ec62016-05-25 12:25:53 +00001101 @Test
1102 public void testNonTopLevelErrorsPrintedExactlyOnce() throws Exception {
1103 scratch.file("parent/BUILD",
1104 "sh_library(name = 'a', deps = ['//child:b'])");
1105 scratch.file("child/BUILD",
1106 "sh_library(name = 'b')",
1107 "undefined_symbol");
1108 reporter.removeHandler(failFastHandler);
1109 try {
1110 update("//parent:a");
1111 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001112 } catch (ViewCreationFailedException expected) {
Ulf Adams76f0ec62016-05-25 12:25:53 +00001113 }
1114 assertContainsEventWithFrequency("name 'undefined_symbol' is not defined", 1);
1115 assertContainsEventWithFrequency(
1116 "Target '//child:b' contains an error and its package is in error and referenced "
1117 + "by '//parent:a'", 1);
1118 }
1119
1120 @Test
1121 public void testNonTopLevelErrorsPrintedExactlyOnce_KeepGoing() throws Exception {
1122 scratch.file("parent/BUILD",
1123 "sh_library(name = 'a', deps = ['//child:b'])");
1124 scratch.file("child/BUILD",
1125 "sh_library(name = 'b')",
1126 "undefined_symbol");
1127 reporter.removeHandler(failFastHandler);
1128 update(defaultFlags().with(Flag.KEEP_GOING), "//parent:a");
1129 assertContainsEventWithFrequency("name 'undefined_symbol' is not defined", 1);
1130 assertContainsEventWithFrequency(
1131 "Target '//child:b' contains an error and its package is in error and referenced "
1132 + "by '//parent:a'", 1);
1133 }
1134
1135 @Test
1136 public void testNonTopLevelErrorsPrintedExactlyOnce_ActionListener() throws Exception {
1137 scratch.file("parent/BUILD",
1138 "sh_library(name = 'a', deps = ['//child:b'])");
1139 scratch.file("child/BUILD",
1140 "sh_library(name = 'b')",
1141 "undefined_symbol");
1142 scratch.file("okay/BUILD",
1143 "sh_binary(name = 'okay', srcs = ['okay.sh'])");
1144 useConfiguration("--experimental_action_listener=//parent:a");
1145 reporter.removeHandler(failFastHandler);
1146 try {
1147 update("//okay");
1148 fail();
Ulf Adamsab64e592016-09-05 09:40:13 +00001149 } catch (ViewCreationFailedException e) {
Ulf Adams76f0ec62016-05-25 12:25:53 +00001150 }
1151 assertContainsEventWithFrequency("name 'undefined_symbol' is not defined", 1);
1152 assertContainsEventWithFrequency(
1153 "Target '//child:b' contains an error and its package is in error and referenced "
1154 + "by '//parent:a'", 1);
1155 }
1156
1157 @Test
1158 public void testNonTopLevelErrorsPrintedExactlyOnce_ActionListener_KeepGoing() throws Exception {
1159 scratch.file("parent/BUILD",
1160 "sh_library(name = 'a', deps = ['//child:b'])");
1161 scratch.file("child/BUILD",
1162 "sh_library(name = 'b')",
1163 "undefined_symbol");
1164 scratch.file("okay/BUILD",
1165 "sh_binary(name = 'okay', srcs = ['okay.sh'])");
1166 useConfiguration("--experimental_action_listener=//parent:a");
1167 reporter.removeHandler(failFastHandler);
Ulf Adamsab64e592016-09-05 09:40:13 +00001168 update(defaultFlags().with(Flag.KEEP_GOING), "//okay");
Ulf Adams76f0ec62016-05-25 12:25:53 +00001169 assertContainsEventWithFrequency("name 'undefined_symbol' is not defined", 1);
1170 assertContainsEventWithFrequency(
1171 "Target '//child:b' contains an error and its package is in error and referenced "
1172 + "by '//parent:a'", 1);
1173 }
1174
Greg Estren7971e672016-06-01 23:22:48 +00001175 @Test
1176 public void testTopLevelTargetsAreTrimmedWithDynamicConfigurations() throws Exception {
1177 scratch.file("foo/BUILD",
1178 "sh_library(name='x', ",
1179 " srcs=['x.sh'])");
Greg Estren1d8ba902016-09-21 21:18:19 +00001180 useConfiguration("--experimental_dynamic_configs=on");
Greg Estren7971e672016-06-01 23:22:48 +00001181 AnalysisResult res = update("//foo:x");
1182 ConfiguredTarget topLevelTarget = Iterables.getOnlyElement(res.getTargetsToBuild());
1183 assertThat(topLevelTarget.getConfiguration().getAllFragments().keySet()).containsExactly(
1184 ruleClassProvider.getUniversalFragment());
1185 }
1186
Greg Estren2bc88382016-08-02 21:45:35 +00001187 @Test
1188 public void errorOnMissingDepFragments() throws Exception {
1189 scratch.file("foo/BUILD",
1190 "cc_library(",
1191 " name = 'ccbin', ",
1192 " srcs = ['c.cc'],",
1193 " data = [':javalib'])",
1194 "java_library(",
1195 " name = 'javalib',",
1196 " srcs = ['javalib.java'])");
Greg Estren1d8ba902016-09-21 21:18:19 +00001197 useConfiguration("--experimental_dynamic_configs=on", "--experimental_disable_jvm");
Greg Estren2bc88382016-08-02 21:45:35 +00001198 reporter.removeHandler(failFastHandler);
1199 try {
1200 update("//foo:ccbin");
1201 fail();
1202 } catch (ViewCreationFailedException e) {
1203 // Expected.
1204 }
1205 assertContainsEvent("//foo:ccbin: dependency //foo:javalib from attribute \"data\" is missing "
1206 + "required config fragments: Jvm");
1207 }
1208
Greg Estren373e3e22016-08-09 22:36:51 +00001209 @Test
1210 public void lateBoundSplitAttributeConfigs() throws Exception {
1211 useRuleClassProvider(LateBoundSplitUtil.getRuleClassProvider());
1212 // Register the latebound split fragment with the config creation environment.
1213 useConfigurationFactory(new ConfigurationFactory(
1214 ruleClassProvider.getConfigurationCollectionFactory(),
1215 ruleClassProvider.getConfigurationFragments()));
1216
1217 scratch.file("foo/BUILD",
1218 "rule_with_latebound_split(",
1219 " name = 'foo')",
Greg Estren247ac162016-08-10 20:50:09 +00001220 "rule_with_test_fragment(",
1221 " name = 'latebound_dep')");
Greg Estren373e3e22016-08-09 22:36:51 +00001222 update("//foo:foo");
1223 assertNotNull(getConfiguredTarget("//foo:foo"));
Greg Estren247ac162016-08-10 20:50:09 +00001224 Iterable<ConfiguredTarget> deps = SkyframeExecutorTestUtils.getExistingConfiguredTargets(
1225 skyframeExecutor, Label.parseAbsolute("//foo:latebound_dep"));
1226 assertThat(deps).hasSize(2);
1227 assertThat(
1228 ImmutableList.of(
1229 LateBoundSplitUtil.getOptions(Iterables.get(deps, 0).getConfiguration()).fooFlag,
1230 LateBoundSplitUtil.getOptions(Iterables.get(deps, 1).getConfiguration()).fooFlag))
1231 .containsExactly("one", "two");
Greg Estren373e3e22016-08-09 22:36:51 +00001232 }
1233
Ulf Adams2ac20962016-02-01 13:04:54 +00001234 /** Runs the same test with the reduced loading phase. */
1235 @TestSpec(size = Suite.SMALL_TESTS)
1236 @RunWith(JUnit4.class)
1237 public static class WithSkyframeLoadingPhase extends BuildViewTest {
1238 @Override
1239 protected FlagBuilder defaultFlags() {
1240 return super.defaultFlags().with(Flag.SKYFRAME_LOADING_PHASE);
1241 }
1242 }
Greg Estrenb5692bd2016-06-08 21:09:11 +00001243
1244 /** Runs the same test with dynamic configurations. */
1245 @TestSpec(size = Suite.SMALL_TESTS)
1246 @RunWith(JUnit4.class)
1247 public static class WithDynamicConfigurations extends BuildViewTest {
1248 @Override
1249 protected FlagBuilder defaultFlags() {
1250 return super.defaultFlags().with(Flag.DYNAMIC_CONFIGURATIONS);
1251 }
1252 }
Dmitry Lomov021a3652015-11-23 14:55:13 +00001253}