blob: cfd2ce0695ed99122789f3bb841fd48d2117cf81 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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 com.google.common.annotations.VisibleForTesting;
17import com.google.common.base.Objects;
18import com.google.common.base.Optional;
19import com.google.common.base.Preconditions;
Laszlo Csomorf04efcc2015-02-12 17:08:06 +000020import com.google.devtools.build.lib.actions.FilesetTraversalParams.PackageBoundaryMode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.devtools.build.lib.collect.nestedset.NestedSet;
22import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
23import com.google.devtools.build.lib.collect.nestedset.Order;
24import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalFunction.DanglingSymlinkException;
25import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalFunction.FileType;
26import com.google.devtools.build.lib.vfs.Path;
27import com.google.devtools.build.lib.vfs.PathFragment;
28import com.google.devtools.build.lib.vfs.RootedPath;
29import com.google.devtools.build.skyframe.SkyKey;
30import com.google.devtools.build.skyframe.SkyValue;
31
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +000032import java.util.regex.Pattern;
33
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import javax.annotation.Nullable;
35
36/**
37 * Collection of files found while recursively traversing a path.
38 *
39 * <p>The path may refer to files, symlinks or directories that may or may not exist.
40 *
41 * <p>Traversing a file or a symlink results in a single {@link ResolvedFile} corresponding to the
42 * file or symlink.
43 *
44 * <p>Traversing a directory results in a collection of {@link ResolvedFile}s for all files and
45 * symlinks under it, and in all of its subdirectories. The {@link TraversalRequest} can specify
46 * whether to traverse source subdirectories that are packages (have BUILD files in them).
47 *
48 * <p>Traversing a symlink that points to a directory is the same as traversing a normal directory.
49 * The paths in the result will not be resolved; the files will be listed under the symlink, as if
50 * it was the actual directory they reside in.
51 *
52 * <p>Editing a file that is part of this traversal, or adding or removing a file in a directory
53 * that is part of this traversal, will invalidate this {@link SkyValue}. This also applies to
54 * directories that are symlinked to.
55 */
56public final class RecursiveFilesystemTraversalValue implements SkyValue {
57 static final RecursiveFilesystemTraversalValue EMPTY = new RecursiveFilesystemTraversalValue(
58 Optional.<ResolvedFile>absent(),
59 NestedSetBuilder.<ResolvedFile>emptySet(Order.STABLE_ORDER));
60
61 /** The root of the traversal. May only be absent for the {@link #EMPTY} instance. */
62 private final Optional<ResolvedFile> resolvedRoot;
63
64 /** The transitive closure of {@link ResolvedFile}s. */
65 private final NestedSet<ResolvedFile> resolvedPaths;
66
67 private RecursiveFilesystemTraversalValue(Optional<ResolvedFile> resolvedRoot,
68 NestedSet<ResolvedFile> resolvedPaths) {
69 this.resolvedRoot = Preconditions.checkNotNull(resolvedRoot);
70 this.resolvedPaths = Preconditions.checkNotNull(resolvedPaths);
71 }
72
73 static RecursiveFilesystemTraversalValue of(ResolvedFile resolvedRoot,
74 NestedSet<ResolvedFile> resolvedPaths) {
75 if (resolvedPaths.isEmpty()) {
76 return EMPTY;
77 } else {
78 return new RecursiveFilesystemTraversalValue(Optional.of(resolvedRoot), resolvedPaths);
79 }
80 }
81
82 static RecursiveFilesystemTraversalValue of(ResolvedFile singleMember) {
83 return new RecursiveFilesystemTraversalValue(Optional.of(singleMember),
84 NestedSetBuilder.<ResolvedFile>create(Order.STABLE_ORDER, singleMember));
85 }
86
87 /** Returns the root of the traversal; absent only for the {@link #EMPTY} instance. */
88 public Optional<ResolvedFile> getResolvedRoot() {
89 return resolvedRoot;
90 }
91
92 /**
93 * Retrieves the set of {@link ResolvedFile}s that were found by this traversal.
94 *
95 * <p>The returned set may be empty if no files were found, or the ones found were to be
96 * considered non-existent. Unless it's empty, the returned set always includes the
97 * {@link #getResolvedRoot() resolved root}.
98 *
99 * <p>The returned set also includes symlinks. If a symlink points to a directory, its contents
100 * are also included in this set, and their path will start with the symlink's path, just like on
101 * a usual Unix file system.
102 */
103 public NestedSet<ResolvedFile> getTransitiveFiles() {
104 return resolvedPaths;
105 }
106
107 public static SkyKey key(TraversalRequest traversal) {
108 return new SkyKey(SkyFunctions.RECURSIVE_FILESYSTEM_TRAVERSAL, traversal);
109 }
110
111 /** The parameters of a file or directory traversal. */
112 public static final class TraversalRequest {
113
114 /** The path to start the traversal from; may be a file, a directory or a symlink. */
115 final RootedPath path;
116
117 /**
118 * Whether the path is in the output tree.
119 *
120 * <p>Such paths and all their subdirectories are assumed not to define packages, so package
121 * lookup for them is skipped.
122 */
123 final boolean isGenerated;
124
125 /** Whether traversal should descend into directories that are roots of subpackages. */
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000126 final PackageBoundaryMode crossPkgBoundaries;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127
128 /**
129 * Whether to skip checking if the root (if it's a directory) contains a BUILD file.
130 *
131 * <p>Such directories are not considered to be packages when this flag is true. This needs to
132 * be true in order to traverse directories of packages, but should be false for <i>their</i>
133 * subdirectories.
134 */
135 final boolean skipTestingForSubpackage;
136
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000137 /** A pattern that files must match to be included in this traversal (may be null.) */
138 @Nullable
139 final Pattern pattern;
140
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100141 /** Information to be attached to any error messages that may be reported. */
142 @Nullable final String errorInfo;
143
144 public TraversalRequest(RootedPath path, boolean isRootGenerated,
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000145 PackageBoundaryMode crossPkgBoundaries, boolean skipTestingForSubpackage,
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000146 @Nullable String errorInfo, @Nullable Pattern pattern) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147 this.path = path;
148 this.isGenerated = isRootGenerated;
149 this.crossPkgBoundaries = crossPkgBoundaries;
150 this.skipTestingForSubpackage = skipTestingForSubpackage;
151 this.errorInfo = errorInfo;
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000152 this.pattern = pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 }
154
155 private TraversalRequest duplicate(RootedPath newRoot, boolean newSkipTestingForSubpackage) {
156 return new TraversalRequest(newRoot, isGenerated, crossPkgBoundaries,
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000157 newSkipTestingForSubpackage, errorInfo, pattern);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158 }
159
160 /** Creates a new request to traverse a child element in the current directory (the root). */
161 TraversalRequest forChildEntry(RootedPath newPath) {
162 return duplicate(newPath, false);
163 }
164
165 /**
166 * Creates a new request for a changed root.
167 *
168 * <p>This method can be used when a package is found out to be under a different root path than
169 * originally assumed.
170 */
171 TraversalRequest forChangedRootPath(Path newRoot) {
172 return duplicate(RootedPath.toRootedPath(newRoot, path.getRelativePath()),
173 skipTestingForSubpackage);
174 }
175
176 @Override
177 public boolean equals(Object obj) {
178 if (this == obj) {
179 return true;
180 }
181 if (!(obj instanceof TraversalRequest)) {
182 return false;
183 }
184 TraversalRequest o = (TraversalRequest) obj;
185 return path.equals(o.path) && isGenerated == o.isGenerated
186 && crossPkgBoundaries == o.crossPkgBoundaries
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000187 && skipTestingForSubpackage == o.skipTestingForSubpackage
188 && Objects.equal(pattern, o.pattern);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 }
190
191 @Override
192 public int hashCode() {
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000193 return Objects.hashCode(path, isGenerated, crossPkgBoundaries, skipTestingForSubpackage,
194 pattern);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100195 }
196
197 @Override
198 public String toString() {
199 return String.format(
200 "TraversalParams(root=%s, is_generated=%d, skip_testing_for_subpkg=%d,"
Janak Ramakrishnan26cb4652015-04-21 01:29:54 +0000201 + " pkg_boundaries=%s, pattern=%s)", path, isGenerated ? 1 : 0,
202 skipTestingForSubpackage ? 1 : 0, crossPkgBoundaries,
203 pattern == null ? "null" : pattern.pattern());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100204 }
205 }
206
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000207 private static final class Symlink {
208 private final RootedPath linkName;
209 private final PathFragment unresolvedLinkTarget;
210 // The resolved link target is returned by ResolvedFile.getPath()
211
212 private Symlink(RootedPath linkName, PathFragment unresolvedLinkTarget) {
213 this.linkName = Preconditions.checkNotNull(linkName);
214 this.unresolvedLinkTarget = Preconditions.checkNotNull(unresolvedLinkTarget);
215 }
216
217 PathFragment getNameInSymlinkTree() {
218 return linkName.getRelativePath();
219 }
220
221 @Override
222 public boolean equals(Object obj) {
223 if (this == obj) {
224 return true;
225 }
226 if (!(obj instanceof Symlink)) {
227 return false;
228 }
229 Symlink o = (Symlink) obj;
230 return linkName.equals(o.linkName) && unresolvedLinkTarget.equals(o.unresolvedLinkTarget);
231 }
232
233 @Override
234 public int hashCode() {
235 return Objects.hashCode(linkName, unresolvedLinkTarget);
236 }
237
238 @Override
239 public String toString() {
240 return String.format("Symlink(link_name=%s, unresolved_target=%s)",
241 linkName, unresolvedLinkTarget);
242 }
243 }
244
245 private static final class RegularFile implements ResolvedFile {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000246 private final RootedPath path;
247 @Nullable private final FileStateValue metadata;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000248
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000249 /** C'tor for {@link #stripMetadataForTesting()}. */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000250 private RegularFile(RootedPath path) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000251 this.path = Preconditions.checkNotNull(path);
252 this.metadata = null;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000253 }
254
255 RegularFile(RootedPath path, FileStateValue metadata) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000256 this.path = Preconditions.checkNotNull(path);
257 this.metadata = Preconditions.checkNotNull(metadata);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000258 }
259
260 @Override
261 public FileType getType() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000262 return FileType.FILE;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000263 }
264
265 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000266 public RootedPath getPath() {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000267 return path;
268 }
269
270 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000271 @Nullable
Laszlo Csomoread99902015-12-07 11:16:52 +0000272 public Integer getMetadataHash() {
273 return metadata == null ? null : Integer.valueOf(metadata.hashCode());
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000274 }
275
276 @Override
277 public boolean equals(Object obj) {
278 if (this == obj) {
279 return true;
280 }
281 if (!(obj instanceof RegularFile)) {
282 return false;
283 }
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000284 return this.path.equals(((RegularFile) obj).path)
285 && Objects.equal(this.metadata, ((RegularFile) obj).metadata);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000286 }
287
288 @Override
289 public int hashCode() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000290 return Objects.hashCode(path, metadata);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000291 }
292
293 @Override
294 public String toString() {
Laszlo Csomor78543c32015-12-04 17:28:32 +0000295 return String.format("RegularFile(path=%s)", path);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000296 }
297
298 @Override
299 public ResolvedFile stripMetadataForTesting() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000300 return new RegularFile(path);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000301 }
302
303 @Override
304 public PathFragment getNameInSymlinkTree() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000305 return path.getRelativePath();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000306 }
307
308 @Override
309 public PathFragment getTargetInSymlinkTree(boolean followSymlinks) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000310 return path.asPath().asFragment();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000311 }
312 }
313
314 private static final class Directory implements ResolvedFile {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000315 private final RootedPath path;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000316
317 Directory(RootedPath path) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000318 this.path = Preconditions.checkNotNull(path);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000319 }
320
321 @Override
322 public FileType getType() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000323 return FileType.DIRECTORY;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000324 }
325
326 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000327 public RootedPath getPath() {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000328 return path;
329 }
330
331 @Override
Laszlo Csomoread99902015-12-07 11:16:52 +0000332 public Integer getMetadataHash() {
333 return Integer.valueOf(FileStateValue.DIRECTORY_FILE_STATE_NODE.hashCode());
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000334 }
335
336 @Override
337 public boolean equals(Object obj) {
338 if (this == obj) {
339 return true;
340 }
341 if (!(obj instanceof Directory)) {
342 return false;
343 }
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000344 return this.path.equals(((Directory) obj).path);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000345 }
346
347 @Override
348 public int hashCode() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000349 return path.hashCode();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000350 }
351
352 @Override
353 public String toString() {
Laszlo Csomor78543c32015-12-04 17:28:32 +0000354 return String.format("Directory(path=%s)", path);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000355 }
356
357 @Override
358 public ResolvedFile stripMetadataForTesting() {
359 return this;
360 }
361
362 @Override
363 public PathFragment getNameInSymlinkTree() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000364 return path.getRelativePath();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000365 }
366
367 @Override
368 public PathFragment getTargetInSymlinkTree(boolean followSymlinks) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000369 return path.asPath().asFragment();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000370 }
371 }
372
373 private static final class DanglingSymlink implements ResolvedFile {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000374 private final Symlink symlink;
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000375 @Nullable private final FileStateValue metadata;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000376
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000377 private DanglingSymlink(Symlink symlink) {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000378 this.symlink = symlink;
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000379 this.metadata = null;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000380 }
381
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000382 private DanglingSymlink(RootedPath linkNamePath, PathFragment linkTargetPath) {
383 this.symlink = new Symlink(linkNamePath, linkTargetPath);
384 this.metadata = null;
385 }
386
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000387 DanglingSymlink(RootedPath linkNamePath, PathFragment linkTargetPath,
388 FileStateValue metadata) {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000389 this.symlink = new Symlink(linkNamePath, linkTargetPath);
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000390 this.metadata = Preconditions.checkNotNull(metadata);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000391 }
392
393 @Override
394 public FileType getType() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000395 return FileType.DANGLING_SYMLINK;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000396 }
397
398 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000399 @Nullable
400 public RootedPath getPath() {
401 return null;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000402 }
403
404 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000405 @Nullable
Laszlo Csomoread99902015-12-07 11:16:52 +0000406 public Integer getMetadataHash() {
407 return metadata == null ? null : Integer.valueOf(metadata.hashCode());
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000408 }
409
410 @Override
411 public boolean equals(Object obj) {
412 if (this == obj) {
413 return true;
414 }
415 if (!(obj instanceof DanglingSymlink)) {
416 return false;
417 }
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000418 return Objects.equal(this.metadata, ((DanglingSymlink) obj).metadata)
Laszlo Csomor78543c32015-12-04 17:28:32 +0000419 && this.symlink.equals(((DanglingSymlink) obj).symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000420 }
421
422 @Override
423 public int hashCode() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000424 return Objects.hashCode(metadata, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000425 }
426
427 @Override
428 public String toString() {
Laszlo Csomor78543c32015-12-04 17:28:32 +0000429 return String.format("DanglingSymlink(%s)", symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000430 }
431
432 @Override
433 public ResolvedFile stripMetadataForTesting() {
434 return new DanglingSymlink(symlink);
435 }
436
437 @Override
438 public PathFragment getNameInSymlinkTree() {
439 return symlink.getNameInSymlinkTree();
440 }
441
442 @Override
443 public PathFragment getTargetInSymlinkTree(boolean followSymlinks)
444 throws DanglingSymlinkException {
445 if (followSymlinks) {
446 throw new DanglingSymlinkException(symlink.linkName.asPath().getPathString(),
447 symlink.unresolvedLinkTarget.getPathString());
448 } else {
449 return symlink.unresolvedLinkTarget;
450 }
451 }
452 }
453
454 private static final class SymlinkToFile implements ResolvedFile {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000455 private final RootedPath path;
456 @Nullable private final FileStateValue metadata;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000457 private final Symlink symlink;
458
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000459 /** C'tor for {@link #stripMetadataForTesting()}. */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000460 private SymlinkToFile(RootedPath targetPath, Symlink symlink) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000461 this.path = Preconditions.checkNotNull(targetPath);
462 this.metadata = null;
463 this.symlink = Preconditions.checkNotNull(symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000464 }
465
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000466 private SymlinkToFile(
467 RootedPath targetPath, RootedPath linkNamePath, PathFragment linkTargetPath) {
468 this.path = Preconditions.checkNotNull(targetPath);
469 this.metadata = null;
470 this.symlink = new Symlink(linkNamePath, linkTargetPath);
471 }
472
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000473 SymlinkToFile(RootedPath targetPath, RootedPath linkNamePath,
474 PathFragment linkTargetPath, FileStateValue metadata) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000475 this.path = Preconditions.checkNotNull(targetPath);
476 this.metadata = Preconditions.checkNotNull(metadata);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000477 this.symlink = new Symlink(linkNamePath, linkTargetPath);
478 }
479
480 @Override
481 public FileType getType() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000482 return FileType.SYMLINK_TO_FILE;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000483 }
484
485 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000486 public RootedPath getPath() {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000487 return path;
488 }
489
490 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000491 @Nullable
Laszlo Csomoread99902015-12-07 11:16:52 +0000492 public Integer getMetadataHash() {
493 return metadata == null ? null : Integer.valueOf(metadata.hashCode());
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000494 }
495
496 @Override
497 public boolean equals(Object obj) {
498 if (this == obj) {
499 return true;
500 }
501 if (!(obj instanceof SymlinkToFile)) {
502 return false;
503 }
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000504 return this.path.equals(((SymlinkToFile) obj).path)
505 && Objects.equal(this.metadata, ((SymlinkToFile) obj).metadata)
Laszlo Csomor78543c32015-12-04 17:28:32 +0000506 && this.symlink.equals(((SymlinkToFile) obj).symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000507 }
508
509 @Override
510 public int hashCode() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000511 return Objects.hashCode(path, metadata, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000512 }
513
514 @Override
515 public String toString() {
Laszlo Csomor78543c32015-12-04 17:28:32 +0000516 return String.format("SymlinkToFile(target=%s, %s)", path, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000517 }
518
519 @Override
520 public ResolvedFile stripMetadataForTesting() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000521 return new SymlinkToFile(path, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000522 }
523
524 @Override
525 public PathFragment getNameInSymlinkTree() {
526 return symlink.getNameInSymlinkTree();
527 }
528
529 @Override
530 public PathFragment getTargetInSymlinkTree(boolean followSymlinks) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000531 return followSymlinks ? path.asPath().asFragment() : symlink.unresolvedLinkTarget;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000532 }
533 }
534
535 private static final class SymlinkToDirectory implements ResolvedFile {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000536 private final RootedPath path;
Laszlo Csomoread99902015-12-07 11:16:52 +0000537 @Nullable private final Integer metadataHash;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000538 private final Symlink symlink;
539
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000540 /** C'tor for {@link #stripMetadataForTesting()}. */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000541 private SymlinkToDirectory(RootedPath targetPath, Symlink symlink) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000542 this.path = Preconditions.checkNotNull(targetPath);
Laszlo Csomoread99902015-12-07 11:16:52 +0000543 this.metadataHash = null;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000544 this.symlink = symlink;
545 }
546
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000547 private SymlinkToDirectory(
548 RootedPath targetPath, RootedPath linkNamePath, PathFragment linkValue) {
549 this.path = Preconditions.checkNotNull(targetPath);
Laszlo Csomoread99902015-12-07 11:16:52 +0000550 this.metadataHash = null;
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000551 this.symlink = new Symlink(linkNamePath, linkValue);
552 }
553
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000554 SymlinkToDirectory(RootedPath targetPath, RootedPath linkNamePath,
Laszlo Csomoread99902015-12-07 11:16:52 +0000555 PathFragment linkValue, Integer metadataHash) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000556 this.path = Preconditions.checkNotNull(targetPath);
Laszlo Csomoread99902015-12-07 11:16:52 +0000557 this.metadataHash = Preconditions.checkNotNull(metadataHash);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000558 this.symlink = new Symlink(linkNamePath, linkValue);
559 }
560
561 @Override
562 public FileType getType() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000563 return FileType.SYMLINK_TO_DIRECTORY;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000564 }
565
566 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000567 public RootedPath getPath() {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000568 return path;
569 }
570
571 @Override
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000572 @Nullable
Laszlo Csomoread99902015-12-07 11:16:52 +0000573 public Integer getMetadataHash() {
574 return metadataHash;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000575 }
576
577 @Override
578 public boolean equals(Object obj) {
579 if (this == obj) {
580 return true;
581 }
582 if (!(obj instanceof SymlinkToDirectory)) {
583 return false;
584 }
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000585 return this.path.equals(((SymlinkToDirectory) obj).path)
Laszlo Csomoread99902015-12-07 11:16:52 +0000586 && Objects.equal(this.metadataHash, ((SymlinkToDirectory) obj).metadataHash)
Laszlo Csomor78543c32015-12-04 17:28:32 +0000587 && this.symlink.equals(((SymlinkToDirectory) obj).symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000588 }
589
590 @Override
591 public int hashCode() {
Laszlo Csomoread99902015-12-07 11:16:52 +0000592 return Objects.hashCode(path, metadataHash, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000593 }
594
595 @Override
596 public String toString() {
Laszlo Csomor78543c32015-12-04 17:28:32 +0000597 return String.format("SymlinkToDirectory(target=%s, %s)", path, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000598 }
599
600 @Override
601 public ResolvedFile stripMetadataForTesting() {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000602 return new SymlinkToDirectory(path, symlink);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000603 }
604
605 @Override
606 public PathFragment getNameInSymlinkTree() {
607 return symlink.getNameInSymlinkTree();
608 }
609
610 @Override
611 public PathFragment getTargetInSymlinkTree(boolean followSymlinks) {
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000612 return followSymlinks ? path.asPath().asFragment() : symlink.unresolvedLinkTarget;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000613 }
614 }
615
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000616 static final class ResolvedFileFactory {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000617 private ResolvedFileFactory() {}
618
619 public static ResolvedFile regularFile(RootedPath path, FileStateValue metadata) {
620 return new RegularFile(path, metadata);
621 }
622
623 public static ResolvedFile directory(RootedPath path) {
624 return new Directory(path);
625 }
626
627 public static ResolvedFile symlinkToFile(RootedPath targetPath, RootedPath linkNamePath,
628 PathFragment linkTargetPath, FileStateValue metadata) {
629 return new SymlinkToFile(targetPath, linkNamePath, linkTargetPath, metadata);
630 }
631
632 public static ResolvedFile symlinkToDirectory(RootedPath targetPath,
Laszlo Csomoread99902015-12-07 11:16:52 +0000633 RootedPath linkNamePath, PathFragment linkValue, Integer metadataHash) {
634 return new SymlinkToDirectory(targetPath, linkNamePath, linkValue, metadataHash);
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000635 }
636
637 public static ResolvedFile danglingSymlink(RootedPath linkNamePath, PathFragment linkValue,
638 FileStateValue metadata) {
639 return new DanglingSymlink(linkNamePath, linkValue, metadata);
640 }
641 }
642
Laszlo Csomore9c41c52015-12-07 10:32:26 +0000643 @VisibleForTesting
644 static final class ResolvedFileFactoryForTesting {
645 private ResolvedFileFactoryForTesting() {}
646
647 static ResolvedFile regularFileForTesting(RootedPath path) {
648 return new RegularFile(path);
649 }
650
651 static ResolvedFile symlinkToFileForTesting(
652 RootedPath targetPath, RootedPath linkNamePath, PathFragment linkTargetPath) {
653 return new SymlinkToFile(targetPath, linkNamePath, linkTargetPath);
654 }
655
656 static ResolvedFile symlinkToDirectoryForTesting(
657 RootedPath targetPath, RootedPath linkNamePath, PathFragment linkValue) {
658 return new SymlinkToDirectory(targetPath, linkNamePath, linkValue);
659 }
660
661 public static ResolvedFile danglingSymlinkForTesting(
662 RootedPath linkNamePath, PathFragment linkValue) {
663 return new DanglingSymlink(linkNamePath, linkValue);
664 }
665 }
666
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100667 /**
668 * Path and type information about a single file or symlink.
669 *
670 * <p>The object stores things such as the absolute path of the file or symlink, its exact type
671 * and, if it's a symlink, the resolved and unresolved link target paths.
672 */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000673 public static interface ResolvedFile {
674 /** Type of the entity under {@link #getPath()}. */
675 FileType getType();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100676
677 /**
678 * Path of the file, directory or resolved target of the symlink.
679 *
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000680 * <p>May only return null for dangling symlinks.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100681 */
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000682 @Nullable
683 RootedPath getPath();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100684
685 /**
Laszlo Csomoread99902015-12-07 11:16:52 +0000686 * Hash code of associated metadata.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100687 *
Laszlo Csomoread99902015-12-07 11:16:52 +0000688 * <p>This is usually some hash of the {@link FileStateValue} of the underlying filesystem
689 * entity.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100690 *
Laszlo Csomoread99902015-12-07 11:16:52 +0000691 * <p>The method only returns null if tests stripped the metadata, or the {@link ResolvedFile}
692 * was created by the {@link ResolvedFileFactoryForTesting}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100693 */
Laszlo Csomor7c0b9952015-12-07 09:48:55 +0000694 @Nullable
Laszlo Csomoread99902015-12-07 11:16:52 +0000695 Integer getMetadataHash();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100696
697 /**
698 * Returns the path of the Fileset-output symlink relative to the output directory.
699 *
700 * <p>The path should contain the FilesetEntry-specific destination directory (if any) and
701 * should have necessary prefixes stripped (if any).
702 */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000703 PathFragment getNameInSymlinkTree();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100704
705 /**
706 * Returns the path of the symlink target.
707 *
708 * @throws DanglingSymlinkException if the target cannot be resolved because the symlink is
709 * dangling
710 */
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000711 PathFragment getTargetInSymlinkTree(boolean followSymlinks) throws DanglingSymlinkException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100712
713 /**
714 * Returns a copy of this object with the metadata stripped away.
715 *
716 * <p>This method should only be used by tests that wish to assert that this
717 * {@link ResolvedFile} refers to the expected absolute path and has the expected type, without
718 * asserting its actual contents (which the metadata is a function of).
719 */
720 @VisibleForTesting
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000721 ResolvedFile stripMetadataForTesting();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100722 }
723
724 @Override
725 public boolean equals(Object obj) {
726 if (this == obj) {
727 return true;
728 }
729 if (!(obj instanceof RecursiveFilesystemTraversalValue)) {
730 return false;
731 }
732 RecursiveFilesystemTraversalValue o = (RecursiveFilesystemTraversalValue) obj;
733 return resolvedRoot.equals(o.resolvedRoot) && resolvedPaths.equals(o.resolvedPaths);
734 }
735
736 @Override
737 public int hashCode() {
738 return Objects.hashCode(resolvedRoot, resolvedPaths);
739 }
740}