blob: 2a2b408bde2a229e5bd4727df3b0344eafa76b74 [file] [log] [blame]
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +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.skyframe;
15
16import static com.google.common.truth.Truth.assertThat;
17import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.CROSS;
18import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.DONT_CROSS;
19import static com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode.REPORT_ERROR;
Laszlo Csomore9c41c52015-12-07 10:32:26 +000020import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.danglingSymlinkForTesting;
21import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.regularFileForTesting;
22import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.symlinkToDirectoryForTesting;
23import static com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactoryForTesting.symlinkToFileForTesting;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000024
tomlua155b532017-11-08 20:12:47 +010025import com.google.common.base.Preconditions;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000026import com.google.common.base.Supplier;
27import com.google.common.collect.ImmutableList;
28import com.google.common.collect.ImmutableSet;
29import com.google.common.collect.Sets;
30import com.google.devtools.build.lib.actions.Artifact;
tomlu1cdcdf92018-01-16 11:07:51 -080031import com.google.devtools.build.lib.actions.ArtifactRoot;
felly5be4dd62018-02-05 11:11:53 -080032import com.google.devtools.build.lib.actions.FilesetTraversalParams.DirectTraversalRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000033import com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode;
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +000034import com.google.devtools.build.lib.analysis.BlazeDirectories;
Ulf Adams015aad92016-07-13 16:49:40 +000035import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
janakr3b63a4e2017-09-14 09:55:40 +020036import com.google.devtools.build.lib.analysis.ServerDirectories;
Ulf Adams015aad92016-07-13 16:49:40 +000037import com.google.devtools.build.lib.analysis.util.AnalysisMock;
Laszlo Csomora31e0352018-03-09 04:51:38 -080038import com.google.devtools.build.lib.clock.BlazeClock;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000039import com.google.devtools.build.lib.cmdline.PackageIdentifier;
40import com.google.devtools.build.lib.events.NullEventHandler;
41import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
Nathan Harmatad4f75942016-10-18 08:55:17 +000042import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
John Cater5e9ce942016-10-12 17:23:30 +000043import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
kush95bf7c82017-08-30 00:27:35 +020044import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalFunction.FileOperationException;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000045import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile;
46import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.TraversalRequest;
Florian Weikertcca703a2015-12-07 09:56:38 +000047import com.google.devtools.build.lib.testutil.FoundationTestCase;
Laszlo Csomora31e0352018-03-09 04:51:38 -080048import com.google.devtools.build.lib.util.io.OutErr;
49import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000050import com.google.devtools.build.lib.vfs.Path;
51import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080052import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000053import com.google.devtools.build.lib.vfs.RootedPath;
54import com.google.devtools.build.skyframe.ErrorInfo;
55import com.google.devtools.build.skyframe.EvaluationProgressReceiver;
56import com.google.devtools.build.skyframe.EvaluationResult;
57import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
58import com.google.devtools.build.skyframe.MemoizingEvaluator;
59import com.google.devtools.build.skyframe.RecordingDifferencer;
janakr1cde8722017-10-10 03:22:21 +020060import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000061import com.google.devtools.build.skyframe.SequentialBuildDriver;
62import com.google.devtools.build.skyframe.SkyFunction;
felly5be4dd62018-02-05 11:11:53 -080063import com.google.devtools.build.skyframe.SkyFunctionException;
64import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000065import com.google.devtools.build.skyframe.SkyFunctionName;
66import com.google.devtools.build.skyframe.SkyKey;
67import com.google.devtools.build.skyframe.SkyValue;
felly5be4dd62018-02-05 11:11:53 -080068import java.io.IOException;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000069import java.io.OutputStream;
70import java.nio.charset.StandardCharsets;
71import java.util.HashMap;
72import java.util.HashSet;
73import java.util.Map;
74import java.util.Set;
75import java.util.UUID;
76import java.util.concurrent.atomic.AtomicReference;
felly5be4dd62018-02-05 11:11:53 -080077import javax.annotation.Nullable;
John Cater5e9ce942016-10-12 17:23:30 +000078import org.junit.Before;
79import org.junit.Test;
80import org.junit.runner.RunWith;
81import org.junit.runners.JUnit4;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000082
83/** Tests for {@link RecursiveFilesystemTraversalFunction}. */
Florian Weikert92b22362015-12-03 10:17:18 +000084@RunWith(JUnit4.class)
Florian Weikertcca703a2015-12-07 09:56:38 +000085public final class RecursiveFilesystemTraversalFunctionTest extends FoundationTestCase {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000086 private RecordingEvaluationProgressReceiver progressReceiver;
87 private MemoizingEvaluator evaluator;
88 private SequentialBuildDriver driver;
89 private RecordingDifferencer differencer;
90 private AtomicReference<PathPackageLocator> pkgLocator;
felly5be4dd62018-02-05 11:11:53 -080091 private ArtifactFakeFunction artifactFakeFunction;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +000092
Florian Weikert92b22362015-12-03 10:17:18 +000093 @Before
94 public final void setUp() throws Exception {
Ulf Adams015aad92016-07-13 16:49:40 +000095 AnalysisMock analysisMock = AnalysisMock.get();
felly5be4dd62018-02-05 11:11:53 -080096 artifactFakeFunction = new ArtifactFakeFunction();
John Catere0d1d0e2017-11-28 20:47:41 -080097 pkgLocator =
98 new AtomicReference<>(
99 new PathPackageLocator(
100 outputBase,
tomluee6a6862018-01-17 14:36:26 -0800101 ImmutableList.of(Root.fromPath(rootDirectory)),
John Catere0d1d0e2017-11-28 20:47:41 -0800102 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000103 AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
104 new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
Ulf Adams015aad92016-07-13 16:49:40 +0000105 BlazeDirectories directories =
106 new BlazeDirectories(
Klaus Aehligc2499c42018-02-27 05:47:21 -0800107 new ServerDirectories(rootDirectory, outputBase, rootDirectory),
janakr3b63a4e2017-09-14 09:55:40 +0200108 rootDirectory,
109 analysisMock.getProductName());
nharmata3fb7d342018-02-23 11:37:51 -0800110 ExternalFilesHelper externalFilesHelper = ExternalFilesHelper.createForTesting(
Nathan Harmatad4f75942016-10-18 08:55:17 +0000111 pkgLocator, ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS, directories);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000112
Ulf Adams015aad92016-07-13 16:49:40 +0000113 ConfiguredRuleClassProvider ruleClassProvider = analysisMock.createRuleClassProvider();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000114 Map<SkyFunctionName, SkyFunction> skyFunctions = new HashMap<>();
Ulf Adamsc73051c62016-03-23 09:18:13 +0000115 skyFunctions.put(SkyFunctions.FILE_STATE, new FileStateFunction(
felly5be4dd62018-02-05 11:11:53 -0800116 new AtomicReference<>(), externalFilesHelper));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000117 skyFunctions.put(SkyFunctions.FILE, new FileFunction(pkgLocator));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000118 skyFunctions.put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction());
119 skyFunctions.put(
120 SkyFunctions.DIRECTORY_LISTING_STATE,
121 new DirectoryListingStateFunction(externalFilesHelper));
122 skyFunctions.put(
123 SkyFunctions.RECURSIVE_FILESYSTEM_TRAVERSAL, new RecursiveFilesystemTraversalFunction());
John Cater5e9ce942016-10-12 17:23:30 +0000124 skyFunctions.put(
125 SkyFunctions.PACKAGE_LOOKUP,
John Cater0c0735a2016-11-11 01:52:02 +0000126 new PackageLookupFunction(
127 deletedPackages,
128 CrossRepositoryLabelViolationStrategy.ERROR,
John Catere0d1d0e2017-11-28 20:47:41 -0800129 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Eric Fellheimer7ef96d72015-11-12 02:28:44 +0000130 skyFunctions.put(SkyFunctions.BLACKLISTED_PACKAGE_PREFIXES,
nharmatae4eb23f2017-12-05 09:27:45 -0800131 new BlacklistedPackagePrefixesFunction(
132 /*hardcodedBlacklistedPackagePrefixes=*/ ImmutableSet.of(),
133 /*additionalBlacklistedPackagePrefixesFile=*/ PathFragment.EMPTY_FRAGMENT));
Kristina Chodorowf9fdc8d2015-12-08 12:49:31 +0000134 skyFunctions.put(SkyFunctions.PACKAGE,
135 new PackageFunction(null, null, null, null, null, null, null));
Ulf Adams015aad92016-07-13 16:49:40 +0000136 skyFunctions.put(SkyFunctions.WORKSPACE_AST, new WorkspaceASTFunction(ruleClassProvider));
137 skyFunctions.put(
138 SkyFunctions.WORKSPACE_FILE,
139 new WorkspaceFileFunction(
140 ruleClassProvider,
141 analysisMock
janakr52d05e82017-09-22 13:27:14 -0400142 .getPackageFactoryBuilderForTesting(directories)
nharmatad922e652017-05-17 20:29:19 +0200143 .build(ruleClassProvider, scratch.getFileSystem()),
Kristina Chodorow5a2936f2016-04-22 17:02:19 +0000144 directories));
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000145 skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
John Caterb4f461e2016-10-25 16:16:35 +0000146 skyFunctions.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
kush95bf7c82017-08-30 00:27:35 +0200147 skyFunctions.put(
148 SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
149 new FileSymlinkInfiniteExpansionUniquenessFunction());
150 skyFunctions.put(
151 SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction());
felly5be4dd62018-02-05 11:11:53 -0800152 skyFunctions.put(
153 SkyFunctions.ARTIFACT,
154 artifactFakeFunction);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000155
156 progressReceiver = new RecordingEvaluationProgressReceiver();
janakr1cde8722017-10-10 03:22:21 +0200157 differencer = new SequencedRecordingDifferencer();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000158 evaluator = new InMemoryMemoizingEvaluator(skyFunctions, differencer, progressReceiver);
159 driver = new SequentialBuildDriver(evaluator);
160 PrecomputedValue.BUILD_ID.set(differencer, UUID.randomUUID());
161 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
162 }
163
164 private Artifact sourceArtifact(String path) {
tomluee6a6862018-01-17 14:36:26 -0800165 return new Artifact(
166 PathFragment.create(path), ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory)));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000167 }
168
169 private Artifact sourceArtifactUnderPackagePath(String path, String packagePath) {
170 return new Artifact(
tomlu1cdcdf92018-01-16 11:07:51 -0800171 PathFragment.create(path),
tomluee6a6862018-01-17 14:36:26 -0800172 ArtifactRoot.asSourceRoot(Root.fromPath(rootDirectory.getRelative(packagePath))));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000173 }
174
175 private Artifact derivedArtifact(String path) {
nharmatab4060b62017-04-04 17:11:39 +0000176 PathFragment execPath = PathFragment.create("out").getRelative(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000177 Artifact output =
178 new Artifact(
tomlu1cdcdf92018-01-16 11:07:51 -0800179 ArtifactRoot.asDerivedRoot(rootDirectory, rootDirectory.getRelative("out")),
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000180 execPath);
181 return output;
182 }
183
184 private static RootedPath rootedPath(Artifact artifact) {
tomluee6a6862018-01-17 14:36:26 -0800185 return RootedPath.toRootedPath(artifact.getRoot().getRoot(), artifact.getRootRelativePath());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000186 }
187
188 private RootedPath rootedPath(String path, String packagePath) {
nharmatab4060b62017-04-04 17:11:39 +0000189 return RootedPath.toRootedPath(
tomluee6a6862018-01-17 14:36:26 -0800190 Root.fromPath(rootDirectory.getRelative(packagePath)), PathFragment.create(path));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000191 }
192
193 private static RootedPath childOf(Artifact artifact, String relative) {
194 return RootedPath.toRootedPath(
tomluee6a6862018-01-17 14:36:26 -0800195 artifact.getRoot().getRoot(), artifact.getRootRelativePath().getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000196 }
197
198 private static RootedPath childOf(RootedPath path, String relative) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800199 return RootedPath.toRootedPath(
200 path.getRoot(), path.getRootRelativePath().getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000201 }
202
203 private static RootedPath parentOf(RootedPath path) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800204 PathFragment parent =
205 Preconditions.checkNotNull(path.getRootRelativePath().getParentDirectory());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000206 return RootedPath.toRootedPath(path.getRoot(), parent);
207 }
208
209 private static RootedPath siblingOf(RootedPath path, String relative) {
tomlu8cc5dcf2018-01-19 09:28:06 -0800210 PathFragment parent =
211 Preconditions.checkNotNull(path.getRootRelativePath().getParentDirectory());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000212 return RootedPath.toRootedPath(path.getRoot(), parent.getRelative(relative));
213 }
214
215 private static RootedPath siblingOf(Artifact artifact, String relative) {
216 PathFragment parent =
217 Preconditions.checkNotNull(artifact.getRootRelativePath().getParentDirectory());
tomluee6a6862018-01-17 14:36:26 -0800218 return RootedPath.toRootedPath(artifact.getRoot().getRoot(), parent.getRelative(relative));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000219 }
220
221 private void createFile(Path path, String... contents) throws Exception {
222 if (!path.getParentDirectory().exists()) {
223 scratch.dir(path.getParentDirectory().getPathString());
224 }
225 scratch.file(path.getPathString(), contents);
226 }
227
228 private void createFile(Artifact artifact, String... contents) throws Exception {
229 createFile(artifact.getPath(), contents);
230 }
231
232 private RootedPath createFile(RootedPath path, String... contents) throws Exception {
233 scratch.dir(parentOf(path).asPath().getPathString());
234 createFile(path.asPath(), contents);
235 return path;
236 }
237
238 private static TraversalRequest fileLikeRoot(Artifact file, PackageBoundaryMode pkgBoundaryMode) {
janakr5fb2a482018-03-02 17:48:57 -0800239 return TraversalRequest.create(
240 DirectTraversalRoot.forFileOrDirectory(file),
241 !file.isSourceArtifact(),
242 pkgBoundaryMode,
243 false,
244 null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000245 }
246
247 private static TraversalRequest pkgRoot(
248 RootedPath pkgDirectory, PackageBoundaryMode pkgBoundaryMode) {
janakr5fb2a482018-03-02 17:48:57 -0800249 return TraversalRequest.create(
250 DirectTraversalRoot.forRootedPath(pkgDirectory), false, pkgBoundaryMode, true, null);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000251 }
252
253 private <T extends SkyValue> EvaluationResult<T> eval(SkyKey key) throws Exception {
254 return driver.evaluate(
255 ImmutableList.of(key),
256 false,
257 SkyframeExecutor.DEFAULT_THREAD_COUNT,
258 NullEventHandler.INSTANCE);
259 }
260
261 private RecursiveFilesystemTraversalValue evalTraversalRequest(TraversalRequest params)
262 throws Exception {
janakr5fb2a482018-03-02 17:48:57 -0800263 EvaluationResult<RecursiveFilesystemTraversalValue> result = eval(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000264 assertThat(result.hasError()).isFalse();
janakr5fb2a482018-03-02 17:48:57 -0800265 return result.get(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000266 }
267
268 /**
269 * Asserts that the requested SkyValue can be built and results in the expected set of files.
270 *
271 * <p>The metadata of files is ignored in comparing the actual results with the expected ones.
272 * The returned object however contains the actual metadata.
273 */
274 @SafeVarargs
275 private final RecursiveFilesystemTraversalValue traverseAndAssertFiles(
276 TraversalRequest params, ResolvedFile... expectedFilesIgnoringMetadata) throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000277 RecursiveFilesystemTraversalValue result = evalTraversalRequest(params);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000278 Set<ResolvedFile> actual = new HashSet<>();
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000279 for (ResolvedFile act : result.getTransitiveFiles()) {
280 // Strip metadata so only the type and path of the objects are compared.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000281 actual.add(act.stripMetadataForTesting());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000282 }
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000283 assertThat(actual).containsExactly((Object[]) expectedFilesIgnoringMetadata);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000284
285 // The returned object still has the unstripped metadata.
286 return result;
287 }
288
felly5be4dd62018-02-05 11:11:53 -0800289 private void appendToFile(RootedPath rootedPath, SkyKey toInvalidate, String content)
290 throws Exception {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000291 Path path = rootedPath.asPath();
292 if (path.exists()) {
293 try (OutputStream os = path.getOutputStream(/*append=*/ true)) {
294 os.write(content.getBytes(StandardCharsets.UTF_8));
295 }
felly5be4dd62018-02-05 11:11:53 -0800296 differencer.invalidate(ImmutableList.of(toInvalidate));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000297 } else {
298 createFile(path, content);
299 }
300 }
301
felly5be4dd62018-02-05 11:11:53 -0800302 private void appendToFile(RootedPath rootedPath, String content) throws Exception {
303 appendToFile(rootedPath, FileStateValue.key(rootedPath), content);
304 }
305
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000306 private void appendToFile(Artifact file, String content) throws Exception {
felly5be4dd62018-02-05 11:11:53 -0800307 SkyKey key = file.isSourceArtifact()
308 ? FileStateValue.key(rootedPath(file))
309 : ArtifactSkyKey.key(file, true);
310 appendToFile(rootedPath(file), key, content);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000311 }
312
313 private void invalidateDirectory(RootedPath path) {
314 differencer.invalidate(ImmutableList.of(DirectoryListingStateValue.key(path)));
315 }
316
felly5be4dd62018-02-05 11:11:53 -0800317 private void invalidateOutputArtifact(Artifact output) {
318 assertThat(output.isSourceArtifact()).isFalse();
319 differencer.invalidate(ImmutableList.of(ArtifactSkyKey.key(output, true)));
320 }
321
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000322 private void invalidateDirectory(Artifact directoryArtifact) {
323 invalidateDirectory(rootedPath(directoryArtifact));
324 }
325
326 private static final class RecordingEvaluationProgressReceiver
nharmata8040b0b2017-05-09 13:53:19 -0400327 extends EvaluationProgressReceiver.NullEvaluationProgressReceiver {
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000328 Set<SkyKey> invalidations;
329 Set<SkyValue> evaluations;
330
331 RecordingEvaluationProgressReceiver() {
332 clear();
333 }
334
335 void clear() {
336 invalidations = Sets.newConcurrentHashSet();
337 evaluations = Sets.newConcurrentHashSet();
338 }
339
340 @Override
341 public void invalidated(SkyKey skyKey, InvalidationState state) {
342 invalidations.add(skyKey);
343 }
344
345 @Override
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000346 public void evaluated(
347 SkyKey skyKey, Supplier<SkyValue> skyValueSupplier, EvaluationState state) {
348 SkyValue value = skyValueSupplier.get();
349 if (value != null) {
350 evaluations.add(value);
351 }
352 }
353 }
354
Laszlo Csomor207140f2015-12-07 15:07:33 +0000355 private void assertTraversalRootHashesAre(
356 boolean equal, RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b)
357 throws Exception {
358 if (equal) {
359 assertThat(a.getResolvedRoot().get().hashCode())
360 .isEqualTo(b.getResolvedRoot().get().hashCode());
361 } else {
362 assertThat(a.getResolvedRoot().get().hashCode())
363 .isNotEqualTo(b.getResolvedRoot().get().hashCode());
364 }
365 }
366
367 private void assertTraversalRootHashesAreEqual(
368 RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b) throws Exception {
369 assertTraversalRootHashesAre(true, a, b);
370 }
371
372 private void assertTraversalRootHashesAreNotEqual(
373 RecursiveFilesystemTraversalValue a, RecursiveFilesystemTraversalValue b) throws Exception {
374 assertTraversalRootHashesAre(false, a, b);
375 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000376
377 private void assertTraversalOfFile(Artifact rootArtifact) throws Exception {
378 TraversalRequest traversalRoot = fileLikeRoot(rootArtifact, DONT_CROSS);
379 RootedPath rootedPath = createFile(rootedPath(rootArtifact), "foo");
380
381 // Assert that the SkyValue is built and looks right.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000382 ResolvedFile expected = regularFileForTesting(rootedPath);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000383 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(traversalRoot, expected);
384 assertThat(progressReceiver.invalidations).isEmpty();
385 assertThat(progressReceiver.evaluations).contains(v1);
386 progressReceiver.clear();
387
388 // Edit the file and verify that the value is rebuilt.
389 appendToFile(rootArtifact, "bar");
390 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(traversalRoot, expected);
janakr5fb2a482018-03-02 17:48:57 -0800391 assertThat(progressReceiver.invalidations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000392 assertThat(progressReceiver.evaluations).contains(v2);
393 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000394 assertTraversalRootHashesAreNotEqual(v1, v2);
395
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000396 progressReceiver.clear();
397 }
398
Florian Weikert92b22362015-12-03 10:17:18 +0000399 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000400 public void testTraversalOfSourceFile() throws Exception {
401 assertTraversalOfFile(sourceArtifact("foo/bar.txt"));
402 }
403
Florian Weikert92b22362015-12-03 10:17:18 +0000404 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000405 public void testTraversalOfGeneratedFile() throws Exception {
406 assertTraversalOfFile(derivedArtifact("foo/bar.txt"));
407 }
408
Florian Weikert92b22362015-12-03 10:17:18 +0000409 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000410 public void testTraversalOfSymlinkToFile() throws Exception {
411 Artifact linkNameArtifact = sourceArtifact("foo/baz/qux.sym");
412 Artifact linkTargetArtifact = sourceArtifact("foo/bar/baz.txt");
nharmatab4060b62017-04-04 17:11:39 +0000413 PathFragment linkValue = PathFragment.create("../bar/baz.txt");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000414 TraversalRequest traversalRoot = fileLikeRoot(linkNameArtifact, DONT_CROSS);
415 createFile(linkTargetArtifact);
416 scratch.dir(linkNameArtifact.getExecPath().getParentDirectory().getPathString());
417 rootDirectory.getRelative(linkNameArtifact.getExecPath()).createSymbolicLink(linkValue);
418
419 // Assert that the SkyValue is built and looks right.
420 RootedPath symlinkNamePath = rootedPath(linkNameArtifact);
421 RootedPath symlinkTargetPath = rootedPath(linkTargetArtifact);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000422 ResolvedFile expected = symlinkToFileForTesting(symlinkTargetPath, symlinkNamePath, linkValue);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000423 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(traversalRoot, expected);
424 assertThat(progressReceiver.invalidations).isEmpty();
425 assertThat(progressReceiver.evaluations).contains(v1);
426 progressReceiver.clear();
427
428 // Edit the target of the symlink and verify that the value is rebuilt.
429 appendToFile(linkTargetArtifact, "bar");
430 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(traversalRoot, expected);
janakr5fb2a482018-03-02 17:48:57 -0800431 assertThat(progressReceiver.invalidations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000432 assertThat(progressReceiver.evaluations).contains(v2);
433 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000434 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000435 }
436
Florian Weikert92b22362015-12-03 10:17:18 +0000437 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000438 public void testTraversalOfTransitiveSymlinkToFile() throws Exception {
439 Artifact directLinkArtifact = sourceArtifact("direct/file.sym");
440 Artifact transitiveLinkArtifact = sourceArtifact("transitive/sym.sym");
441 RootedPath fileA = createFile(rootedPath(sourceArtifact("a/file.a")));
442 RootedPath directLink = rootedPath(directLinkArtifact);
443 RootedPath transitiveLink = rootedPath(transitiveLinkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000444 PathFragment directLinkPath = PathFragment.create("../a/file.a");
445 PathFragment transitiveLinkPath = PathFragment.create("../direct/file.sym");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000446
447 parentOf(directLink).asPath().createDirectory();
448 parentOf(transitiveLink).asPath().createDirectory();
449 directLink.asPath().createSymbolicLink(directLinkPath);
450 transitiveLink.asPath().createSymbolicLink(transitiveLinkPath);
451
452 traverseAndAssertFiles(
453 fileLikeRoot(directLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000454 symlinkToFileForTesting(fileA, directLink, directLinkPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000455
456 traverseAndAssertFiles(
457 fileLikeRoot(transitiveLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000458 symlinkToFileForTesting(fileA, transitiveLink, transitiveLinkPath));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000459 }
460
461 private void assertTraversalOfDirectory(Artifact directoryArtifact) throws Exception {
462 // Create files under the directory.
463 // Use the root + root-relative path of the rootArtifact to create these files, rather than
464 // using the rootDirectory + execpath of the rootArtifact. The resulting paths are the same
465 // but the RootedPaths are different:
466 // in the 1st case, it is: RootedPath(/root/execroot, relative), in the second it is
467 // in the 2nd case, it is: RootedPath(/root, execroot/relative).
468 // Creating the files will also create the parent directories.
469 RootedPath file1 = createFile(childOf(directoryArtifact, "bar.txt"));
470 RootedPath file2 = createFile(childOf(directoryArtifact, "baz/qux.txt"));
471
472 TraversalRequest traversalRoot = fileLikeRoot(directoryArtifact, DONT_CROSS);
473
474 // Assert that the SkyValue is built and looks right.
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000475 ResolvedFile expected1 = regularFileForTesting(file1);
476 ResolvedFile expected2 = regularFileForTesting(file2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000477 RecursiveFilesystemTraversalValue v1 =
478 traverseAndAssertFiles(traversalRoot, expected1, expected2);
479 assertThat(progressReceiver.invalidations).isEmpty();
480 assertThat(progressReceiver.evaluations).contains(v1);
481 progressReceiver.clear();
482
483 // Add a new file to the directory and see that the value is rebuilt.
Laszlo Csomora31e0352018-03-09 04:51:38 -0800484 TimestampGranularityMonitor.waitForTimestampGranularity(
485 directoryArtifact.getPath().stat().getLastChangeTime(),
486 BlazeClock.instance(),
487 OutErr.SYSTEM_OUT_ERR);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000488 RootedPath file3 = createFile(childOf(directoryArtifact, "foo.txt"));
felly5be4dd62018-02-05 11:11:53 -0800489 if (directoryArtifact.isSourceArtifact()) {
490 invalidateDirectory(directoryArtifact);
491 } else {
492 invalidateOutputArtifact(directoryArtifact);
493 }
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000494 ResolvedFile expected3 = regularFileForTesting(file3);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000495 RecursiveFilesystemTraversalValue v2 =
496 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
janakr5fb2a482018-03-02 17:48:57 -0800497 assertThat(progressReceiver.invalidations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000498 assertThat(progressReceiver.evaluations).contains(v2);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000499 // Directories always have the same hash code, but that is fine because their contents are also
500 // part of the RecursiveFilesystemTraversalValue, so v1 and v2 are unequal.
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000501 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000502 assertTraversalRootHashesAreEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000503 progressReceiver.clear();
504
505 // Edit a file in the directory and see that the value is rebuilt.
felly5be4dd62018-02-05 11:11:53 -0800506 RecursiveFilesystemTraversalValue v3;
507 if (directoryArtifact.isSourceArtifact()) {
508 SkyKey toInvalidate = FileStateValue.key(file1);
509 appendToFile(file1, toInvalidate, "bar");
510 v3 = traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
janakr5fb2a482018-03-02 17:48:57 -0800511 assertThat(progressReceiver.invalidations).contains(traversalRoot);
felly5be4dd62018-02-05 11:11:53 -0800512 assertThat(progressReceiver.evaluations).contains(v3);
513 assertThat(v3).isNotEqualTo(v2);
514 // Directories always have the same hash code, but that is fine because their contents are
515 // also part of the RecursiveFilesystemTraversalValue, so v2 and v3 are unequal.
516 assertTraversalRootHashesAreEqual(v2, v3);
517 progressReceiver.clear();
518 } else {
519 // Dependency checking of output directories is unsound. Specifically, the directory mtime
520 // is not changed when a contained file is modified.
521 v3 = v2;
522 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000523
524 // Add a new file *outside* of the directory and see that the value is *not* rebuilt.
525 Artifact someFile = sourceArtifact("somewhere/else/a.file");
526 createFile(someFile, "new file");
527 appendToFile(someFile, "not all changes are treated equal");
528 RecursiveFilesystemTraversalValue v4 =
529 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
530 assertThat(v4).isEqualTo(v3);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000531 assertTraversalRootHashesAreEqual(v3, v4);
janakr5fb2a482018-03-02 17:48:57 -0800532 assertThat(progressReceiver.invalidations).doesNotContain(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000533 }
534
Florian Weikert92b22362015-12-03 10:17:18 +0000535 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000536 public void testTraversalOfSourceDirectory() throws Exception {
537 assertTraversalOfDirectory(sourceArtifact("dir"));
538 }
539
Florian Weikert92b22362015-12-03 10:17:18 +0000540 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000541 public void testTraversalOfGeneratedDirectory() throws Exception {
542 assertTraversalOfDirectory(derivedArtifact("dir"));
543 }
544
Florian Weikert92b22362015-12-03 10:17:18 +0000545 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000546 public void testTraversalOfTransitiveSymlinkToDirectory() throws Exception {
547 Artifact directLinkArtifact = sourceArtifact("direct/dir.sym");
548 Artifact transitiveLinkArtifact = sourceArtifact("transitive/sym.sym");
549 RootedPath fileA = createFile(rootedPath(sourceArtifact("a/file.a")));
550 RootedPath directLink = rootedPath(directLinkArtifact);
551 RootedPath transitiveLink = rootedPath(transitiveLinkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000552 PathFragment directLinkPath = PathFragment.create("../a");
553 PathFragment transitiveLinkPath = PathFragment.create("../direct/dir.sym");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000554
555 parentOf(directLink).asPath().createDirectory();
556 parentOf(transitiveLink).asPath().createDirectory();
557 directLink.asPath().createSymbolicLink(directLinkPath);
558 transitiveLink.asPath().createSymbolicLink(transitiveLinkPath);
559
560 // Expect the file as if was a child of the direct symlink, not of the actual directory.
561 traverseAndAssertFiles(
562 fileLikeRoot(directLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000563 symlinkToDirectoryForTesting(parentOf(fileA), directLink, directLinkPath),
564 regularFileForTesting(childOf(directLinkArtifact, "file.a")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000565
566 // Expect the file as if was a child of the transitive symlink, not of the actual directory.
567 traverseAndAssertFiles(
568 fileLikeRoot(transitiveLinkArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000569 symlinkToDirectoryForTesting(parentOf(fileA), transitiveLink, transitiveLinkPath),
570 regularFileForTesting(childOf(transitiveLinkArtifact, "file.a")));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000571 }
572
Florian Weikert92b22362015-12-03 10:17:18 +0000573 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000574 public void testTraversePackage() throws Exception {
575 Artifact buildFile = sourceArtifact("pkg/BUILD");
576 RootedPath buildFilePath = createFile(rootedPath(buildFile));
577 RootedPath file1 = createFile(siblingOf(buildFile, "subdir/file.a"));
578
579 traverseAndAssertFiles(
580 pkgRoot(parentOf(buildFilePath), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000581 regularFileForTesting(buildFilePath),
582 regularFileForTesting(file1));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000583 }
584
Florian Weikert92b22362015-12-03 10:17:18 +0000585 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000586 public void testTraversalOfSymlinkToDirectory() throws Exception {
587 Artifact linkNameArtifact = sourceArtifact("link/foo.sym");
588 Artifact linkTargetArtifact = sourceArtifact("dir");
589 RootedPath linkName = rootedPath(linkNameArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000590 PathFragment linkValue = PathFragment.create("../dir");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000591 RootedPath file1 = createFile(childOf(linkTargetArtifact, "file.1"));
592 createFile(childOf(linkTargetArtifact, "sub/file.2"));
593 scratch.dir(parentOf(linkName).asPath().getPathString());
594 linkName.asPath().createSymbolicLink(linkValue);
595
596 // Assert that the SkyValue is built and looks right.
597 TraversalRequest traversalRoot = fileLikeRoot(linkNameArtifact, DONT_CROSS);
598 ResolvedFile expected1 =
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000599 symlinkToDirectoryForTesting(rootedPath(linkTargetArtifact), linkName, linkValue);
600 ResolvedFile expected2 = regularFileForTesting(childOf(linkNameArtifact, "file.1"));
601 ResolvedFile expected3 = regularFileForTesting(childOf(linkNameArtifact, "sub/file.2"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000602 // We expect to see all the files from the symlink'd directory, under the symlink's path, not
603 // under the symlink target's path.
604 RecursiveFilesystemTraversalValue v1 =
605 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3);
606 assertThat(progressReceiver.invalidations).isEmpty();
607 assertThat(progressReceiver.evaluations).contains(v1);
608 progressReceiver.clear();
609
610 // Add a new file to the directory and see that the value is rebuilt.
611 createFile(childOf(linkTargetArtifact, "file.3"));
612 invalidateDirectory(linkTargetArtifact);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000613 ResolvedFile expected4 = regularFileForTesting(childOf(linkNameArtifact, "file.3"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000614 RecursiveFilesystemTraversalValue v2 =
615 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
janakr5fb2a482018-03-02 17:48:57 -0800616 assertThat(progressReceiver.invalidations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000617 assertThat(progressReceiver.evaluations).contains(v2);
618 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000619 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000620 progressReceiver.clear();
621
622 // Edit a file in the directory and see that the value is rebuilt.
623 appendToFile(file1, "bar");
624 RecursiveFilesystemTraversalValue v3 =
625 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
janakr5fb2a482018-03-02 17:48:57 -0800626 assertThat(progressReceiver.invalidations).contains(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000627 assertThat(progressReceiver.evaluations).contains(v3);
628 assertThat(v3).isNotEqualTo(v2);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000629 assertTraversalRootHashesAreNotEqual(v2, v3);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000630 progressReceiver.clear();
631
632 // Add a new file *outside* of the directory and see that the value is *not* rebuilt.
633 Artifact someFile = sourceArtifact("somewhere/else/a.file");
634 createFile(someFile, "new file");
635 appendToFile(someFile, "not all changes are treated equal");
636 RecursiveFilesystemTraversalValue v4 =
637 traverseAndAssertFiles(traversalRoot, expected1, expected2, expected3, expected4);
638 assertThat(v4).isEqualTo(v3);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000639 assertTraversalRootHashesAreEqual(v3, v4);
janakr5fb2a482018-03-02 17:48:57 -0800640 assertThat(progressReceiver.invalidations).doesNotContain(traversalRoot);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000641 }
642
Florian Weikert92b22362015-12-03 10:17:18 +0000643 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000644 public void testTraversalOfDanglingSymlink() throws Exception {
645 Artifact linkArtifact = sourceArtifact("a/dangling.sym");
646 RootedPath link = rootedPath(linkArtifact);
nharmatab4060b62017-04-04 17:11:39 +0000647 PathFragment linkTarget = PathFragment.create("non_existent");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000648 parentOf(link).asPath().createDirectory();
649 link.asPath().createSymbolicLink(linkTarget);
650 traverseAndAssertFiles(
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000651 fileLikeRoot(linkArtifact, DONT_CROSS), danglingSymlinkForTesting(link, linkTarget));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000652 }
653
Florian Weikert92b22362015-12-03 10:17:18 +0000654 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000655 public void testTraversalOfDanglingSymlinkInADirectory() throws Exception {
656 Artifact dirArtifact = sourceArtifact("a");
657 RootedPath file = createFile(childOf(dirArtifact, "file.txt"));
658 RootedPath link = rootedPath(sourceArtifact("a/dangling.sym"));
nharmatab4060b62017-04-04 17:11:39 +0000659 PathFragment linkTarget = PathFragment.create("non_existent");
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000660 parentOf(link).asPath().createDirectory();
661 link.asPath().createSymbolicLink(linkTarget);
662 traverseAndAssertFiles(
663 fileLikeRoot(dirArtifact, DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000664 regularFileForTesting(file),
665 danglingSymlinkForTesting(link, linkTarget));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000666 }
667
668 private void assertTraverseSubpackages(PackageBoundaryMode traverseSubpackages) throws Exception {
669 Artifact pkgDirArtifact = sourceArtifact("pkg1/foo");
670 Artifact subpkgDirArtifact = sourceArtifact("pkg1/foo/subdir/subpkg");
671 RootedPath pkgBuildFile = childOf(pkgDirArtifact, "BUILD");
672 RootedPath subpkgBuildFile = childOf(subpkgDirArtifact, "BUILD");
673 scratch.dir(rootedPath(pkgDirArtifact).asPath().getPathString());
674 scratch.dir(rootedPath(subpkgDirArtifact).asPath().getPathString());
675 createFile(pkgBuildFile);
676 createFile(subpkgBuildFile);
677
678 TraversalRequest traversalRoot = pkgRoot(parentOf(pkgBuildFile), traverseSubpackages);
679
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000680 ResolvedFile expected1 = regularFileForTesting(pkgBuildFile);
681 ResolvedFile expected2 = regularFileForTesting(subpkgBuildFile);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000682 switch (traverseSubpackages) {
683 case CROSS:
684 traverseAndAssertFiles(traversalRoot, expected1, expected2);
685 break;
686 case DONT_CROSS:
687 traverseAndAssertFiles(traversalRoot, expected1);
688 break;
689 case REPORT_ERROR:
janakr5fb2a482018-03-02 17:48:57 -0800690 SkyKey key = traversalRoot;
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000691 EvaluationResult<SkyValue> result = eval(key);
692 assertThat(result.hasError()).isTrue();
lberkiaea56b32017-05-30 12:35:33 +0200693 assertThat(result.getError().getException())
694 .hasMessageThat()
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000695 .contains("crosses package boundary into package rooted at");
696 break;
697 default:
698 throw new IllegalStateException(traverseSubpackages.toString());
699 }
700 }
701
Florian Weikert92b22362015-12-03 10:17:18 +0000702 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000703 public void testTraverseSubpackages() throws Exception {
704 assertTraverseSubpackages(CROSS);
705 }
706
Florian Weikert92b22362015-12-03 10:17:18 +0000707 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000708 public void testDoNotTraverseSubpackages() throws Exception {
709 assertTraverseSubpackages(DONT_CROSS);
710 }
711
Florian Weikert92b22362015-12-03 10:17:18 +0000712 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000713 public void testReportErrorWhenTraversingSubpackages() throws Exception {
714 assertTraverseSubpackages(REPORT_ERROR);
715 }
716
Florian Weikert92b22362015-12-03 10:17:18 +0000717 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000718 public void testSwitchPackageRootsWhenUsingMultiplePackagePaths() throws Exception {
719 // Layout:
720 // pp1://a/BUILD
721 // pp1://a/file.a
722 // pp1://a/b.sym -> b/ (only created later)
723 // pp1://a/b/
724 // pp1://a/b/file.fake
725 // pp1://a/subdir/file.b
726 //
727 // pp2://a/BUILD
728 // pp2://a/b/
729 // pp2://a/b/BUILD
730 // pp2://a/b/file.a
731 // pp2://a/subdir.fake/
732 // pp2://a/subdir.fake/file.fake
733 //
734 // Notice that pp1://a/b will be overlaid by pp2://a/b as the latter has a BUILD file and that
735 // takes precedence. On the other hand the package definition pp2://a/BUILD will be ignored
736 // since package //a is already defined under pp1.
737 //
738 // Notice also that pp1://a/b.sym is a relative symlink pointing to b/. This should be resolved
739 // to the definition of //a/b/ under pp1, not under pp2.
740
741 // Set the package paths.
John Catere0d1d0e2017-11-28 20:47:41 -0800742 pkgLocator.set(
743 new PathPackageLocator(
744 outputBase,
tomluee6a6862018-01-17 14:36:26 -0800745 ImmutableList.of(
746 Root.fromPath(rootDirectory.getRelative("pp1")),
747 Root.fromPath(rootDirectory.getRelative("pp2"))),
John Catere0d1d0e2017-11-28 20:47:41 -0800748 BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000749 PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
750
751 Artifact aBuildArtifact = sourceArtifactUnderPackagePath("a/BUILD", "pp1");
752 Artifact bBuildArtifact = sourceArtifactUnderPackagePath("a/b/BUILD", "pp2");
753
754 RootedPath pp1aBuild = createFile(rootedPath(aBuildArtifact));
755 RootedPath pp1aFileA = createFile(siblingOf(pp1aBuild, "file.a"));
756 RootedPath pp1bFileFake = createFile(siblingOf(pp1aBuild, "b/file.fake"));
757 RootedPath pp1aSubdirFileB = createFile(siblingOf(pp1aBuild, "subdir/file.b"));
758
759 RootedPath pp2aBuild = createFile(rootedPath("a/BUILD", "pp2"));
760 RootedPath pp2bBuild = createFile(rootedPath(bBuildArtifact));
761 RootedPath pp2bFileA = createFile(siblingOf(pp2bBuild, "file.a"));
762 createFile(siblingOf(pp2aBuild, "subdir.fake/file.fake"));
763
764 // Traverse //a including subpackages. The result should contain the pp1-definition of //a and
765 // the pp2-definition of //a/b.
766 traverseAndAssertFiles(
767 pkgRoot(parentOf(rootedPath(aBuildArtifact)), CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000768 regularFileForTesting(pp1aBuild),
769 regularFileForTesting(pp1aFileA),
770 regularFileForTesting(pp1aSubdirFileB),
771 regularFileForTesting(pp2bBuild),
772 regularFileForTesting(pp2bFileA));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000773
774 // Traverse //a excluding subpackages. The result should only contain files from //a and not
775 // from //a/b.
776 traverseAndAssertFiles(
777 pkgRoot(parentOf(rootedPath(aBuildArtifact)), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000778 regularFileForTesting(pp1aBuild),
779 regularFileForTesting(pp1aFileA),
780 regularFileForTesting(pp1aSubdirFileB));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000781
782 // Create a relative symlink pp1://a/b.sym -> b/. It will be resolved to the subdirectory
783 // pp1://a/b, even though a package definition pp2://a/b exists.
784 RootedPath pp1aBsym = siblingOf(pp1aFileA, "b.sym");
nharmatab4060b62017-04-04 17:11:39 +0000785 pp1aBsym.asPath().createSymbolicLink(PathFragment.create("b"));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000786 invalidateDirectory(parentOf(pp1aBsym));
787
788 // Traverse //a excluding subpackages. The relative symlink //a/b.sym points to the subdirectory
789 // a/b, i.e. the pp1-definition, even though there is a pp2-defined package //a/b and we expect
790 // to see b.sym/b.fake (not b/b.fake).
791 traverseAndAssertFiles(
792 pkgRoot(parentOf(rootedPath(aBuildArtifact)), DONT_CROSS),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000793 regularFileForTesting(pp1aBuild),
794 regularFileForTesting(pp1aFileA),
795 regularFileForTesting(childOf(pp1aBsym, "file.fake")),
nharmatab4060b62017-04-04 17:11:39 +0000796 symlinkToDirectoryForTesting(parentOf(pp1bFileFake), pp1aBsym, PathFragment.create("b")),
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000797 regularFileForTesting(pp1aSubdirFileB));
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000798 }
799
Florian Weikert92b22362015-12-03 10:17:18 +0000800 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000801 public void testFileDigestChangeCausesRebuild() throws Exception {
802 Artifact artifact = sourceArtifact("foo/bar.txt");
803 RootedPath path = rootedPath(artifact);
804 createFile(path, "hello");
805
806 // Assert that the SkyValue is built and looks right.
807 TraversalRequest params = fileLikeRoot(artifact, DONT_CROSS);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000808 ResolvedFile expected = regularFileForTesting(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000809 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(params, expected);
810 assertThat(progressReceiver.evaluations).contains(v1);
811 progressReceiver.clear();
812
813 // Change the digest of the file. See that the value is rebuilt.
814 appendToFile(path, "world");
815 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(params, expected);
janakr5fb2a482018-03-02 17:48:57 -0800816 assertThat(progressReceiver.invalidations).contains(params);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000817 assertThat(v2).isNotEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000818 assertTraversalRootHashesAreNotEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000819 }
820
Florian Weikert92b22362015-12-03 10:17:18 +0000821 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000822 public void testFileMtimeChangeDoesNotCauseRebuildIfDigestIsUnchanged() throws Exception {
823 Artifact artifact = sourceArtifact("foo/bar.txt");
824 RootedPath path = rootedPath(artifact);
825 createFile(path, "hello");
826
827 // Assert that the SkyValue is built and looks right.
828 TraversalRequest params = fileLikeRoot(artifact, DONT_CROSS);
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000829 ResolvedFile expected = regularFileForTesting(path);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000830 RecursiveFilesystemTraversalValue v1 = traverseAndAssertFiles(params, expected);
831 assertThat(progressReceiver.evaluations).contains(v1);
832 progressReceiver.clear();
833
834 // Change the mtime of the file but not the digest. See that the value is *not* rebuilt.
Laszlo Csomora31e0352018-03-09 04:51:38 -0800835 TimestampGranularityMonitor.waitForTimestampGranularity(
836 path.asPath().stat().getLastChangeTime(), BlazeClock.instance(), OutErr.SYSTEM_OUT_ERR);
837 path.asPath().setLastModifiedTime(System.currentTimeMillis());
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000838 RecursiveFilesystemTraversalValue v2 = traverseAndAssertFiles(params, expected);
839 assertThat(v2).isEqualTo(v1);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000840 assertTraversalRootHashesAreEqual(v1, v2);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000841 }
842
Florian Weikert92b22362015-12-03 10:17:18 +0000843 @Test
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000844 public void testGeneratedDirectoryConflictsWithPackage() throws Exception {
845 Artifact genDir = derivedArtifact("a/b");
846 createFile(rootedPath(sourceArtifact("a/b/c/file.real")));
847 createFile(rootedPath(derivedArtifact("a/b/c/file.fake")));
848 createFile(sourceArtifact("a/b/c/BUILD"));
849
janakr5fb2a482018-03-02 17:48:57 -0800850 SkyKey key = fileLikeRoot(genDir, CROSS);
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000851 EvaluationResult<SkyValue> result = eval(key);
852 assertThat(result.hasError()).isTrue();
853 ErrorInfo error = result.getError(key);
nharmatabea67e92017-06-16 00:26:27 +0200854 assertThat(error.isTransitivelyTransient()).isFalse();
lberkiaea56b32017-05-30 12:35:33 +0200855 assertThat(error.getException())
856 .hasMessageThat()
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000857 .contains("Generated directory a/b/c conflicts with package under the same path.");
858 }
kush95bf7c82017-08-30 00:27:35 +0200859
860 @Test
861 public void unboundedSymlinkExpansionError() throws Exception {
862 Artifact bazLink = sourceArtifact("foo/baz.sym");
863 Path parentDir = scratch.dir("foo");
864 bazLink.getPath().createSymbolicLink(parentDir);
janakr5fb2a482018-03-02 17:48:57 -0800865 SkyKey key = pkgRoot(parentOf(rootedPath(bazLink)), DONT_CROSS);
kush95bf7c82017-08-30 00:27:35 +0200866 EvaluationResult<SkyValue> result = eval(key);
867 assertThat(result.hasError()).isTrue();
868 ErrorInfo error = result.getError(key);
869 assertThat(error.getException()).isInstanceOf(FileOperationException.class);
870 assertThat(error.getException()).hasMessageThat().contains("Infinite symlink expansion");
871 }
872
873 @Test
874 public void symlinkChainError() throws Exception {
875 scratch.dir("a");
876 Artifact fooLink = sourceArtifact("a/foo.sym");
877 Artifact barLink = sourceArtifact("a/bar.sym");
878 Artifact bazLink = sourceArtifact("a/baz.sym");
879 fooLink.getPath().createSymbolicLink(barLink.getPath());
880 barLink.getPath().createSymbolicLink(bazLink.getPath());
881 bazLink.getPath().createSymbolicLink(fooLink.getPath());
882
janakr5fb2a482018-03-02 17:48:57 -0800883 SkyKey key = pkgRoot(parentOf(rootedPath(bazLink)), DONT_CROSS);
kush95bf7c82017-08-30 00:27:35 +0200884 EvaluationResult<SkyValue> result = eval(key);
885 assertThat(result.hasError()).isTrue();
886 ErrorInfo error = result.getError(key);
887 assertThat(error.getException()).isInstanceOf(FileOperationException.class);
888 assertThat(error.getException()).hasMessageThat().contains("Symlink cycle");
889 }
felly5be4dd62018-02-05 11:11:53 -0800890
891 private static class ArtifactFakeFunction implements SkyFunction {
892 @Nullable
893 @Override
894 public SkyValue compute(SkyKey skyKey, Environment env)
895 throws SkyFunctionException, InterruptedException {
felly1eeba9c2018-02-05 14:32:51 -0800896 ArtifactSkyKey artifactKey = (ArtifactSkyKey) skyKey.argument();
897 Artifact artifact = artifactKey.getArtifact();
felly5be4dd62018-02-05 11:11:53 -0800898 try {
899 return FileArtifactValue.create(artifact.getPath());
900 } catch (IOException e) {
901 throw new SkyFunctionException(e, Transience.PERSISTENT){};
902 }
903 }
904
905 @Nullable
906 @Override
907 public String extractTag(SkyKey skyKey) {
908 return null;
909 }
910 }
Han-Wen Nienhuys81b90832015-10-26 16:57:27 +0000911}