blob: a68ad4e3882d2ca2920eaf14ec064ab1843d4745 [file] [log] [blame]
Ulf Adamsd186d6a2015-12-21 09:43:52 +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.
14package com.google.devtools.build.lib.pkgcache;
15
16import static com.google.common.truth.Truth.assertThat;
17import static org.junit.Assert.assertEquals;
18import static org.junit.Assert.assertFalse;
19import static org.junit.Assert.assertNotNull;
20import static org.junit.Assert.assertNotSame;
21import static org.junit.Assert.assertSame;
22import static org.junit.Assert.assertTrue;
23import static org.junit.Assert.fail;
24
25import com.google.common.base.Predicates;
26import com.google.common.collect.ImmutableList;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000027import com.google.common.collect.ImmutableMap;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000028import com.google.common.collect.ImmutableSet;
29import com.google.devtools.build.lib.analysis.BlazeDirectories;
30import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
31import com.google.devtools.build.lib.analysis.util.AnalysisMock;
32import com.google.devtools.build.lib.cmdline.Label;
33import com.google.devtools.build.lib.cmdline.PackageIdentifier;
34import com.google.devtools.build.lib.events.Event;
Luis Fernando Pino Duque964712c02016-03-31 11:05:31 +000035import com.google.devtools.build.lib.flags.InvocationPolicyEnforcer;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000036import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000037import com.google.devtools.build.lib.packages.NoSuchPackageException;
38import com.google.devtools.build.lib.packages.NoSuchTargetException;
39import com.google.devtools.build.lib.packages.Package;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000040import com.google.devtools.build.lib.packages.Preprocessor;
41import com.google.devtools.build.lib.packages.Rule;
42import com.google.devtools.build.lib.packages.Target;
43import com.google.devtools.build.lib.packages.util.MockToolsConfig;
44import com.google.devtools.build.lib.skyframe.DiffAwareness;
45import com.google.devtools.build.lib.skyframe.PrecomputedValue;
46import com.google.devtools.build.lib.skyframe.SequencedSkyframeExecutor;
47import com.google.devtools.build.lib.skyframe.SkyValueDirtinessChecker;
48import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
49import com.google.devtools.build.lib.syntax.BuildFileAST;
50import com.google.devtools.build.lib.testutil.FoundationTestCase;
51import com.google.devtools.build.lib.testutil.MoreAsserts;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000052import com.google.devtools.build.lib.util.BlazeClock;
53import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
54import com.google.devtools.build.lib.vfs.ModifiedFileSet;
55import com.google.devtools.build.lib.vfs.Path;
56import com.google.devtools.build.lib.vfs.PathFragment;
57import com.google.devtools.common.options.OptionsParser;
Luis Fernando Pino Duque964712c02016-03-31 11:05:31 +000058import com.google.devtools.common.options.OptionsParsingException;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000059import java.io.IOException;
60import java.util.HashSet;
61import java.util.List;
62import java.util.Set;
63import java.util.UUID;
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +000064import org.junit.Before;
65import org.junit.Test;
66import org.junit.runner.RunWith;
67import org.junit.runners.JUnit4;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000068
69/**
70 * Tests for package loading.
71 */
72@RunWith(JUnit4.class)
73public class PackageCacheTest extends FoundationTestCase {
74
Ulf Adams015aad92016-07-13 16:49:40 +000075 private AnalysisMock analysisMock;
Ulf Adamsd186d6a2015-12-21 09:43:52 +000076 private ConfiguredRuleClassProvider ruleClassProvider;
77 private SkyframeExecutor skyframeExecutor;
78
79 @Before
Ulf Adamsc73051c62016-03-23 09:18:13 +000080 public final void initializeSkyframeExecutor() throws Exception {
Ulf Adams015aad92016-07-13 16:49:40 +000081 analysisMock = AnalysisMock.get();
82 ruleClassProvider = analysisMock.createRuleClassProvider();
83 BlazeDirectories directories =
84 new BlazeDirectories(outputBase, outputBase, rootDirectory, analysisMock.getProductName());
Ulf Adamsd186d6a2015-12-21 09:43:52 +000085 skyframeExecutor =
86 SequencedSkyframeExecutor.create(
Ulf Adams015aad92016-07-13 16:49:40 +000087 analysisMock
88 .getPackageFactoryForTesting()
89 .create(ruleClassProvider, scratch.getFileSystem()),
Ulf Adamsd186d6a2015-12-21 09:43:52 +000090 directories,
91 null, /* BinTools */
92 null, /* workspaceStatusActionFactory */
93 ruleClassProvider.getBuildInfoFactories(),
94 ImmutableList.<DiffAwareness.Factory>of(),
95 Predicates.<PathFragment>alwaysFalse(),
96 Preprocessor.Factory.Supplier.NullSupplier.INSTANCE,
Ulf Adamsa4c1a562016-04-15 11:12:54 +000097 AnalysisMock.get().getSkyFunctions(),
Ulf Adamsd186d6a2015-12-21 09:43:52 +000098 ImmutableList.<PrecomputedValue.Injected>of(),
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +000099 ImmutableList.<SkyValueDirtinessChecker>of(),
Ulf Adams015aad92016-07-13 16:49:40 +0000100 analysisMock.getProductName());
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000101 setUpSkyframe(parsePackageCacheOptions());
102 }
103
104 private void setUpSkyframe(PackageCacheOptions packageCacheOptions) {
105 PathPackageLocator pkgLocator = PathPackageLocator.create(
106 null, packageCacheOptions.packagePath, reporter, rootDirectory, rootDirectory);
Janak Ramakrishnan326c6982016-09-27 14:58:26 +0000107 packageCacheOptions.showLoadingProgress = true;
108 packageCacheOptions.globbingThreads = 7;
Janak Ramakrishnanb92c0972016-03-23 16:47:13 +0000109 skyframeExecutor.preparePackageLoading(
110 pkgLocator,
Janak Ramakrishnan326c6982016-09-27 14:58:26 +0000111 packageCacheOptions,
Ulf Adams015aad92016-07-13 16:49:40 +0000112 analysisMock.getDefaultsPackageContent(),
Janak Ramakrishnanb92c0972016-03-23 16:47:13 +0000113 UUID.randomUUID(),
Klaus Aehlig03b9cfd2016-09-14 13:14:39 +0000114 ImmutableMap.<String, String>of(),
Janak Ramakrishnanb92c0972016-03-23 16:47:13 +0000115 new TimestampGranularityMonitor(BlazeClock.instance()));
Klaus Aehlig6f33a1c2016-09-13 16:46:10 +0000116 skyframeExecutor.setDeletedPackages(
117 ImmutableSet.copyOf(packageCacheOptions.getDeletedPackages()));
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000118 }
119
120 private PackageCacheOptions parsePackageCacheOptions(String... options) throws Exception {
121 OptionsParser parser = OptionsParser.newOptionsParser(PackageCacheOptions.class);
122 parser.parse(new String[] { "--default_visibility=public" });
123 parser.parse(options);
Luis Fernando Pino Duque964712c02016-03-31 11:05:31 +0000124
Ulf Adams015aad92016-07-13 16:49:40 +0000125 InvocationPolicyEnforcer optionsPolicyEnforcer = analysisMock.getInvocationPolicyEnforcer();
Luis Fernando Pino Duque964712c02016-03-31 11:05:31 +0000126 try {
127 optionsPolicyEnforcer.enforce(parser);
128 } catch (OptionsParsingException e) {
129 throw new IllegalStateException(e);
130 }
131
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000132 return parser.getOptions(PackageCacheOptions.class);
133 }
134
135 protected void setOptions(String... options) throws Exception {
136 setUpSkyframe(parsePackageCacheOptions(options));
137 }
138
139 private PackageManager getPackageManager() {
140 return skyframeExecutor.getPackageManager();
141 }
142
143 private void invalidatePackages() throws InterruptedException {
144 skyframeExecutor.invalidateFilesUnderPathForTesting(
145 reporter, ModifiedFileSet.EVERYTHING_MODIFIED, rootDirectory);
146 }
147
148 private Package getPackage(String packageName)
149 throws NoSuchPackageException, InterruptedException {
150 return getPackageManager().getPackage(reporter,
Brian Silvermand7d6d622016-03-17 09:53:39 +0000151 PackageIdentifier.createInMainRepo(packageName));
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000152 }
153
154 private Target getTarget(Label label)
155 throws NoSuchPackageException, NoSuchTargetException, InterruptedException {
156 return getPackageManager().getTarget(reporter, label);
157 }
158
159 private Target getTarget(String label) throws Exception {
160 return getTarget(Label.parseAbsolute(label));
161 }
162
163 private void createPkg1() throws IOException {
164 scratch.file("pkg1/BUILD", "cc_library(name = 'foo') # a BUILD file");
165 }
166
167 // Check that a substring is present in an error message.
168 private void checkGetPackageFails(String packageName, String expectedMessage) throws Exception {
169 try {
170 getPackage(packageName);
171 fail();
172 } catch (NoSuchPackageException e) {
173 assertThat(e.getMessage()).contains(expectedMessage);
174 }
175 }
176
177 @Test
178 public void testGetPackage() throws Exception {
179 createPkg1();
180 Package pkg1 = getPackage("pkg1");
181 assertEquals("pkg1", pkg1.getName());
182 assertEquals("/workspace/pkg1/BUILD",
183 pkg1.getFilename().toString());
184 assertSame(pkg1, getPackageManager().getPackage(reporter,
Brian Silvermand7d6d622016-03-17 09:53:39 +0000185 PackageIdentifier.createInMainRepo("pkg1")));
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000186 }
187
188 @Test
189 public void testASTIsNotRetained() throws Exception {
190 createPkg1();
191 Package pkg1 = getPackage("pkg1");
192 MoreAsserts.assertInstanceOfNotReachable(pkg1, BuildFileAST.class);
193 }
194
195 @Test
196 public void testGetNonexistentPackage() throws Exception {
197 checkGetPackageFails("not-there",
198 "no such package 'not-there': "
199 + "BUILD file not found on package path");
200 }
201
202 @Test
203 public void testGetPackageWithInvalidName() throws Exception {
204 scratch.file("invalidpackagename&42/BUILD", "cc_library(name = 'foo') # a BUILD file");
205 checkGetPackageFails(
206 "invalidpackagename&42",
207 "no such package 'invalidpackagename&42': Invalid package name 'invalidpackagename&42'");
208 }
209
210 @Test
211 public void testGetTarget() throws Exception {
212 createPkg1();
213 Label label = Label.parseAbsolute("//pkg1:foo");
214 Target target = getTarget(label);
215 assertEquals(label, target.getLabel());
216 }
217
218 @Test
219 public void testGetNonexistentTarget() throws Exception {
220 createPkg1();
221 try {
222 getTarget("//pkg1:not-there");
223 fail();
224 } catch (NoSuchTargetException e) {
225 assertThat(e).hasMessage("no such target '//pkg1:not-there': target 'not-there' "
226 + "not declared in package 'pkg1' defined by /workspace/pkg1/BUILD");
227 }
228 }
229
230 /**
231 * A missing package is one for which no BUILD file can be found. The
232 * PackageCache caches failures of this kind until the next sync.
233 */
234 @Test
235 public void testRepeatedAttemptsToParseMissingPackage() throws Exception {
236 checkGetPackageFails("missing",
237 "no such package 'missing': "
238 + "BUILD file not found on package path");
239
240 // Still missing:
241 checkGetPackageFails("missing",
242 "no such package 'missing': "
243 + "BUILD file not found on package path");
244
245 // Update the BUILD file on disk so "missing" is no longer missing:
246 scratch.file("missing/BUILD",
247 "# an ok build file");
248
249 // Still missing:
250 checkGetPackageFails("missing",
251 "no such package 'missing': "
252 + "BUILD file not found on package path");
253
254 invalidatePackages();
255
256 // Found:
257 Package missing = getPackage("missing");
258
259 assertEquals("missing", missing.getName());
260 }
261
262 /**
263 * A broken package is one that exists but contains lexer/parser/evaluator errors. The
264 * PackageCache only makes one attempt to parse each package once found.
265 *
266 * <p>Depending on the strictness of the PackageFactory, parsing a broken package may cause a
267 * Package object to be returned (possibly missing some rules) or an exception to be thrown. For
268 * this test we need that strict behavior.
269 *
270 * <p>Note: since the PackageCache.setStrictPackageCreation method was deleted (since it wasn't
271 * used by any significant clients) creating a "broken" build file got trickier--syntax errors are
272 * not enough. For now, we create an unreadable BUILD file, which will cause an IOException to be
273 * thrown. This test seems less valuable than it once did.
274 */
275 @Test
276 public void testParseBrokenPackage() throws Exception {
277 reporter.removeHandler(failFastHandler);
278
279 Path brokenBuildFile = scratch.file("broken/BUILD");
280 brokenBuildFile.setReadable(false);
281
282 try {
283 getPackage("broken");
284 fail();
285 } catch (BuildFileContainsErrorsException e) {
286 assertThat(e.getMessage()).contains("/workspace/broken/BUILD (Permission denied)");
287 }
288 eventCollector.clear();
289
290 // Update the BUILD file on disk so "broken" is no longer broken:
291 scratch.overwriteFile("broken/BUILD",
292 "# an ok build file");
293
294 invalidatePackages(); // resets cache of failures
295
296 Package broken = getPackage("broken");
297 assertEquals("broken", broken.getName());
298 assertNoEvents();
299 }
300
301 @Test
302 public void testPackageInErrorReloadedWhenFixed() throws Exception {
303 reporter.removeHandler(failFastHandler);
304 Path build = scratch.file("a/BUILD", "cc_library(name='a', feet='stinky')");
305 build.setLastModifiedTime(1);
306 Package a1 = getPackage("a");
307 assertTrue(a1.containsErrors());
308 assertContainsEvent("//a:a: no such attribute 'feet'");
309
310 eventCollector.clear();
311 build.delete();
312 build = scratch.file("a/BUILD", "cc_library(name='a', srcs=['a.cc'])");
313 build.setLastModifiedTime(2);
314 invalidatePackages();
315 Package a2 = getPackage("a");
316 assertNotSame(a1, a2);
317 assertFalse(a2.containsErrors());
318 assertNoEvents();
319 }
320
321 @Test
322 public void testModifiedBuildFileCausesReloadAfterSync() throws Exception {
323 Path path = scratch.file("pkg/BUILD",
324 "cc_library(name = 'foo')");
325 path.setLastModifiedTime(1000);
326
327 Package oldPkg = getPackage("pkg");
328 // modify BUILD file (and change its timestamp)
329 path.delete();
330 scratch.file("pkg/BUILD", "cc_library(name = 'bar')");
331 path.setLastModifiedTime(999); // earlier; mtime doesn't have to advance
332 assertSame(oldPkg, getPackage("pkg")); // change not yet visible
333
334 invalidatePackages();
335
336 Package newPkg = getPackage("pkg");
337 assertNotSame(oldPkg, newPkg);
338 assertNotNull(newPkg.getTarget("bar"));
339 }
340
341 @Test
342 public void testTouchedBuildFileCausesReloadAfterSync() throws Exception {
343 Path path = scratch.file("pkg/BUILD",
344 "cc_library(name = 'foo')");
345 path.setLastModifiedTime(1000);
346
347 Package oldPkg = getPackage("pkg");
348 path.setLastModifiedTime(1001);
349 assertSame(oldPkg, getPackage("pkg")); // change not yet visible
350
351 invalidatePackages();
352
353 Package newPkg = getPackage("pkg");
354 assertNotSame(oldPkg, newPkg);
355 }
356
357 @Test
358 public void testMovedBuildFileCausesReloadAfterSync() throws Exception {
359 Path buildFile1 = scratch.file("pkg/BUILD",
360 "cc_library(name = 'foo')");
361 Path buildFile2 = scratch.file("/otherroot/pkg/BUILD",
362 "cc_library(name = 'bar')");
363 setOptions("--package_path=/workspace:/otherroot");
364
365 Package oldPkg = getPackage("pkg");
366 assertSame(oldPkg, getPackage("pkg")); // change not yet visible
367 assertEquals(buildFile1, oldPkg.getFilename());
368 assertEquals(rootDirectory, oldPkg.getSourceRoot());
369
370 buildFile1.delete();
371 invalidatePackages();
372
373 Package newPkg = getPackage("pkg");
374 assertNotSame(oldPkg, newPkg);
375 assertEquals(buildFile2, newPkg.getFilename());
376 assertEquals(scratch.dir("/otherroot"), newPkg.getSourceRoot());
377
378 // TODO(bazel-team): (2009) test BUILD file moves in the other direction too.
379 }
380
381 private Path rootDir1;
382 private Path rootDir2;
383
384 private void setUpCacheWithTwoRootLocator() throws Exception {
385 // Root 1:
386 // /a/BUILD
387 // /b/BUILD
388 // /c/d
389 // /c/e
390 //
391 // Root 2:
392 // /b/BUILD
393 // /c/BUILD
394 // /c/d/BUILD
395 // /f/BUILD
396 // /f/g
397 // /f/g/h/BUILD
398
399 rootDir1 = scratch.dir("/workspace");
400 rootDir2 = scratch.dir("/otherroot");
401
402 createBuildFile(rootDir1, "a", "foo.txt", "bar/foo.txt");
403 createBuildFile(rootDir1, "b", "foo.txt", "bar/foo.txt");
404
405 rootDir1.getRelative("c").createDirectory();
406 rootDir1.getRelative("c/d").createDirectory();
407 rootDir1.getRelative("c/e").createDirectory();
408
409 createBuildFile(rootDir2, "c", "d", "d/foo.txt", "foo.txt", "bar/foo.txt", "e", "e/foo.txt");
410 createBuildFile(rootDir2, "c/d", "foo.txt");
411 createBuildFile(rootDir2, "f", "g/foo.txt", "g/h", "g/h/foo.txt", "foo.txt");
412 createBuildFile(rootDir2, "f/g/h", "foo.txt");
413
414 setOptions("--package_path=/workspace:/otherroot");
415 }
416
417 protected Path createBuildFile(Path workspace, String packageName,
418 String... targets) throws IOException {
419 String[] lines = new String[targets.length];
420
421 for (int i = 0; i < targets.length; i++) {
422 lines[i] = "sh_library(name='" + targets[i] + "')";
423 }
424
425 return scratch.file(workspace + "/" + packageName + "/BUILD", lines);
426 }
427
428 private void assertLabelValidity(boolean expected, String labelString)
429 throws Exception {
430 Label label = Label.parseAbsolute(labelString);
431
432 boolean actual = false;
433 String error = null;
434 try {
435 getTarget(label);
436 actual = true;
437 } catch (NoSuchPackageException | NoSuchTargetException e) {
438 error = e.getMessage();
439 }
440 if (actual != expected) {
441 fail("assertLabelValidity(" + label + ") "
442 + actual + ", not equal to expected value " + expected
443 + " (error=" + error + ")");
444 }
445 }
446
447 private void assertPackageLoadingFails(String pkgName, String expectedError) throws Exception {
448 Package pkg = getPackage(pkgName);
449 assertTrue(pkg.containsErrors());
450 assertContainsEvent(expectedError);
451 }
452
453 @Test
454 public void testLocationForLabelCrossingSubpackage() throws Exception {
455 scratch.file("e/f/BUILD");
456 scratch.file("e/BUILD",
457 "# Whatever",
458 "filegroup(name='fg', srcs=['f/g'])");
459 reporter.removeHandler(failFastHandler);
460 List<Event> events = getPackage("e").getEvents();
461 assertThat(events).hasSize(1);
462 assertEquals(2, events.get(0).getLocation().getStartLineAndColumn().getLine());
463 }
464
465 /** Static tests (i.e. no changes to filesystem, nor calls to sync). */
466 @Test
467 public void testLabelValidity() throws Exception {
468 reporter.removeHandler(failFastHandler);
469 setUpCacheWithTwoRootLocator();
470
471 scratch.file(rootDir2 + "/c/d/foo.txt");
472
473 assertLabelValidity(true, "//a:foo.txt");
474 assertLabelValidity(true, "//a:bar/foo.txt");
475 assertLabelValidity(false, "//a/bar:foo.txt"); // no such package a/bar
476
477 assertLabelValidity(true, "//b:foo.txt");
478 assertLabelValidity(true, "//b:bar/foo.txt");
479 assertLabelValidity(false, "//b/bar:foo.txt"); // no such package b/bar
480
481 assertLabelValidity(true, "//c:foo.txt");
482 assertLabelValidity(true, "//c:bar/foo.txt");
483 assertLabelValidity(false, "//c/bar:foo.txt"); // no such package c/bar
484
485 assertLabelValidity(true, "//c:foo.txt");
486
487 assertLabelValidity(false, "//c:d/foo.txt"); // crosses boundary of c/d
488 assertLabelValidity(true, "//c/d:foo.txt");
489
490 assertLabelValidity(true, "//c:foo.txt");
491 assertLabelValidity(true, "//c:e");
492 assertLabelValidity(true, "//c:e/foo.txt");
493 assertLabelValidity(false, "//c/e:foo.txt"); // no such package c/e
494
495 assertLabelValidity(true, "//f:foo.txt");
496 assertLabelValidity(true, "//f:g/foo.txt");
497 assertLabelValidity(false, "//f/g:foo.txt"); // no such package f/g
498 assertLabelValidity(false, "//f:g/h/foo.txt"); // crosses boundary of f/g/h
499 assertLabelValidity(false, "//f/g:h/foo.txt"); // no such package f/g
500 assertLabelValidity(true, "//f/g/h:foo.txt");
501 }
502
503 /** Dynamic tests of label validity. */
504 @Test
505 public void testAddedBuildFileCausesLabelToBecomeInvalid() throws Exception {
506 reporter.removeHandler(failFastHandler);
507 scratch.file("pkg/BUILD",
508 " cc_library(name = 'foo', ",
509 " srcs = ['x/y.cc'])");
510
511 assertLabelValidity(true, "//pkg:x/y.cc");
512
513 // The existence of this file makes 'x/y.cc' an invalid reference.
514 scratch.file("pkg/x/BUILD");
515
516 // but not yet...
517 assertLabelValidity(true, "//pkg:x/y.cc");
518
519 invalidatePackages();
520
521 // now:
522 assertPackageLoadingFails("pkg",
523 "Label '//pkg:x/y.cc' crosses boundary of subpackage 'pkg/x' "
524 + "(perhaps you meant to put the colon here: '//pkg/x:y.cc'?)");
525 }
526
527 @Test
528 public void testDeletedPackages() throws Exception {
529 reporter.removeHandler(failFastHandler);
530 setUpCacheWithTwoRootLocator();
531 createBuildFile(rootDir1, "c", "d/x");
532 // Now package c exists in both roots, and c/d exists in only in the second
533 // root. It's as if we've merged c and c/d in the first root.
534
535 // c/d is still a subpackage--found in the second root:
536 assertEquals(rootDir2.getRelative("c/d/BUILD"),
537 getPackage("c/d").getFilename());
538
539 // Subpackage labels are still valid...
540 assertLabelValidity(true, "//c/d:foo.txt");
541 // ...and this crosses package boundaries:
542 assertLabelValidity(false, "//c:d/x");
543 assertPackageLoadingFails("c",
544 "Label '//c:d/x' crosses boundary of subpackage 'c/d' (have you deleted c/d/BUILD? "
545 + "If so, use the --deleted_packages=c/d option)");
546
547 assertTrue(getPackageManager().isPackage(
Brian Silvermand7d6d622016-03-17 09:53:39 +0000548 reporter, PackageIdentifier.createInMainRepo("c/d")));
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000549
550 setOptions("--deleted_packages=c/d");
551 invalidatePackages();
552
553 assertFalse(getPackageManager().isPackage(
Brian Silvermand7d6d622016-03-17 09:53:39 +0000554 reporter, PackageIdentifier.createInMainRepo("c/d")));
Ulf Adamsd186d6a2015-12-21 09:43:52 +0000555
556 // c/d is no longer a subpackage--even though there's a BUILD file in the
557 // second root:
558 try {
559 getPackage("c/d");
560 fail();
561 } catch (NoSuchPackageException e) {
562 assertThat(e).hasMessage(
563 "no such package 'c/d': Package is considered deleted due to --deleted_packages");
564 }
565
566 // Labels in the subpackage are no longer valid...
567 assertLabelValidity(false, "//c/d:x");
568 // ...and now d is just a subdirectory of c:
569 assertLabelValidity(true, "//c:d/x");
570 }
571
572 @Test
573 public void testPackageFeatures() throws Exception {
574 scratch.file("peach/BUILD",
575 "package(features = ['crosstool_default_false'])",
576 "cc_library(name = 'cc', srcs = ['cc.cc'])");
577 Rule cc = (Rule) getTarget("//peach:cc");
578 assertThat(cc.getFeatures()).hasSize(1);
579 }
580
581 /** Visit label and its dependencies and load all of them. */
582 private void visitLabel(String label) throws Exception {
583 TransitivePackageLoader visitor = getPackageManager().newTransitiveLoader();
584 Set<Target> targets = new HashSet<>();
585 targets.add(getPackageManager().getTarget(reporter, Label.parseAbsolute(label)));
586 visitor.sync(reporter, targets, ImmutableSet.<Label>of(),
587 false, 1, Integer.MAX_VALUE);
588 }
589
590 @Test
591 public void testSyntaxErrorInDepPackage() throws Exception {
592 reporter.removeHandler(failFastHandler);
593 AnalysisMock.get().setupMockClient(new MockToolsConfig(rootDirectory));
594
595 scratch.file("a/BUILD",
596 "genrule(name='x',",
597 " srcs = ['file.txt'],",
598 " outs = ['foo'],",
599 " cmd = 'echo')",
600 "@"); // syntax error
601
602 scratch.file("b/BUILD",
603 "genrule(name= 'cc',",
604 " tools = ['//a:x'],",
605 " outs = ['bar'],",
606 " cmd = 'echo')");
607
608 Package pkgB = getPackage("b");
609
610 // We should get error message from package a, but package is properly loaded.
611 visitLabel("//b:cc");
612 assertContainsEvent("invalid character: '@'");
613 assertFalse(pkgB.containsErrors());
614 }
615
616 @Test
617 public void testBrokenPackageOnMultiplePackagePathEntries() throws Exception {
618 reporter.removeHandler(failFastHandler);
619 setOptions("--package_path=.:.");
620 scratch.file("x/y/BUILD");
621 scratch.file("x/BUILD",
622 "genrule(name = 'x',",
623 "srcs = [],",
624 "outs = ['y/z.h'],",
625 "cmd = '')");
626 Package p = getPackage("x");
627 assertTrue(p.containsErrors());
628 }
629}