blob: 1a8cd4ecc709ade86c9c246ee3291e0f197cbeda [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
Googler8b68efe2023-03-15 14:57:50 -070016import static com.google.common.base.Preconditions.checkArgument;
17import static com.google.common.base.Preconditions.checkNotNull;
fellyf1e30f32019-07-09 12:26:10 -070018import static java.nio.charset.StandardCharsets.UTF_8;
19
Googler5284e6c2019-10-30 07:43:54 -070020import com.google.common.annotations.VisibleForTesting;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.base.Verify;
Googlerc8acdbd2018-11-08 10:47:15 -080022import com.google.common.collect.ImmutableList;
felly5be4dd62018-02-05 11:11:53 -080023import com.google.devtools.build.lib.actions.Artifact;
Googlerc8acdbd2018-11-08 10:47:15 -080024import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
shahan602cc852018-06-06 20:09:57 -070025import com.google.devtools.build.lib.actions.FileArtifactValue;
fellyf1e30f32019-07-09 12:26:10 -070026import com.google.devtools.build.lib.actions.FileStateType;
27import com.google.devtools.build.lib.actions.FileStateValue;
Googler8b68efe2023-03-15 14:57:50 -070028import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithContentsProxy;
29import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithDigest;
shahan602cc852018-06-06 20:09:57 -070030import com.google.devtools.build.lib.actions.FileValue;
fellyf1e30f32019-07-09 12:26:10 -070031import com.google.devtools.build.lib.actions.HasDigest;
Laszlo Csomor207140f2015-12-07 15:07:33 +000032import com.google.devtools.build.lib.collect.nestedset.NestedSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
34import com.google.devtools.build.lib.events.Event;
janakre2af68f2021-03-18 15:11:30 -070035import com.google.devtools.build.lib.io.FileSymlinkException;
36import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionException;
37import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionUniquenessFunction;
Googler0d4739e2023-10-30 10:00:55 -070038import com.google.devtools.build.lib.io.InconsistentFilesystemException;
janakrb77246c2021-03-05 14:41:58 -080039import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
twerth45d82ca2021-02-24 02:28:54 -080040import com.google.devtools.build.lib.profiler.Profiler;
41import com.google.devtools.build.lib.profiler.ProfilerTask;
42import com.google.devtools.build.lib.profiler.SilentCloseable;
janakra4a564a2021-03-18 12:40:11 -070043import com.google.devtools.build.lib.server.FailureDetails;
jcaterb11c6e22020-04-03 11:40:16 -070044import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.FileType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile;
Laszlo Csomorb4d482b2015-12-04 13:23:54 +000046import com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFileFactory;
Googlerde9d1f52023-11-06 09:28:56 -080047import com.google.devtools.build.lib.skyframe.serialization.VisibleForSerialization;
Googler0129a242021-12-01 09:04:45 -080048import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
fellyf1e30f32019-07-09 12:26:10 -070049import com.google.devtools.build.lib.util.Fingerprint;
Googler225d0a82024-10-18 08:26:22 -070050import com.google.devtools.build.lib.vfs.DetailedIOException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051import com.google.devtools.build.lib.vfs.Dirent;
felly5be4dd62018-02-05 11:11:53 -080052import com.google.devtools.build.lib.vfs.FileStatus;
felly5be4dd62018-02-05 11:11:53 -080053import com.google.devtools.build.lib.vfs.Path;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.vfs.PathFragment;
tomluee6a6862018-01-17 14:36:26 -080055import com.google.devtools.build.lib.vfs.Root;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056import com.google.devtools.build.lib.vfs.RootedPath;
felly5be4dd62018-02-05 11:11:53 -080057import com.google.devtools.build.lib.vfs.Symlinks;
janakrfc1d79c2022-01-27 13:02:07 -080058import com.google.devtools.build.lib.vfs.SyscallCache;
janakre2853222022-03-08 10:52:18 -080059import com.google.devtools.build.lib.vfs.XattrProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.skyframe.SkyFunction;
61import com.google.devtools.build.skyframe.SkyFunctionException;
62import com.google.devtools.build.skyframe.SkyKey;
63import com.google.devtools.build.skyframe.SkyValue;
Googlerb5052b82022-11-03 11:33:56 -070064import com.google.devtools.build.skyframe.SkyframeLookupResult;
kush95bf7c82017-08-30 00:27:35 +020065import java.io.IOException;
fellyf1e30f32019-07-09 12:26:10 -070066import java.math.BigInteger;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067import java.util.ArrayList;
Googlerd78fb572022-07-06 11:44:42 -070068import java.util.Collection;
felly5be4dd62018-02-05 11:11:53 -080069import java.util.Collections;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070import java.util.List;
71import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072import javax.annotation.Nullable;
73
74/** A {@link SkyFunction} to build {@link RecursiveFilesystemTraversalValue}s. */
75public final class RecursiveFilesystemTraversalFunction implements SkyFunction {
fellyf1e30f32019-07-09 12:26:10 -070076 private static final byte[] MISSING_FINGERPRINT =
77 new BigInteger(1, "NonexistentFileStateValue".getBytes(UTF_8)).toByteArray();
78
Googlerde9d1f52023-11-06 09:28:56 -080079 @SerializationConstant @VisibleForSerialization
fellyf1e30f32019-07-09 12:26:10 -070080 static final HasDigest NON_EXISTENT_HAS_DIGEST = () -> MISSING_FINGERPRINT;
81
82 private static final FileInfo NON_EXISTENT_FILE_INFO =
83 new FileInfo(FileType.NONEXISTENT, NON_EXISTENT_HAS_DIGEST, null, null);
84
mschallerc12c0442020-06-08 12:06:35 -070085 /** The exception that {@link RecursiveFilesystemTraversalFunctionException} wraps. */
86 public static class RecursiveFilesystemTraversalException extends Exception {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087
mschallerc12c0442020-06-08 12:06:35 -070088 /**
89 * Categories of errors that prevent normal {@link RecursiveFilesystemTraversalFunction}
90 * evaluation.
91 */
92 public enum Type {
93 /**
94 * The traversal encountered a subdirectory with a BUILD file but is not allowed to recurse
95 * into it. See {@code PackageBoundaryMode#REPORT_ERROR}.
96 */
97 CANNOT_CROSS_PACKAGE_BOUNDARY,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098
mschallerc12c0442020-06-08 12:06:35 -070099 /** A dangling symlink was dereferenced. */
100 DANGLING_SYMLINK,
101
102 /** A file operation failed. */
103 FILE_OPERATION_FAILURE,
104
105 /** A generated directory's root-relative path conflicts with a package's path. */
106 GENERATED_PATH_CONFLICT,
janakrb77246c2021-03-05 14:41:58 -0800107
108 /** A file/directory visited was part of a symlink cycle or infinite expansion. */
109 SYMLINK_CYCLE_OR_INFINITE_EXPANSION,
Googler0d4739e2023-10-30 10:00:55 -0700110
111 /** The filesystem told us inconsistent information. */
112 INCONSISTENT_FILESYSTEM,
Googler225d0a82024-10-18 08:26:22 -0700113
114 /** The filesystem threw a {@link DetailedIOException}. */
115 DETAILED_IO_EXCEPTION,
mschallerc12c0442020-06-08 12:06:35 -0700116 }
117
Googler8b68efe2023-03-15 14:57:50 -0700118 private final RecursiveFilesystemTraversalException.Type type;
mschallerc12c0442020-06-08 12:06:35 -0700119
Googler225d0a82024-10-18 08:26:22 -0700120 RecursiveFilesystemTraversalException(String message, DetailedIOException cause) {
121 super(message, cause);
122 this.type = RecursiveFilesystemTraversalException.Type.DETAILED_IO_EXCEPTION;
123 }
124
Googler8b68efe2023-03-15 14:57:50 -0700125 RecursiveFilesystemTraversalException(
126 String message, RecursiveFilesystemTraversalException.Type type) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 super(message);
Googler225d0a82024-10-18 08:26:22 -0700128 checkArgument(type != Type.DETAILED_IO_EXCEPTION);
mschallerc12c0442020-06-08 12:06:35 -0700129 this.type = type;
130 }
131
Googler8b68efe2023-03-15 14:57:50 -0700132 public RecursiveFilesystemTraversalException.Type getType() {
mschallerc12c0442020-06-08 12:06:35 -0700133 return type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 }
135 }
136
137 /**
138 * Thrown when a dangling symlink is attempted to be dereferenced.
139 *
140 * <p>Note: this class is not identical to the one in com.google.devtools.build.lib.view.fileset
141 * and it's not easy to merge the two because of the dependency structure. The other one will
142 * probably be removed along with the rest of the legacy Fileset code.
143 */
mschallerc12c0442020-06-08 12:06:35 -0700144 static final class DanglingSymlinkException extends RecursiveFilesystemTraversalException {
145 DanglingSymlinkException(String path, String unresolvedLink) {
Laszlo Csomor0ad729f2015-12-02 15:20:35 +0000146 super(
147 String.format(
mschallerc12c0442020-06-08 12:06:35 -0700148 "Found dangling symlink: %s, unresolved path: \"%s\"", path, unresolvedLink),
Googler8b68efe2023-03-15 14:57:50 -0700149 RecursiveFilesystemTraversalException.Type.DANGLING_SYMLINK);
150 checkArgument(path != null && !path.isEmpty());
151 checkArgument(unresolvedLink != null && !unresolvedLink.isEmpty());
kush95bf7c82017-08-30 00:27:35 +0200152 }
153 }
154
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 /** Exception type thrown by {@link RecursiveFilesystemTraversalFunction#compute}. */
156 private static final class RecursiveFilesystemTraversalFunctionException extends
157 SkyFunctionException {
158 RecursiveFilesystemTraversalFunctionException(RecursiveFilesystemTraversalException e) {
159 super(e, Transience.PERSISTENT);
160 }
161 }
162
janakrb2a94342022-02-05 22:02:14 -0800163 private final SyscallCache syscallCache;
janakr491e4412022-01-28 15:35:34 -0800164
janakrb2a94342022-02-05 22:02:14 -0800165 RecursiveFilesystemTraversalFunction(SyscallCache syscallCache) {
janakr491e4412022-01-28 15:35:34 -0800166 this.syscallCache = syscallCache;
167 }
168
janakrb77246c2021-03-05 14:41:58 -0800169 @Nullable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 @Override
Googlere0ba7962022-08-11 08:37:36 -0700171 public RecursiveFilesystemTraversalValue compute(SkyKey skyKey, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000172 throws RecursiveFilesystemTraversalFunctionException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100173 TraversalRequest traversal = (TraversalRequest) skyKey.argument();
twerth45d82ca2021-02-24 02:28:54 -0800174 try (SilentCloseable c =
175 Profiler.instance()
Googlerd78fb572022-07-06 11:44:42 -0700176 .profile(ProfilerTask.FILESYSTEM_TRAVERSAL, traversal.root().toString())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177 // Stat the traversal root.
janakrb2a94342022-02-05 22:02:14 -0800178 FileInfo rootInfo = lookUpFileInfo(env, traversal, syscallCache);
janakrb77246c2021-03-05 14:41:58 -0800179 if (rootInfo == null) {
180 return null;
181 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100182
183 if (!rootInfo.type.exists()) {
184 // May be a dangling symlink or a non-existent file. Handle gracefully.
185 if (rootInfo.type.isSymlink()) {
Googler0d4739e2023-10-30 10:00:55 -0700186 return RecursiveFilesystemTraversalValue.of(
187 ResolvedFileFactory.danglingSymlink(
188 traversal.root().asRootedPath(),
189 rootInfo.unresolvedSymlinkTarget,
190 rootInfo.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 } else {
192 return RecursiveFilesystemTraversalValue.EMPTY;
193 }
194 }
195
196 if (rootInfo.type.isFile()) {
Googlerd78fb572022-07-06 11:44:42 -0700197 return resultForFileRoot(traversal.root().asRootedPath(), rootInfo);
198 }
Googler6f48f1c2024-04-16 14:29:09 -0700199 if (rootInfo.type.isDirectory() && rootInfo.metadata instanceof TreeArtifactValue value) {
Googlerd78fb572022-07-06 11:44:42 -0700200 ImmutableList.Builder<RecursiveFilesystemTraversalValue> traversalValues =
201 ImmutableList.builderWithExpectedSize(value.getChildValues().size());
Googlerc8acdbd2018-11-08 10:47:15 -0800202 for (Map.Entry<TreeFileArtifact, FileArtifactValue> entry
203 : value.getChildValues().entrySet()) {
204 RootedPath path =
Googlerd78fb572022-07-06 11:44:42 -0700205 RootedPath.toRootedPath(traversal.root().getRootPart(), entry.getKey().getPath());
206 traversalValues.add(
jcaterb11c6e22020-04-03 11:40:16 -0700207 resultForFileRoot(
208 path,
209 // TreeArtifact can't have symbolic inside. So the assumption for FileType.FILE
210 // is always true.
211 new FileInfo(FileType.FILE, entry.getValue(), path, null)));
Googlerc8acdbd2018-11-08 10:47:15 -0800212 }
Googlerd78fb572022-07-06 11:44:42 -0700213 return resultForDirectory(traversal, rootInfo, traversalValues.build());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100214 }
215
216 // Otherwise the root is a directory or a symlink to one.
janakrb2a94342022-02-05 22:02:14 -0800217 PkgLookupResult pkgLookupResult = checkIfPackage(env, traversal, rootInfo, syscallCache);
janakrb77246c2021-03-05 14:41:58 -0800218 if (pkgLookupResult == null) {
219 return null;
220 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100221 traversal = pkgLookupResult.traversal;
222
223 if (pkgLookupResult.isConflicting()) {
224 // The traversal was requested for an output directory whose root-relative path conflicts
225 // with a source package. We can't handle that, bail out.
mschallerc12c0442020-06-08 12:06:35 -0700226 throw createGeneratedPathConflictException(traversal);
Googlerd78fb572022-07-06 11:44:42 -0700227 } else if (pkgLookupResult.isPackage() && !traversal.skipTestingForSubpackage()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100228 // The traversal was requested for a directory that defines a package.
tomlu8cc5dcf2018-01-19 09:28:06 -0800229 String msg =
Googlerd78fb572022-07-06 11:44:42 -0700230 traversal.errorInfo()
tomlu8cc5dcf2018-01-19 09:28:06 -0800231 + " crosses package boundary into package rooted at "
Googlerd78fb572022-07-06 11:44:42 -0700232 + traversal.root().getRelativePart().getPathString();
233 switch (traversal.crossPkgBoundaries()) {
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000234 case CROSS:
235 // We are free to traverse the subpackage but we need to display a warning.
Ulf Adams760e7092016-04-21 08:09:51 +0000236 env.getListener().handle(Event.warn(null, msg));
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000237 break;
238 case DONT_CROSS:
239 // We cannot traverse the subpackage and should skip it silently. Return empty results.
240 return RecursiveFilesystemTraversalValue.EMPTY;
241 case REPORT_ERROR:
242 // We cannot traverse the subpackage and should complain loudly (display an error).
243 throw new RecursiveFilesystemTraversalFunctionException(
mschallerc12c0442020-06-08 12:06:35 -0700244 new RecursiveFilesystemTraversalException(
245 msg, RecursiveFilesystemTraversalException.Type.CANNOT_CROSS_PACKAGE_BOUNDARY));
Laszlo Csomorf04efcc2015-02-12 17:08:06 +0000246 default:
247 throw new IllegalStateException(traversal.toString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100248 }
249 }
250
251 // We are free to traverse this directory.
emilyguo305fcc32022-03-29 10:10:07 -0700252 ImmutableList<RecursiveFilesystemTraversalValue> subdirTraversals =
emilyguoc07e66d2022-03-23 18:57:24 -0700253 traverseChildren(env, traversal);
janakrb77246c2021-03-05 14:41:58 -0800254 if (subdirTraversals == null) {
255 return null;
256 }
257 return resultForDirectory(traversal, rootInfo, subdirTraversals);
258 } catch (IOException | BuildFileNotFoundException e) {
259 String message =
260 String.format(
261 "Error while traversing directory %s: %s",
Googlerd78fb572022-07-06 11:44:42 -0700262 traversal.root().getRelativePart(), e.getMessage());
Googler225d0a82024-10-18 08:26:22 -0700263
264 if (e instanceof DetailedIOException detailedException) {
265 throw new RecursiveFilesystemTraversalFunctionException(
266 new RecursiveFilesystemTraversalException(message, detailedException));
267 }
268
janakra4a564a2021-03-18 12:40:11 -0700269 // Trying to stat the starting point of this root may have failed with a symlink cycle or
270 // trying to get a package lookup value may have failed due to a symlink cycle.
271 RecursiveFilesystemTraversalException.Type exceptionType =
272 RecursiveFilesystemTraversalException.Type.FILE_OPERATION_FAILURE;
Googler0d4739e2023-10-30 10:00:55 -0700273 if (e instanceof InconsistentFilesystemException) {
274 exceptionType = RecursiveFilesystemTraversalException.Type.INCONSISTENT_FILESYSTEM;
275 }
janakra4a564a2021-03-18 12:40:11 -0700276 if (e instanceof FileSymlinkException) {
277 exceptionType =
278 RecursiveFilesystemTraversalException.Type.SYMLINK_CYCLE_OR_INFINITE_EXPANSION;
279 }
280 if (e instanceof DetailedException) {
281 FailureDetails.PackageLoading.Code code =
282 ((DetailedException) e)
283 .getDetailedExitCode()
284 .getFailureDetail()
285 .getPackageLoading()
286 .getCode();
287 if (code == FailureDetails.PackageLoading.Code.SYMLINK_CYCLE_OR_INFINITE_EXPANSION) {
288 exceptionType =
289 RecursiveFilesystemTraversalException.Type.SYMLINK_CYCLE_OR_INFINITE_EXPANSION;
290 }
291 }
kush95bf7c82017-08-30 00:27:35 +0200292 throw new RecursiveFilesystemTraversalFunctionException(
janakra4a564a2021-03-18 12:40:11 -0700293 new RecursiveFilesystemTraversalException(message, exceptionType));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100294 }
295 }
296
mschallerc12c0442020-06-08 12:06:35 -0700297 private static RecursiveFilesystemTraversalFunctionException createGeneratedPathConflictException(
298 TraversalRequest traversal) {
299 String message =
300 String.format(
301 "Generated directory %s conflicts with package under the same path. "
302 + "Additional info: %s",
Googlerd78fb572022-07-06 11:44:42 -0700303 traversal.root().getRelativePart().getPathString(), traversal.errorInfo());
mschallerc12c0442020-06-08 12:06:35 -0700304 return new RecursiveFilesystemTraversalFunctionException(
305 new RecursiveFilesystemTraversalException(
306 message, RecursiveFilesystemTraversalException.Type.GENERATED_PATH_CONFLICT));
307 }
308
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100309 private static final class FileInfo {
310 final FileType type;
fellyf1e30f32019-07-09 12:26:10 -0700311 final HasDigest metadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100312 @Nullable final RootedPath realPath;
313 @Nullable final PathFragment unresolvedSymlinkTarget;
314
kush4b120e72018-07-11 16:21:27 -0700315 FileInfo(
316 FileType type,
fellyf1e30f32019-07-09 12:26:10 -0700317 HasDigest metadata,
kush4b120e72018-07-11 16:21:27 -0700318 @Nullable RootedPath realPath,
kushb39c6932018-07-12 21:25:23 -0700319 @Nullable PathFragment unresolvedSymlinkTarget) {
Googler8b68efe2023-03-15 14:57:50 -0700320 checkNotNull(metadata.getDigest(), metadata);
321 this.type = checkNotNull(type);
kushb39c6932018-07-12 21:25:23 -0700322 this.metadata = metadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100323 this.realPath = realPath;
324 this.unresolvedSymlinkTarget = unresolvedSymlinkTarget;
325 }
326
327 @Override
328 public String toString() {
329 if (type.isSymlink()) {
330 return String.format("(%s: link_value=%s, real_path=%s)", type,
331 unresolvedSymlinkTarget.getPathString(), realPath);
332 } else {
333 return String.format("(%s: real_path=%s)", type, realPath);
334 }
335 }
336 }
337
janakrb77246c2021-03-05 14:41:58 -0800338 @Nullable
janakr491e4412022-01-28 15:35:34 -0800339 private static FileInfo lookUpFileInfo(
340 Environment env, TraversalRequest traversal, SyscallCache syscallCache)
janakrb77246c2021-03-05 14:41:58 -0800341 throws IOException, InterruptedException {
Googlerd78fb572022-07-06 11:44:42 -0700342 if (traversal.isRootGenerated()) {
fellyf1e30f32019-07-09 12:26:10 -0700343 HasDigest fsVal = null;
Googlerd78fb572022-07-06 11:44:42 -0700344 if (traversal.root().getOutputArtifact() != null) {
345 Artifact artifact = traversal.root().getOutputArtifact();
lberki36df7ed2019-06-27 06:32:03 -0700346 SkyKey artifactKey = Artifact.key(artifact);
felly5be4dd62018-02-05 11:11:53 -0800347 SkyValue value = env.getValue(artifactKey);
348 if (env.valuesMissing()) {
janakrb77246c2021-03-05 14:41:58 -0800349 return null;
felly5be4dd62018-02-05 11:11:53 -0800350 }
kush95bf7c82017-08-30 00:27:35 +0200351
fellyf1e30f32019-07-09 12:26:10 -0700352 if (value instanceof FileArtifactValue || value instanceof TreeArtifactValue) {
353 fsVal = (HasDigest) value;
Googler3beaaaf2024-04-25 18:37:43 -0700354 } else if (value instanceof ActionExecutionValue actionExecutionValue) {
355 fsVal = actionExecutionValue.getExistingFileArtifactValue(artifact);
felly5be4dd62018-02-05 11:11:53 -0800356 } else {
kushb39c6932018-07-12 21:25:23 -0700357 return NON_EXISTENT_FILE_INFO;
felly5be4dd62018-02-05 11:11:53 -0800358 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100359 }
Googlerd78fb572022-07-06 11:44:42 -0700360 RootedPath realPath = traversal.root().asRootedPath();
361 if (traversal.strictOutputFiles()) {
Googler8b68efe2023-03-15 14:57:50 -0700362 checkNotNull(fsVal, "Strict Fileset output tree has null FileArtifactValue");
Googlerc8acdbd2018-11-08 10:47:15 -0800363 return new FileInfo(
Googlerd78fb572022-07-06 11:44:42 -0700364 fsVal instanceof TreeArtifactValue ? FileType.DIRECTORY : FileType.FILE,
jcaterb11c6e22020-04-03 11:40:16 -0700365 fsVal,
366 realPath,
367 null);
felly5be4dd62018-02-05 11:11:53 -0800368 } else {
felly6c399d62018-07-18 15:55:59 -0700369 // FileArtifactValue does not currently track symlinks. If it did, we could potentially
370 // remove some of the filesystem operations we're doing here.
Googlerd78fb572022-07-06 11:44:42 -0700371 Path path = traversal.root().asPath();
fellyf1e30f32019-07-09 12:26:10 -0700372 FileStateValue fileState =
Googlerd78fb572022-07-06 11:44:42 -0700373 FileStateValue.create(traversal.root().asRootedPath(), syscallCache, null);
fellyf1e30f32019-07-09 12:26:10 -0700374 if (fileState.getType() == FileStateType.NONEXISTENT) {
375 throw new IOException("Missing file: " + path);
376 }
felly6c399d62018-07-18 15:55:59 -0700377 FileStatus followStat = path.statIfFound(Symlinks.FOLLOW);
378 FileType type;
379 PathFragment unresolvedLinkTarget = null;
380 if (followStat == null) {
381 type = FileType.DANGLING_SYMLINK;
fellyf1e30f32019-07-09 12:26:10 -0700382 if (fileState.getType() != FileStateType.SYMLINK) {
383 throw new IOException("Expected symlink for " + path + ", but got: " + fileState);
felly6c399d62018-07-18 15:55:59 -0700384 }
385 unresolvedLinkTarget = path.readSymbolicLink();
fellyf1e30f32019-07-09 12:26:10 -0700386 } else if (fileState.getType() == FileStateType.REGULAR_FILE) {
felly6c399d62018-07-18 15:55:59 -0700387 type = FileType.FILE;
fellyf1e30f32019-07-09 12:26:10 -0700388 } else if (fileState.getType() == FileStateType.DIRECTORY) {
felly6c399d62018-07-18 15:55:59 -0700389 type = FileType.DIRECTORY;
390 } else {
391 unresolvedLinkTarget = path.readSymbolicLink();
392 realPath =
393 RootedPath.toRootedPath(
394 Root.absoluteRoot(path.getFileSystem()), path.resolveSymbolicLinks());
395 type = followStat.isFile() ? FileType.SYMLINK_TO_FILE : FileType.SYMLINK_TO_DIRECTORY;
396 }
fellyf1e30f32019-07-09 12:26:10 -0700397 if (fsVal == null) {
398 fsVal = fileState;
399 }
janakr491e4412022-01-28 15:35:34 -0800400 return new FileInfo(
401 type, withDigest(fsVal, path, syscallCache), realPath, unresolvedLinkTarget);
felly5be4dd62018-02-05 11:11:53 -0800402 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100403 } else {
felly5be4dd62018-02-05 11:11:53 -0800404 // Stat the file.
Googlerbf9a3d42024-07-24 18:52:40 -0700405 RootedPath rootedPath = traversal.root().asRootedPath();
felly5be4dd62018-02-05 11:11:53 -0800406 FileValue fileValue =
Googlerbf9a3d42024-07-24 18:52:40 -0700407 (FileValue) env.getValueOrThrow(FileValue.key(rootedPath), IOException.class);
felly5be4dd62018-02-05 11:11:53 -0800408
409 if (env.valuesMissing()) {
janakrb77246c2021-03-05 14:41:58 -0800410 return null;
felly5be4dd62018-02-05 11:11:53 -0800411 }
Googlerbf9a3d42024-07-24 18:52:40 -0700412 return toFileInfo(rootedPath, fileValue, env, traversal.root().asPath(), syscallCache);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100413 }
414 }
415
Googlere0ba7962022-08-11 08:37:36 -0700416 @Nullable
417 private static FileInfo toFileInfo(
Googlerbf9a3d42024-07-24 18:52:40 -0700418 RootedPath rootedPath,
419 FileValue fileValue,
420 Environment env,
421 Path path,
422 SyscallCache syscallCache)
Googlere0ba7962022-08-11 08:37:36 -0700423 throws IOException, InterruptedException {
424 if (fileValue.unboundedAncestorSymlinkExpansionChain() != null) {
Googler9fb98e32023-07-13 07:40:38 -0700425 SkyKey uniquenessKey =
426 FileSymlinkInfiniteExpansionUniquenessFunction.key(
427 fileValue.unboundedAncestorSymlinkExpansionChain());
428 env.getValue(uniquenessKey);
429 if (env.valuesMissing()) {
430 return null;
Googlere0ba7962022-08-11 08:37:36 -0700431 }
432
433 throw new FileSymlinkInfiniteExpansionException(
434 fileValue.pathToUnboundedAncestorSymlinkExpansionChain(),
435 fileValue.unboundedAncestorSymlinkExpansionChain());
436 }
437
438 if (!fileValue.exists()) {
439 // If it doesn't exist, or it's a dangling symlink, we still want to handle that gracefully.
440 return new FileInfo(
441 fileValue.isSymlink() ? FileType.DANGLING_SYMLINK : FileType.NONEXISTENT,
442 withDigest(fileValue.realFileStateValue(), null, syscallCache),
443 null,
444 fileValue.isSymlink() ? fileValue.getUnresolvedLinkTarget() : null);
445 }
446
447 // If it exists, it may either be a symlink or a file/directory.
448 PathFragment unresolvedLinkTarget = null;
449 FileType type;
450 if (fileValue.isSymlink()) {
451 unresolvedLinkTarget = fileValue.getUnresolvedLinkTarget();
452 type = fileValue.isDirectory() ? FileType.SYMLINK_TO_DIRECTORY : FileType.SYMLINK_TO_FILE;
453 } else {
454 type = fileValue.isDirectory() ? FileType.DIRECTORY : FileType.FILE;
455 }
456 return new FileInfo(
457 type,
458 withDigest(fileValue.realFileStateValue(), path, syscallCache),
Googlerbf9a3d42024-07-24 18:52:40 -0700459 fileValue.realRootedPath(rootedPath),
Googlere0ba7962022-08-11 08:37:36 -0700460 unresolvedLinkTarget);
461 }
462
Googler5284e6c2019-10-30 07:43:54 -0700463 /**
464 * Transform the HasDigest to the appropriate type based on the current state of the digest. If
465 * fsVal is type RegularFileStateValue or FileArtifactValue and has a valid digest value, then we
466 * want to convert it to a new FileArtifactValue type. Otherwise if they are of the two
467 * forementioned types but do not have a digest, then we will create a FileArtifactValue using its
468 * {@link Path}. Otherwise we will fingerprint the digest and return it as a new {@link
469 * HasDigest.ByteStringDigest} object.
470 *
471 * @param fsVal - the HasDigest value that was in the graph.
472 * @param path - the Path of the digest.
473 * @return transformed HasDigest value based on the digest field and object type.
474 */
475 @VisibleForTesting
janakre2853222022-03-08 10:52:18 -0800476 static HasDigest withDigest(HasDigest fsVal, Path path, XattrProvider syscallCache)
janakr491e4412022-01-28 15:35:34 -0800477 throws IOException {
Googler6f48f1c2024-04-16 14:29:09 -0700478 if (fsVal instanceof FileStateValue fsv) {
479 if (fsv instanceof RegularFileStateValueWithDigest rfsv) {
Googler8b68efe2023-03-15 14:57:50 -0700480 return FileArtifactValue.createForVirtualActionInput(rfsv.getDigest(), rfsv.getSize());
Googler6f48f1c2024-04-16 14:29:09 -0700481 } else if (fsv instanceof RegularFileStateValueWithContentsProxy rfsv) {
Googler8b68efe2023-03-15 14:57:50 -0700482 return FileArtifactValue.createForNormalFileUsingPath(path, rfsv.getSize(), syscallCache);
Googler5284e6c2019-10-30 07:43:54 -0700483 }
Googler8b68efe2023-03-15 14:57:50 -0700484
janakre430dc02021-04-28 12:28:57 -0700485 return new HasDigest.ByteStringDigest(fsv.getValueFingerprint());
Googler6f48f1c2024-04-16 14:29:09 -0700486 } else if (fsVal instanceof FileArtifactValue fav) {
Googler5284e6c2019-10-30 07:43:54 -0700487 if (fav.getDigest() != null) {
488 return fav;
489 }
490
491 // In the case there is a directory, the HasDigest value should not be converted. Otherwise,
492 // if the HasDigest value is a file, convert it using the Path and size values.
493 return fav.getType().isFile()
janakr491e4412022-01-28 15:35:34 -0800494 ? FileArtifactValue.createForNormalFileUsingPath(path, fav.getSize(), syscallCache)
janakre430dc02021-04-28 12:28:57 -0700495 : new HasDigest.ByteStringDigest(fav.getValueFingerprint());
fellyf1e30f32019-07-09 12:26:10 -0700496 }
497 return fsVal;
498 }
499
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100500 private static final class PkgLookupResult {
501 private enum Type {
502 CONFLICT, DIRECTORY, PKG
503 }
504
Googler8b68efe2023-03-15 14:57:50 -0700505 private final PkgLookupResult.Type type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100506 final TraversalRequest traversal;
507 final FileInfo rootInfo;
508
509 /** Result for a generated directory that conflicts with a source package. */
510 static PkgLookupResult conflict(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700511 return new PkgLookupResult(PkgLookupResult.Type.CONFLICT, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100512 }
513
514 /** Result for a source or generated directory (not a package). */
515 static PkgLookupResult directory(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700516 return new PkgLookupResult(PkgLookupResult.Type.DIRECTORY, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100517 }
518
519 /** Result for a package, i.e. a directory with a BUILD file. */
520 static PkgLookupResult pkg(TraversalRequest traversal, FileInfo rootInfo) {
Googler8b68efe2023-03-15 14:57:50 -0700521 return new PkgLookupResult(PkgLookupResult.Type.PKG, traversal, rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100522 }
523
Googler8b68efe2023-03-15 14:57:50 -0700524 private PkgLookupResult(
525 PkgLookupResult.Type type, TraversalRequest traversal, FileInfo rootInfo) {
526 this.type = checkNotNull(type);
527 this.traversal = checkNotNull(traversal);
528 this.rootInfo = checkNotNull(rootInfo);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100529 }
530
531 boolean isPackage() {
Googler8b68efe2023-03-15 14:57:50 -0700532 return type == PkgLookupResult.Type.PKG;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100533 }
534
535 boolean isConflicting() {
Googler8b68efe2023-03-15 14:57:50 -0700536 return type == PkgLookupResult.Type.CONFLICT;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100537 }
538
539 @Override
540 public String toString() {
541 return String.format("(%s: info=%s, traversal=%s)", type, rootInfo, traversal);
542 }
543 }
544
545 /**
546 * Checks whether the {@code traversal}'s path refers to a package directory.
547 *
548 * @return the result of the lookup; it contains potentially new {@link TraversalRequest} and
549 * {@link FileInfo} so the caller should use these instead of the old ones (this happens when
550 * a package is found, but under a different root than expected)
551 */
Googler5cc598c2022-07-06 03:29:45 -0700552 @Nullable
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000553 private static PkgLookupResult checkIfPackage(
janakr491e4412022-01-28 15:35:34 -0800554 Environment env, TraversalRequest traversal, FileInfo rootInfo, SyscallCache syscallCache)
janakrb77246c2021-03-05 14:41:58 -0800555 throws IOException, InterruptedException, BuildFileNotFoundException {
Googler8b68efe2023-03-15 14:57:50 -0700556 checkArgument(
557 rootInfo.type.exists() && !rootInfo.type.isFile(), "{%s} {%s}", traversal, rootInfo);
janakrb77246c2021-03-05 14:41:58 -0800558 // PackageLookupFunction/dependencies can only throw IOException, BuildFileNotFoundException,
559 // and RepositoryFetchException, and RepositoryFetchException is not in play here. Note that
560 // run-of-the-mill circular symlinks will *not* throw here, and will trigger later errors during
561 // the recursive traversal.
tomlu8cc5dcf2018-01-19 09:28:06 -0800562 PackageLookupValue pkgLookup =
563 (PackageLookupValue)
janakrb77246c2021-03-05 14:41:58 -0800564 env.getValueOrThrow(
Googlerd78fb572022-07-06 11:44:42 -0700565 PackageLookupValue.key(traversal.root().getRelativePart()),
janakrb77246c2021-03-05 14:41:58 -0800566 BuildFileNotFoundException.class,
567 IOException.class);
568 if (env.valuesMissing()) {
569 return null;
570 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100571
572 if (pkgLookup.packageExists()) {
Googlerd78fb572022-07-06 11:44:42 -0700573 if (traversal.isRootGenerated()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100574 // The traversal's root was a generated directory, but its root-relative path conflicts with
575 // an existing package.
576 return PkgLookupResult.conflict(traversal, rootInfo);
577 } else {
578 // The traversal's root was a source directory and it defines a package.
tomluee6a6862018-01-17 14:36:26 -0800579 Root pkgRoot = pkgLookup.getRoot();
Googlerd78fb572022-07-06 11:44:42 -0700580 if (!pkgRoot.equals(traversal.root().getRootPart())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100581 // However the root of this package is different from what we expected. stat() the real
582 // BUILD file of that package.
583 traversal = traversal.forChangedRootPath(pkgRoot);
janakr491e4412022-01-28 15:35:34 -0800584 rootInfo = lookUpFileInfo(env, traversal, syscallCache);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100585 Verify.verify(rootInfo.type.exists(), "{%s} {%s}", traversal, rootInfo);
586 }
587 return PkgLookupResult.pkg(traversal, rootInfo);
588 }
589 } else {
590 // The traversal's root was a directory (source or generated one), no package exists under the
591 // same root-relative path.
592 return PkgLookupResult.directory(traversal, rootInfo);
593 }
594 }
595
596 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100597 * Creates results for a file or for a symlink that points to one.
598 *
599 * <p>A symlink may be direct (points to a file) or transitive (points at a direct or transitive
600 * symlink).
601 */
Googler0d4739e2023-10-30 10:00:55 -0700602 private static RecursiveFilesystemTraversalValue resultForFileRoot(RootedPath path, FileInfo info)
603 throws InconsistentFilesystemException {
604 if (!info.type.isFile() || !info.type.exists()) {
605 throw new InconsistentFilesystemException(
606 String.format(
607 "We were previously told %s was an existing file but it's actually %s", path, info));
608 }
609
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100610 if (info.type.isSymlink()) {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000611 return RecursiveFilesystemTraversalValue.of(
612 ResolvedFileFactory.symlinkToFile(
kushb39c6932018-07-12 21:25:23 -0700613 info.realPath, path, info.unresolvedSymlinkTarget, info.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100614 } else {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000615 return RecursiveFilesystemTraversalValue.of(
kushb39c6932018-07-12 21:25:23 -0700616 ResolvedFileFactory.regularFile(path, info.metadata));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100617 }
618 }
619
emilyguoc07e66d2022-03-23 18:57:24 -0700620 private static RecursiveFilesystemTraversalValue resultForDirectory(
621 TraversalRequest traversal,
622 FileInfo rootInfo,
emilyguo305fcc32022-03-29 10:10:07 -0700623 ImmutableList<RecursiveFilesystemTraversalValue> subdirTraversals) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100624 // Collect transitive closure of files in subdirectories.
625 NestedSetBuilder<ResolvedFile> paths = NestedSetBuilder.stableOrder();
626 for (RecursiveFilesystemTraversalValue child : subdirTraversals) {
627 paths.addTransitive(child.getTransitiveFiles());
628 }
629 ResolvedFile root;
630 if (rootInfo.type.isSymlink()) {
Laszlo Csomor207140f2015-12-07 15:07:33 +0000631 NestedSet<ResolvedFile> children = paths.build();
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000632 root =
633 ResolvedFileFactory.symlinkToDirectory(
634 rootInfo.realPath,
Googlerd78fb572022-07-06 11:44:42 -0700635 traversal.root().asRootedPath(),
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000636 rootInfo.unresolvedSymlinkTarget,
kushb39c6932018-07-12 21:25:23 -0700637 hashDirectorySymlink(children, rootInfo.metadata));
Laszlo Csomor207140f2015-12-07 15:07:33 +0000638 paths = NestedSetBuilder.<ResolvedFile>stableOrder().addTransitive(children).add(root);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100639 } else {
Laszlo Csomorb4d482b2015-12-04 13:23:54 +0000640 root = ResolvedFileFactory.directory(rootInfo.realPath);
Fabian Meumertzheimfbfab662022-08-19 16:08:17 -0700641 if (traversal.emitEmptyDirectoryNodes() && paths.isEmpty()) {
642 paths.add(root);
643 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100644 }
645 return RecursiveFilesystemTraversalValue.of(root, paths.build());
646 }
647
fellyf1e30f32019-07-09 12:26:10 -0700648 private static HasDigest hashDirectorySymlink(
ulfjack61216fd2019-12-18 12:31:03 -0800649 NestedSet<ResolvedFile> children, HasDigest metadata) {
Laszlo Csomor207140f2015-12-07 15:07:33 +0000650 // If the root is a directory symlink, the associated FileStateValue does not change when the
651 // linked directory's contents change, so we can't use the FileStateValue as metadata like we
652 // do with other ResolvedFile kinds. Instead we compute a metadata hash from the child
653 // elements and return that as the ResolvedFile's metadata hash.
fellyf1e30f32019-07-09 12:26:10 -0700654 Fingerprint fp = new Fingerprint();
655 fp.addBytes(metadata.getDigest());
ulfjack61216fd2019-12-18 12:31:03 -0800656 for (ResolvedFile file : children.toList()) {
fellyf1e30f32019-07-09 12:26:10 -0700657 fp.addPath(file.getNameInSymlinkTree());
658 fp.addBytes(file.getMetadata().getDigest());
Laszlo Csomor207140f2015-12-07 15:07:33 +0000659 }
fellyf1e30f32019-07-09 12:26:10 -0700660 byte[] result = fp.digestAndReset();
661 return new HasDigest.ByteStringDigest(result);
Laszlo Csomor207140f2015-12-07 15:07:33 +0000662 }
663
emilyguoc07e66d2022-03-23 18:57:24 -0700664 /** Requests Skyframe to compute the dependent values and returns them. */
janakrb77246c2021-03-05 14:41:58 -0800665 @Nullable
emilyguo305fcc32022-03-29 10:10:07 -0700666 private ImmutableList<RecursiveFilesystemTraversalValue> traverseChildren(
emilyguoc07e66d2022-03-23 18:57:24 -0700667 Environment env, TraversalRequest traversal)
668 throws InterruptedException, RecursiveFilesystemTraversalFunctionException, IOException {
Googlere0ba7962022-08-11 08:37:36 -0700669 return traversal.isRootGenerated()
670 ? traverseGeneratedChildren(env, traversal)
671 : traverseSourceChildren(env, traversal);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100672 }
673
Googlere0ba7962022-08-11 08:37:36 -0700674 @Nullable
675 private ImmutableList<RecursiveFilesystemTraversalValue> traverseGeneratedChildren(
676 Environment env, TraversalRequest traversal)
677 throws InterruptedException, RecursiveFilesystemTraversalFunctionException, IOException {
678 // If we're dealing with an output file, read the directory directly instead of creating
679 // filesystem nodes under the output tree.
680 Collection<Dirent> dirents = traversal.root().asPath().readdir(Symlinks.FOLLOW);
681 if (dirents.isEmpty()) {
682 return ImmutableList.of();
683 }
684 List<Dirent> sortedDirents = new ArrayList<>(dirents);
685 Collections.sort(sortedDirents);
686
687 ImmutableList.Builder<RecursiveFilesystemTraversalValue> childValues =
688 ImmutableList.builderWithExpectedSize(dirents.size());
689 for (Dirent dirent : sortedDirents) {
690 RecursiveFilesystemTraversalValue childValue =
691 compute(traversal.forChildEntry(dirent.getName()), env);
692 if (childValue != null) {
693 childValues.add(childValue);
694 }
695 }
696 return env.valuesMissing() ? null : childValues.build();
697 }
698
699 @Nullable
700 private ImmutableList<RecursiveFilesystemTraversalValue> traverseSourceChildren(
701 Environment env, TraversalRequest traversal) throws InterruptedException, IOException {
702 DirectoryListingValue dirListingValue =
703 (DirectoryListingValue)
704 env.getValueOrThrow(
705 DirectoryListingValue.key(traversal.root().asRootedPath()), IOException.class);
706 if (dirListingValue == null) {
707 return null;
708 }
709 Dirents dirents = dirListingValue.getDirents();
710 if (dirents.size() == 0) {
711 return ImmutableList.of();
712 }
713
714 List<SkyKey> childKeys = new ArrayList<>(dirents.size());
715 for (Dirent dirent : dirents) {
716 TraversalRequest childRequest = traversal.forChildEntry(dirent.getName());
717 // For source files, request the FileValue directly instead of another TraversalRequest. This
718 // makes the base case of the recursive traversal a directory with no subdirectories instead
719 // of a file, greatly reducing the number of skyframe nodes for directories with many files.
720 if (dirent.getType() == Dirent.Type.FILE) {
721 childKeys.add(FileValue.key(childRequest.root().asRootedPath()));
722 } else {
723 childKeys.add(childRequest);
724 }
725 }
726
Googlerb5052b82022-11-03 11:33:56 -0700727 SkyframeLookupResult result = env.getValuesAndExceptions(childKeys);
Googlere0ba7962022-08-11 08:37:36 -0700728 ImmutableList.Builder<RecursiveFilesystemTraversalValue> childValues =
729 ImmutableList.builderWithExpectedSize(childKeys.size());
730 for (SkyKey key : childKeys) {
Googlerb5052b82022-11-03 11:33:56 -0700731 SkyValue value = result.get(key);
Googlere0ba7962022-08-11 08:37:36 -0700732 if (value == null) {
733 continue;
734 }
Googler6f48f1c2024-04-16 14:29:09 -0700735 if (key instanceof FileValue.Key fileKey) {
Googlere0ba7962022-08-11 08:37:36 -0700736 FileInfo fileInfo =
Googlerbf9a3d42024-07-24 18:52:40 -0700737 toFileInfo(
738 fileKey.argument(),
739 (FileValue) value,
740 env,
741 fileKey.argument().asPath(),
742 syscallCache);
Googlere0ba7962022-08-11 08:37:36 -0700743 if (fileInfo != null) {
744 childValues.add(resultForFileRoot(fileKey.argument(), fileInfo));
745 }
746 } else {
747 childValues.add((RecursiveFilesystemTraversalValue) value);
748 }
749 }
750 return env.valuesMissing() ? null : childValues.build();
751 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100752}