blob: 272041194fe19c354a0f3423a4114ab7bee89b3e [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
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
nharmata8494cc72019-02-28 13:09:20 -080017import com.google.common.collect.ImmutableList;
Janak Ramakrishnance372c32016-03-14 16:19:18 +000018import com.google.common.collect.Maps;
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +000019import com.google.common.collect.Sets;
shahan602cc852018-06-06 20:09:57 -070020import com.google.devtools.build.lib.actions.FileValue;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000021import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Yun Pengbf2c4d92019-12-13 09:21:28 -080022import com.google.devtools.build.lib.cmdline.RepositoryName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.devtools.build.lib.collect.nestedset.NestedSet;
24import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
janakre2af68f2021-03-18 15:11:30 -070025import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionException;
26import com.google.devtools.build.lib.io.FileSymlinkInfiniteExpansionUniquenessFunction;
janakr3d7424a2021-03-18 11:44:25 -070027import com.google.devtools.build.lib.io.InconsistentFilesystemException;
kkress7dbabb42022-01-11 14:24:38 -080028import com.google.devtools.build.lib.packages.Globber;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.vfs.Dirent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.vfs.PathFragment;
31import com.google.devtools.build.lib.vfs.RootedPath;
32import com.google.devtools.build.lib.vfs.UnixGlob;
33import com.google.devtools.build.skyframe.SkyFunction;
34import com.google.devtools.build.skyframe.SkyFunctionException;
35import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
36import com.google.devtools.build.skyframe.SkyKey;
37import com.google.devtools.build.skyframe.SkyValue;
emilyguo38722db2022-03-31 15:31:21 -070038import com.google.devtools.build.skyframe.SkyframeIterableResult;
Janak Ramakrishnance372c32016-03-14 16:19:18 +000039import java.util.Map;
emilyguo38722db2022-03-31 15:31:21 -070040import java.util.Set;
Googlerd3501d82018-08-15 13:39:01 -070041import java.util.concurrent.ConcurrentHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import java.util.regex.Pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import javax.annotation.Nullable;
44
45/**
46 * A {@link SkyFunction} for {@link GlobValue}s.
47 *
48 * <p>This code drives the glob matching process.
49 */
Nathan Harmata029de3d2015-07-27 18:08:09 +000050public final class GlobFunction implements SkyFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051
Googlerf4bf6f62021-12-09 08:34:52 -080052 private ConcurrentHashMap<String, Pattern> regexPatternCache = new ConcurrentHashMap<>();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053
Eric Fellheimer434e4732015-08-04 18:08:45 +000054 private final boolean alwaysUseDirListing;
55
56 public GlobFunction(boolean alwaysUseDirListing) {
57 this.alwaysUseDirListing = alwaysUseDirListing;
58 }
59
Googlerf4bf6f62021-12-09 08:34:52 -080060 void complete() {
61 this.regexPatternCache = new ConcurrentHashMap<>();
62 }
63
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000065 public SkyValue compute(SkyKey skyKey, Environment env)
66 throws GlobFunctionException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067 GlobDescriptor glob = (GlobDescriptor) skyKey.argument();
kkress7dbabb42022-01-11 14:24:38 -080068 Globber.Operation globberOperation = glob.globberOperation();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069
Yun Pengbf2c4d92019-12-13 09:21:28 -080070 RepositoryName repositoryName = glob.getPackageId().getRepository();
kkress1847a012020-06-24 12:30:11 -070071 IgnoredPackagePrefixesValue ignoredPackagePrefixes =
72 (IgnoredPackagePrefixesValue) env.getValue(IgnoredPackagePrefixesValue.key(repositoryName));
lberki7abdcb42019-10-22 02:17:13 -070073 if (env.valuesMissing()) {
74 return null;
75 }
76
77 PathFragment globSubdir = glob.getSubdir();
78 PathFragment dirPathFragment = glob.getPackageId().getPackageFragment().getRelative(globSubdir);
79
kkress1847a012020-06-24 12:30:11 -070080 for (PathFragment ignoredPrefix : ignoredPackagePrefixes.getPatterns()) {
81 if (dirPathFragment.startsWith(ignoredPrefix)) {
lberki7abdcb42019-10-22 02:17:13 -070082 return GlobValue.EMPTY;
83 }
84 }
85
Nathan Harmatab795e6b2016-02-04 01:10:19 +000086 // Note that the glob's package is assumed to exist which implies that the package's BUILD file
87 // exists which implies that the package's directory exists.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010088 if (!globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
kkress986d4bd2022-01-18 14:48:34 -080089 PathFragment subDirFragment =
90 glob.getPackageId().getPackageFragment().getRelative(globSubdir);
91
Janak Ramakrishnance372c32016-03-14 16:19:18 +000092 PackageLookupValue globSubdirPkgLookupValue =
93 (PackageLookupValue)
94 env.getValue(
kkress986d4bd2022-01-18 14:48:34 -080095 PackageLookupValue.key(PackageIdentifier.create(repositoryName, subDirFragment)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 if (globSubdirPkgLookupValue == null) {
97 return null;
98 }
kkress986d4bd2022-01-18 14:48:34 -080099
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 if (globSubdirPkgLookupValue.packageExists()) {
101 // We crossed the package boundary, that is, pkg/subdir contains a BUILD file and thus
kkress986d4bd2022-01-18 14:48:34 -0800102 // defines another package, so glob expansion should not descend into
103 // that subdir.
104 //
105 // For SUBPACKAGES, we encounter this when the pattern is a recursive ** and we are a
106 // terminal package for that pattern. In that case we should include the subDirFragment
107 // PathFragment (relative to the glob's package) in the GlobValue.getMatches,
108 // otherwise for file/dir matching return EMPTY;
109 if (globberOperation == Globber.Operation.SUBPACKAGES) {
110 return new GlobValue(
111 NestedSetBuilder.<PathFragment>stableOrder()
112 .add(subDirFragment.relativeTo(glob.getPackageId().getPackageFragment()))
113 .build());
114 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100115 return GlobValue.EMPTY;
John Cater4117c862017-11-20 08:09:02 -0800116 } else if (globSubdirPkgLookupValue
117 instanceof PackageLookupValue.IncorrectRepositoryReferencePackageLookupValue) {
118 // We crossed a repository boundary, so glob expansion should not descend into that subdir.
119 return GlobValue.EMPTY;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 }
121 }
122
123 String pattern = glob.getPattern();
124 // Split off the first path component of the pattern.
Ulf Adams07dba942015-03-05 14:47:37 +0000125 int slashPos = pattern.indexOf('/');
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100126 String patternHead;
127 String patternTail;
128 if (slashPos == -1) {
129 patternHead = pattern;
130 patternTail = null;
131 } else {
132 // Substrings will share the backing array of the original glob string. That should be fine.
133 patternHead = pattern.substring(0, slashPos);
134 patternTail = pattern.substring(slashPos + 1);
135 }
136
137 NestedSetBuilder<PathFragment> matches = NestedSetBuilder.stableOrder();
138
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000139 boolean globMatchesBareFile = patternTail == null;
140
Nathan Harmatab795e6b2016-02-04 01:10:19 +0000141 RootedPath dirRootedPath = RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment);
Eric Fellheimer434e4732015-08-04 18:08:45 +0000142 if (alwaysUseDirListing || containsGlobs(patternHead)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 // Pattern contains globs, so a directory listing is required.
144 //
145 // Note that we have good reason to believe the directory exists: if this is the
146 // top-level directory of the package, the package's existence implies the directory's
147 // existence; if this is a lower-level directory in the package, then we got here from
148 // previous directory listings. Filesystem operations concurrent with build could mean the
149 // directory no longer exists, but DirectoryListingFunction handles that gracefully.
nharmata8494cc72019-02-28 13:09:20 -0800150 SkyKey directoryListingKey = DirectoryListingValue.key(dirRootedPath);
151 DirectoryListingValue listingValue = null;
152
153 boolean patternHeadIsStarStar = "**".equals(patternHead);
154 if (patternHeadIsStarStar) {
155 // "**" also matches an empty segment, so try the case where it is not present.
156 if (globMatchesBareFile) {
157 // Recursive globs aren't supposed to match the package's directory.
kkress7dbabb42022-01-11 14:24:38 -0800158 if (globberOperation == Globber.Operation.FILES_AND_DIRS
159 && !globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
nharmata8494cc72019-02-28 13:09:20 -0800160 matches.add(globSubdir);
161 }
162 } else {
163 // Optimize away a Skyframe restart by requesting the DirectoryListingValue dep and
164 // recursive GlobValue dep in a single batch.
165
166 SkyKey keyForRecursiveGlobInCurrentDirectory =
167 GlobValue.internalKey(
168 glob.getPackageId(),
169 glob.getPackageRoot(),
170 globSubdir,
171 patternTail,
kkress7dbabb42022-01-11 14:24:38 -0800172 globberOperation);
emilyguo38722db2022-03-31 15:31:21 -0700173 SkyframeIterableResult listingAndRecursiveGlobResult =
174 env.getOrderedValuesAndExceptions(
nharmata8494cc72019-02-28 13:09:20 -0800175 ImmutableList.of(keyForRecursiveGlobInCurrentDirectory, directoryListingKey));
176 if (env.valuesMissing()) {
177 return null;
178 }
emilyguo38722db2022-03-31 15:31:21 -0700179 GlobValue globValue = (GlobValue) listingAndRecursiveGlobResult.next();
emilyguo8f0034c2022-04-09 10:09:52 -0700180 if (globValue == null) {
181 // has exception, will be handled later.
182 return null;
183 }
nharmata8494cc72019-02-28 13:09:20 -0800184 matches.addTransitive(globValue.getMatches());
emilyguo38722db2022-03-31 15:31:21 -0700185 listingValue = (DirectoryListingValue) listingAndRecursiveGlobResult.next();
nharmata8494cc72019-02-28 13:09:20 -0800186 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100187 }
188
nharmata8494cc72019-02-28 13:09:20 -0800189 if (listingValue == null) {
190 listingValue = (DirectoryListingValue) env.getValue(directoryListingKey);
191 if (listingValue == null) {
192 return null;
193 }
194 }
195
196 // Now that we have the directory listing, we do three passes over it so as to maximize
197 // skyframe batching:
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000198 // (1) Process every dirent, keeping track of values we need to request if the dirent cannot
199 // be processed with current information (symlink targets and subdirectory globs/package
200 // lookups for some subdirectories).
201 // (2) Get those values and process the symlinks, keeping track of subdirectory globs/package
202 // lookups we may need to request in case the symlink's target is a directory.
203 // (3) Process the necessary subdirectories.
204 int direntsSize = listingValue.getDirents().size();
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000205 Map<SkyKey, Dirent> symlinkFileMap = Maps.newHashMapWithExpectedSize(direntsSize);
206 Map<SkyKey, Dirent> subdirMap = Maps.newHashMapWithExpectedSize(direntsSize);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000207 Map<Dirent, Object> sortedResultMap = Maps.newTreeMap();
nharmata8494cc72019-02-28 13:09:20 -0800208 String subdirPattern = patternHeadIsStarStar ? glob.getPattern() : patternTail;
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000209 // First pass: do normal files and collect SkyKeys to request for subdirectories and symlinks.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 for (Dirent dirent : listingValue.getDirents()) {
jcatercecb3a82018-05-01 14:37:48 -0700211 Dirent.Type direntType = dirent.getType();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100212 String fileName = dirent.getName();
laszlocsomor9efbb492019-07-04 08:36:29 -0700213 if (!UnixGlob.matches(patternHead, fileName, regexPatternCache)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100214 continue;
215 }
216
217 if (direntType == Dirent.Type.SYMLINK) {
218 // TODO(bazel-team): Consider extracting the symlink resolution logic.
219 // For symlinks, look up the corresponding FileValue. This ensures that if the symlink
220 // changes and "switches types" (say, from a file to a directory), this value will be
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000221 // invalidated. We also need the target's type to properly process the symlink.
222 symlinkFileMap.put(
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000223 FileValue.key(
224 RootedPath.toRootedPath(
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000225 glob.getPackageRoot(), dirPathFragment.getRelative(fileName))),
226 dirent);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000227 continue;
Janak Ramakrishnan4bf00182016-02-29 19:59:44 +0000228 }
Janak Ramakrishnan4bf00182016-02-29 19:59:44 +0000229
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000230 if (direntType == Dirent.Type.DIRECTORY) {
231 SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, subdirPattern);
232 if (keyToRequest != null) {
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000233 subdirMap.put(keyToRequest, dirent);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000234 }
kkress986d4bd2022-01-18 14:48:34 -0800235 } else if (globMatchesBareFile && globberOperation != Globber.Operation.SUBPACKAGES) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000236 sortedResultMap.put(dirent, glob.getSubdir().getRelative(fileName));
237 }
238 }
239
emilyguo38722db2022-03-31 15:31:21 -0700240 Set<SkyKey> subdirAndSymlinksKeys = Sets.union(subdirMap.keySet(), symlinkFileMap.keySet());
241 SkyframeIterableResult subdirAndSymlinksResult =
242 env.getOrderedValuesAndExceptions(subdirAndSymlinksKeys);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000243 if (env.valuesMissing()) {
244 return null;
245 }
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000246 Map<SkyKey, Dirent> symlinkSubdirMap = Maps.newHashMapWithExpectedSize(symlinkFileMap.size());
247 // Second pass: process the symlinks and subdirectories from the first pass, and maybe
248 // collect further SkyKeys if fully resolved symlink targets are themselves directories.
249 // Also process any known directories.
emilyguo38722db2022-03-31 15:31:21 -0700250 for (SkyKey subdirAndSymlinksKey : subdirAndSymlinksKeys) {
251 if (symlinkFileMap.containsKey(subdirAndSymlinksKey)) {
252 FileValue symlinkFileValue = (FileValue) subdirAndSymlinksResult.next();
253 if (symlinkFileValue == null) {
254 return null;
255 }
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000256 if (!symlinkFileValue.isSymlink()) {
257 throw new GlobFunctionException(
258 new InconsistentFilesystemException(
259 "readdir and stat disagree about whether "
emilyguo38722db2022-03-31 15:31:21 -0700260 + ((RootedPath) subdirAndSymlinksKey.argument()).asPath()
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000261 + " is a symlink."),
262 Transience.TRANSIENT);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000263 }
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000264 if (!symlinkFileValue.exists()) {
265 continue;
266 }
lberki7598bc62020-10-02 01:33:14 -0700267
268 // This check is more strict than necessary: we raise an error if globbing traverses into
269 // a directory for any reason, even though it's only necessary if that reason was the
270 // resolution of a recursive glob ("**"). Fixing this would require plumbing the ancestor
271 // symlink information through DirectoryListingValue.
272 if (symlinkFileValue.isDirectory()
273 && symlinkFileValue.unboundedAncestorSymlinkExpansionChain() != null) {
274 SkyKey uniquenessKey =
275 FileSymlinkInfiniteExpansionUniquenessFunction.key(
276 symlinkFileValue.unboundedAncestorSymlinkExpansionChain());
277 env.getValue(uniquenessKey);
278 if (env.valuesMissing()) {
279 return null;
280 }
281
282 FileSymlinkInfiniteExpansionException symlinkException =
283 new FileSymlinkInfiniteExpansionException(
284 symlinkFileValue.pathToUnboundedAncestorSymlinkExpansionChain(),
285 symlinkFileValue.unboundedAncestorSymlinkExpansionChain());
286 throw new GlobFunctionException(symlinkException, Transience.PERSISTENT);
287 }
288
emilyguo38722db2022-03-31 15:31:21 -0700289 Dirent dirent = symlinkFileMap.get(subdirAndSymlinksKey);
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000290 String fileName = dirent.getName();
291 if (symlinkFileValue.isDirectory()) {
292 SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, subdirPattern);
293 if (keyToRequest != null) {
294 symlinkSubdirMap.put(keyToRequest, dirent);
295 }
kkress986d4bd2022-01-18 14:48:34 -0800296 } else if (globMatchesBareFile && globberOperation != Globber.Operation.SUBPACKAGES) {
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000297 sortedResultMap.put(dirent, glob.getSubdir().getRelative(fileName));
298 }
299 } else {
emilyguo38722db2022-03-31 15:31:21 -0700300 SkyValue value = subdirAndSymlinksResult.next();
301 if (value == null) {
302 return null;
303 }
304 processSubdir(Map.entry(subdirAndSymlinksKey, value), subdirMap, glob, sortedResultMap);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000305 }
306 }
307
emilyguo38722db2022-03-31 15:31:21 -0700308 Set<SkyKey> symlinkSubdirKeys = symlinkSubdirMap.keySet();
309 SkyframeIterableResult symlinkSubdirResult =
310 env.getOrderedValuesAndExceptions(symlinkSubdirKeys);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000311 if (env.valuesMissing()) {
312 return null;
313 }
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000314 // Third pass: do needed subdirectories of symlinked directories discovered during the second
315 // pass.
emilyguo38722db2022-03-31 15:31:21 -0700316 for (SkyKey symlinkSubdirKey : symlinkSubdirKeys) {
317 SkyValue symlinkSubdirValue = symlinkSubdirResult.next();
318 if (symlinkSubdirValue == null) {
319 return null;
320 }
321 processSubdir(
322 Map.entry(symlinkSubdirKey, symlinkSubdirValue),
323 symlinkSubdirMap,
324 glob,
325 sortedResultMap);
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000326 }
327 for (Map.Entry<Dirent, Object> fileMatches : sortedResultMap.entrySet()) {
328 addToMatches(fileMatches.getValue(), matches);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100329 }
330 } else {
331 // Pattern does not contain globs, so a direct stat is enough.
332 String fileName = patternHead;
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000333 RootedPath fileRootedPath =
334 RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment.getRelative(fileName));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 FileValue fileValue = (FileValue) env.getValue(FileValue.key(fileRootedPath));
336 if (fileValue == null) {
337 return null;
338 }
339 if (fileValue.exists()) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000340 if (fileValue.isDirectory()) {
341 SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, patternTail);
342 if (keyToRequest != null) {
343 SkyValue valueRequested = env.getValue(keyToRequest);
344 if (env.valuesMissing()) {
345 return null;
346 }
347 Object fileMatches = getSubdirMatchesFromSkyValue(fileName, glob, valueRequested);
348 if (fileMatches != null) {
349 addToMatches(fileMatches, matches);
350 }
351 }
kkress986d4bd2022-01-18 14:48:34 -0800352 } else if (globMatchesBareFile && globberOperation != Globber.Operation.SUBPACKAGES) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000353 matches.add(glob.getSubdir().getRelative(fileName));
354 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100355 }
356 }
357
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000358 Preconditions.checkState(!env.valuesMissing(), skyKey);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100359
360 NestedSet<PathFragment> matchesBuilt = matches.build();
361 // Use the same value to represent that we did not match anything.
362 if (matchesBuilt.isEmpty()) {
363 return GlobValue.EMPTY;
364 }
365 return new GlobValue(matchesBuilt);
366 }
367
Janak Ramakrishnanbce6fc52016-08-18 16:46:09 +0000368 private static void processSubdir(
369 Map.Entry<SkyKey, SkyValue> keyAndValue,
370 Map<SkyKey, Dirent> subdirMap,
371 GlobDescriptor glob,
372 Map<Dirent, Object> sortedResultMap) {
373 Dirent dirent = Preconditions.checkNotNull(subdirMap.get(keyAndValue.getKey()), keyAndValue);
374 String fileName = dirent.getName();
375 Object dirMatches = getSubdirMatchesFromSkyValue(fileName, glob, keyAndValue.getValue());
376 if (dirMatches != null) {
377 sortedResultMap.put(dirent, dirMatches);
378 }
379 }
380
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000381 /** Returns true if the given pattern contains globs. */
382 private static boolean containsGlobs(String pattern) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100383 return pattern.contains("*") || pattern.contains("?");
384 }
385
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000386 @SuppressWarnings("unchecked") // cast to NestedSet<PathFragment>
387 private static void addToMatches(Object toAdd, NestedSetBuilder<PathFragment> matches) {
388 if (toAdd instanceof PathFragment) {
389 matches.add((PathFragment) toAdd);
kkress986d4bd2022-01-18 14:48:34 -0800390 } else if (toAdd instanceof NestedSet) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000391 matches.addTransitive((NestedSet<PathFragment>) toAdd);
392 }
kkress986d4bd2022-01-18 14:48:34 -0800393 // else Not actually a valid type and ignore.
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000394 }
395
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100396 /**
397 * Includes the given file/directory in the glob.
398 *
399 * <p>{@code fileName} must exist.
400 *
401 * <p>{@code isDirectory} must be true iff the file is a directory.
402 *
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000403 * <p>Returns a {@link SkyKey} for a value that is needed to compute the files that will be added
404 * to {@code matches}, or {@code null} if no additional value is needed. The returned value should
405 * be opaquely passed to {@link #getSubdirMatchesFromSkyValue}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100406 */
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000407 private static SkyKey getSkyKeyForSubdir(
408 String fileName, GlobDescriptor glob, String subdirPattern) {
409 if (subdirPattern == null) {
kkress7dbabb42022-01-11 14:24:38 -0800410 if (glob.globberOperation() == Globber.Operation.FILES) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000411 return null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100412 }
kkress986d4bd2022-01-18 14:48:34 -0800413
414 // For FILES_AND_DIRS and SUBPACKAGES we want to maybe inspect a
415 // PackageLookupValue for it.
416 return PackageLookupValue.key(
417 PackageIdentifier.create(
418 glob.getPackageId().getRepository(),
419 glob.getPackageId()
420 .getPackageFragment()
421 .getRelative(glob.getSubdir())
422 .getRelative(fileName)));
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000423 } else {
424 // There is some more pattern to match. Get the glob for the subdirectory. Note that this
425 // directory may also match directly in the case of a pattern that starts with "**", but that
426 // match will be found in the subdirectory glob.
427 return GlobValue.internalKey(
428 glob.getPackageId(),
429 glob.getPackageRoot(),
430 glob.getSubdir().getRelative(fileName),
431 subdirPattern,
kkress7dbabb42022-01-11 14:24:38 -0800432 glob.globberOperation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100433 }
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000434 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000436 /**
kkress986d4bd2022-01-18 14:48:34 -0800437 * Returns an Object indicating a match was found for the given fileName in the given
438 * valueRequested. The Object will be one of:
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000439 *
kkress986d4bd2022-01-18 14:48:34 -0800440 * <ul>
441 * <li>{@code null} if no matches for the given parameters exists
442 * <li>{@code NestedSet<PathFragment>} if a match exists, either because we are looking for
443 * files/directories or the SkyValue is a package and we're globbing for {@link
444 * Globber.Operation.SUBPACKAGES}
445 * </ul>
446 *
447 * <p>{@code valueRequested} must be the SkyValue whose key was returned by {@link
448 * #getSkyKeyForSubdir} for these parameters.
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000449 */
450 @Nullable
451 private static Object getSubdirMatchesFromSkyValue(
kkress986d4bd2022-01-18 14:48:34 -0800452 String fileName, GlobDescriptor glob, SkyValue valueRequested) {
Janak Ramakrishnance372c32016-03-14 16:19:18 +0000453 if (valueRequested instanceof GlobValue) {
454 return ((GlobValue) valueRequested).getMatches();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100455 }
kkress986d4bd2022-01-18 14:48:34 -0800456
457 Preconditions.checkState(
458 valueRequested instanceof PackageLookupValue,
459 "%s is not a GlobValue or PackageLookupValue (%s %s)",
460 valueRequested,
461 fileName,
462 glob);
463
464 PackageLookupValue packageLookupValue = (PackageLookupValue) valueRequested;
465 if (packageLookupValue
466 instanceof PackageLookupValue.IncorrectRepositoryReferencePackageLookupValue) {
467 // This is a separate repository, so ignore it.
468 return null;
469 }
470
471 boolean isSubpackagesOp = glob.globberOperation() == Globber.Operation.SUBPACKAGES;
472 boolean pkgExists = packageLookupValue.packageExists();
473
474 if (!isSubpackagesOp && pkgExists) {
475 // We're in our repo and fileName is a package. Since we're not doing SUBPACKAGES listing, we
476 // do not want to add it to the results.
477 return null;
478 } else if (isSubpackagesOp && !pkgExists) {
479 // We're in our repo and the package exists. Since we're doing SUBPACKAGES listing, we do
480 // want to add fileName to the results.
481 return null;
482 }
483
484 // The fileName should be added to the results of the glob.
485 return glob.getSubdir().getRelative(fileName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100486 }
487
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100488 /**
489 * Used to declare all the exception types that can be wrapped in the exception thrown by
490 * {@link GlobFunction#compute}.
491 */
492 private static final class GlobFunctionException extends SkyFunctionException {
493 public GlobFunctionException(InconsistentFilesystemException e, Transience transience) {
494 super(e, transience);
495 }
lberki7598bc62020-10-02 01:33:14 -0700496
497 public GlobFunctionException(FileSymlinkInfiniteExpansionException e, Transience transience) {
498 super(e, transience);
499 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100500 }
501}