use of com.google.devtools.build.lib.vfs.Dirent.Type in project bazel by bazelbuild.
the class FileSystem method readdir.
/**
* Returns a Dirents structure, listing the names of all entries within the
* directory {@code path}, plus their types (file, directory, other).
*
* @param followSymlinks whether to follow symlinks when determining the file types of
* individual directory entries. No matter the value of this parameter, symlinks are
* followed when resolving the directory whose entries are to be read.
* @throws IOException if there was an error reading the directory entries
*/
protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
Collection<Path> children = getDirectoryEntries(path);
List<Dirent> dirents = Lists.newArrayListWithCapacity(children.size());
for (Path child : children) {
Dirent.Type type = direntFromStat(statNullable(child, followSymlinks));
dirents.add(new Dirent(child.getBaseName(), type));
}
return dirents;
}
use of com.google.devtools.build.lib.vfs.Dirent.Type in project bazel by bazelbuild.
the class GlobFunction method compute.
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws GlobFunctionException, InterruptedException {
GlobDescriptor glob = (GlobDescriptor) skyKey.argument();
// Note that the glob's package is assumed to exist which implies that the package's BUILD file
// exists which implies that the package's directory exists.
PathFragment globSubdir = glob.getSubdir();
if (!globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
PackageLookupValue globSubdirPkgLookupValue = (PackageLookupValue) env.getValue(PackageLookupValue.key(PackageIdentifier.create(glob.getPackageId().getRepository(), glob.getPackageId().getPackageFragment().getRelative(globSubdir))));
if (globSubdirPkgLookupValue == null) {
return null;
}
if (globSubdirPkgLookupValue.packageExists()) {
// defines another package, so glob expansion should not descend into that subdir.
return GlobValue.EMPTY;
}
}
String pattern = glob.getPattern();
// Split off the first path component of the pattern.
int slashPos = pattern.indexOf('/');
String patternHead;
String patternTail;
if (slashPos == -1) {
patternHead = pattern;
patternTail = null;
} else {
// Substrings will share the backing array of the original glob string. That should be fine.
patternHead = pattern.substring(0, slashPos);
patternTail = pattern.substring(slashPos + 1);
}
NestedSetBuilder<PathFragment> matches = NestedSetBuilder.stableOrder();
boolean globMatchesBareFile = patternTail == null;
// "**" also matches an empty segment, so try the case where it is not present.
if ("**".equals(patternHead)) {
if (globMatchesBareFile) {
// Recursive globs aren't supposed to match the package's directory.
if (!glob.excludeDirs() && !globSubdir.equals(PathFragment.EMPTY_FRAGMENT)) {
matches.add(globSubdir);
}
} else {
SkyKey globKey = GlobValue.internalKey(glob.getPackageId(), glob.getPackageRoot(), globSubdir, patternTail, glob.excludeDirs());
GlobValue globValue = (GlobValue) env.getValue(globKey);
if (globValue == null) {
return null;
}
matches.addTransitive(globValue.getMatches());
}
}
PathFragment dirPathFragment = glob.getPackageId().getPackageFragment().getRelative(globSubdir);
RootedPath dirRootedPath = RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment);
if (alwaysUseDirListing || containsGlobs(patternHead)) {
String subdirPattern = "**".equals(patternHead) ? glob.getPattern() : patternTail;
// Pattern contains globs, so a directory listing is required.
//
// Note that we have good reason to believe the directory exists: if this is the
// top-level directory of the package, the package's existence implies the directory's
// existence; if this is a lower-level directory in the package, then we got here from
// previous directory listings. Filesystem operations concurrent with build could mean the
// directory no longer exists, but DirectoryListingFunction handles that gracefully.
DirectoryListingValue listingValue = (DirectoryListingValue) env.getValue(DirectoryListingValue.key(dirRootedPath));
if (listingValue == null) {
return null;
}
// In order to batch Skyframe requests, we do three passes over the directory:
// (1) Process every dirent, keeping track of values we need to request if the dirent cannot
// be processed with current information (symlink targets and subdirectory globs/package
// lookups for some subdirectories).
// (2) Get those values and process the symlinks, keeping track of subdirectory globs/package
// lookups we may need to request in case the symlink's target is a directory.
// (3) Process the necessary subdirectories.
int direntsSize = listingValue.getDirents().size();
Map<SkyKey, Dirent> symlinkFileMap = Maps.newHashMapWithExpectedSize(direntsSize);
Map<SkyKey, Dirent> subdirMap = Maps.newHashMapWithExpectedSize(direntsSize);
Map<Dirent, Object> sortedResultMap = Maps.newTreeMap();
// First pass: do normal files and collect SkyKeys to request for subdirectories and symlinks.
for (Dirent dirent : listingValue.getDirents()) {
Type direntType = dirent.getType();
String fileName = dirent.getName();
if (!UnixGlob.matches(patternHead, fileName, regexPatternCache)) {
continue;
}
if (direntType == Dirent.Type.SYMLINK) {
// TODO(bazel-team): Consider extracting the symlink resolution logic.
// For symlinks, look up the corresponding FileValue. This ensures that if the symlink
// changes and "switches types" (say, from a file to a directory), this value will be
// invalidated. We also need the target's type to properly process the symlink.
symlinkFileMap.put(FileValue.key(RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment.getRelative(fileName))), dirent);
continue;
}
if (direntType == Dirent.Type.DIRECTORY) {
SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, subdirPattern);
if (keyToRequest != null) {
subdirMap.put(keyToRequest, dirent);
}
} else if (globMatchesBareFile) {
sortedResultMap.put(dirent, glob.getSubdir().getRelative(fileName));
}
}
Map<SkyKey, SkyValue> subdirAndSymlinksResult = env.getValues(Sets.union(subdirMap.keySet(), symlinkFileMap.keySet()));
if (env.valuesMissing()) {
return null;
}
Map<SkyKey, Dirent> symlinkSubdirMap = Maps.newHashMapWithExpectedSize(symlinkFileMap.size());
// Also process any known directories.
for (Map.Entry<SkyKey, SkyValue> lookedUpKeyAndValue : subdirAndSymlinksResult.entrySet()) {
if (symlinkFileMap.containsKey(lookedUpKeyAndValue.getKey())) {
FileValue symlinkFileValue = (FileValue) lookedUpKeyAndValue.getValue();
if (!symlinkFileValue.isSymlink()) {
throw new GlobFunctionException(new InconsistentFilesystemException("readdir and stat disagree about whether " + ((RootedPath) lookedUpKeyAndValue.getKey().argument()).asPath() + " is a symlink."), Transience.TRANSIENT);
}
if (!symlinkFileValue.exists()) {
continue;
}
Dirent dirent = symlinkFileMap.get(lookedUpKeyAndValue.getKey());
String fileName = dirent.getName();
if (symlinkFileValue.isDirectory()) {
SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, subdirPattern);
if (keyToRequest != null) {
symlinkSubdirMap.put(keyToRequest, dirent);
}
} else if (globMatchesBareFile) {
sortedResultMap.put(dirent, glob.getSubdir().getRelative(fileName));
}
} else {
processSubdir(lookedUpKeyAndValue, subdirMap, glob, sortedResultMap);
}
}
Map<SkyKey, SkyValue> symlinkSubdirResult = env.getValues(symlinkSubdirMap.keySet());
if (env.valuesMissing()) {
return null;
}
// pass.
for (Map.Entry<SkyKey, SkyValue> lookedUpKeyAndValue : symlinkSubdirResult.entrySet()) {
processSubdir(lookedUpKeyAndValue, symlinkSubdirMap, glob, sortedResultMap);
}
for (Map.Entry<Dirent, Object> fileMatches : sortedResultMap.entrySet()) {
addToMatches(fileMatches.getValue(), matches);
}
} else {
// Pattern does not contain globs, so a direct stat is enough.
String fileName = patternHead;
RootedPath fileRootedPath = RootedPath.toRootedPath(glob.getPackageRoot(), dirPathFragment.getRelative(fileName));
FileValue fileValue = (FileValue) env.getValue(FileValue.key(fileRootedPath));
if (fileValue == null) {
return null;
}
if (fileValue.exists()) {
if (fileValue.isDirectory()) {
SkyKey keyToRequest = getSkyKeyForSubdir(fileName, glob, patternTail);
if (keyToRequest != null) {
SkyValue valueRequested = env.getValue(keyToRequest);
if (env.valuesMissing()) {
return null;
}
Object fileMatches = getSubdirMatchesFromSkyValue(fileName, glob, valueRequested);
if (fileMatches != null) {
addToMatches(fileMatches, matches);
}
}
} else if (globMatchesBareFile) {
matches.add(glob.getSubdir().getRelative(fileName));
}
}
}
Preconditions.checkState(!env.valuesMissing(), skyKey);
NestedSet<PathFragment> matchesBuilt = matches.build();
// Use the same value to represent that we did not match anything.
if (matchesBuilt.isEmpty()) {
return GlobValue.EMPTY;
}
return new GlobValue(matchesBuilt);
}
use of com.google.devtools.build.lib.vfs.Dirent.Type in project bazel by bazelbuild.
the class ProcessPackageDirectory method getSubdirDeps.
private Iterable<SkyKey> getSubdirDeps(DirectoryListingValue dirListingValue, RootedPath rootedPath, RepositoryName repositoryName, Set<PathFragment> excludedPaths) {
Path root = rootedPath.getRoot();
PathFragment rootRelativePath = rootedPath.getRelativePath();
boolean followSymlinks = shouldFollowSymlinksWhenTraversing(dirListingValue.getDirents());
List<SkyKey> childDeps = new ArrayList<>();
for (Dirent dirent : dirListingValue.getDirents()) {
Type type = dirent.getType();
if (type != Type.DIRECTORY && (type != Type.SYMLINK || !followSymlinks)) {
// rise to infinite directory trees are diagnosed by FileValue.
continue;
}
String basename = dirent.getName();
if (rootRelativePath.equals(PathFragment.EMPTY_FRAGMENT) && PathPackageLocator.DEFAULT_TOP_LEVEL_EXCLUDES.contains(basename)) {
continue;
}
PathFragment subdirectory = rootRelativePath.getRelative(basename);
// If this subdirectory is one of the excluded paths, don't recurse into it.
if (excludedPaths.contains(subdirectory)) {
continue;
}
// If we have an excluded path that isn't below this subdirectory, we shouldn't pass that
// excluded path to our evaluation of the subdirectory, because the exclusion can't
// possibly match anything beneath the subdirectory.
//
// For example, if we're currently evaluating directory "a", are looking at its subdirectory
// "a/b", and we have an excluded path "a/c/d", there's no need to pass the excluded path
// "a/c/d" to our evaluation of "a/b".
//
// This strategy should help to get more skyframe sharing. Consider the example above. A
// subsequent request of "a/b/...", without any excluded paths, will be a cache hit.
//
// TODO(bazel-team): Replace the excludedPaths set with a trie or a SortedSet for better
// efficiency.
ImmutableSet<PathFragment> excludedSubdirectoriesBeneathThisSubdirectory = PathFragment.filterPathsStartingWith(excludedPaths, subdirectory);
RootedPath subdirectoryRootedPath = RootedPath.toRootedPath(root, subdirectory);
childDeps.add(skyKeyTransformer.makeSkyKey(repositoryName, subdirectoryRootedPath, excludedSubdirectoriesBeneathThisSubdirectory));
}
return childDeps;
}
Aggregations