use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class NewRepositoryBuildFileHandler method getBuildFileValue.
private FileValue getBuildFileValue(Rule rule, Environment env) throws RepositoryFunctionException, InterruptedException {
WorkspaceAttributeMapper mapper = WorkspaceAttributeMapper.of(rule);
String buildFileAttribute;
try {
buildFileAttribute = mapper.get("build_file", Type.STRING);
} catch (EvalException e) {
throw new RepositoryFunctionException(e, Transience.PERSISTENT);
}
RootedPath rootedBuild;
if (LabelValidator.isAbsolute(buildFileAttribute)) {
try {
// Parse a label
Label label = Label.parseAbsolute(buildFileAttribute);
SkyKey pkgSkyKey = PackageLookupValue.key(label.getPackageIdentifier());
PackageLookupValue pkgLookupValue = (PackageLookupValue) env.getValue(pkgSkyKey);
if (pkgLookupValue == null) {
return null;
}
if (!pkgLookupValue.packageExists()) {
throw new RepositoryFunctionException(new EvalException(rule.getLocation(), "Unable to load package for " + buildFileAttribute + ": not found."), Transience.PERSISTENT);
}
// And now for the file
Path packageRoot = pkgLookupValue.getRoot();
rootedBuild = RootedPath.toRootedPath(packageRoot, label.toPathFragment());
} catch (LabelSyntaxException ex) {
throw new RepositoryFunctionException(new EvalException(rule.getLocation(), String.format("In %s the 'build_file' attribute does not specify a valid label: %s", rule, ex.getMessage())), Transience.PERSISTENT);
}
} else {
// TODO(dmarting): deprecate using a path for the build_file attribute.
PathFragment buildFile = new PathFragment(buildFileAttribute);
Path buildFileTarget = workspacePath.getRelative(buildFile);
if (!buildFileTarget.exists()) {
throw new RepositoryFunctionException(new EvalException(rule.getLocation(), String.format("In %s the 'build_file' attribute does not specify an existing file " + "(%s does not exist)", rule, buildFileTarget)), Transience.PERSISTENT);
}
if (buildFile.isAbsolute()) {
rootedBuild = RootedPath.toRootedPath(buildFileTarget.getParentDirectory(), new PathFragment(buildFileTarget.getBaseName()));
} else {
rootedBuild = RootedPath.toRootedPath(workspacePath, buildFile);
}
}
SkyKey buildFileKey = FileValue.key(rootedBuild);
FileValue buildFileValue;
try {
// Note that this dependency is, strictly speaking, not necessary: the symlink could simply
// point to this FileValue and the symlink chasing could be done while loading the package
// but this results in a nicer error message and it's correct as long as RepositoryFunctions
// don't write to things in the file system this FileValue depends on. In theory, the latter
// is possible if the file referenced by build_file is a symlink to somewhere under the
// external/ directory, but if you do that, you are really asking for trouble.
buildFileValue = (FileValue) env.getValueOrThrow(buildFileKey, IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class);
if (buildFileValue == null) {
return null;
}
} catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
throw new RepositoryFunctionException(new IOException("Cannot lookup " + buildFileAttribute + ": " + e.getMessage()), Transience.TRANSIENT);
}
return buildFileValue;
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class RepositoryFunction method addExternalFilesDependencies.
/**
* For files that are under $OUTPUT_BASE/external, add a dependency on the corresponding rule so
* that if the WORKSPACE file changes, the File/DirectoryStateValue will be re-evaluated.
*
* <p>Note that: - We don't add a dependency on the parent directory at the package root boundary,
* so the only transitive dependencies from files inside the package roots to external files are
* through symlinks. So the upwards transitive closure of external files is small. - The only way
* other than external repositories for external source files to get into the skyframe graph in
* the first place is through symlinks outside the package roots, which we neither want to
* encourage nor optimize for since it is not common. So the set of external files is small.
*/
public static void addExternalFilesDependencies(RootedPath rootedPath, BlazeDirectories directories, Environment env) throws IOException, InterruptedException {
Path externalRepoDir = getExternalRepositoryDirectory(directories);
PathFragment repositoryPath = rootedPath.asPath().relativeTo(externalRepoDir);
if (repositoryPath.segmentCount() == 0) {
// repository path.
return;
}
String repositoryName = repositoryPath.getSegment(0);
try {
// Add a dependency to the repository rule. RepositoryDirectoryValue does add this dependency
// already but we want to catch RepositoryNotFoundException, so invoke #getRule first.
RepositoryFunction.getRule(repositoryName, env);
if (repositoryPath.segmentCount() > 1) {
// For all file under the repository directory, depends on the actual RepositoryDirectory
// function so we get invalidation when the repository is fetched.
// For the repository directory itself, we cannot depends on the RepositoryDirectoryValue
// (cycle).
env.getValue(RepositoryDirectoryValue.key(RepositoryName.create("@" + repositoryName)));
}
} catch (RepositoryFunction.RepositoryNotFoundException ex) {
// of the resolution. Therefore we are safe to ignore that Exception.
return;
} catch (RepositoryFunctionException | LabelSyntaxException ex) {
// This should never happen.
throw new IllegalStateException("Repository " + repositoryName + " cannot be resolved for path " + rootedPath, ex);
}
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class PathWindowsTest method testCaseInsensitiveRootedPath.
@Test
public void testCaseInsensitiveRootedPath() {
Path ancestor = filesystem.getPath("C:\\foo\\bar");
assertThat(ancestor).isInstanceOf(WindowsPath.class);
Path child = filesystem.getPath("C:\\FOO\\Bar\\baz");
assertThat(child).isInstanceOf(WindowsPath.class);
assertThat(child.startsWith(ancestor)).isTrue();
assertThat(child.relativeTo(ancestor)).isEqualTo(new PathFragment("baz"));
RootedPath actual = RootedPath.toRootedPath(ancestor, child);
assertThat(actual.getRoot()).isEqualTo(ancestor);
assertThat(actual.getRelativePath()).isEqualTo(new PathFragment("baz"));
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class SkylarkRepositoryContext method getPathFromLabel.
// Resolve the label given by value into a file path.
private SkylarkPath getPathFromLabel(Label label) throws EvalException, InterruptedException {
RootedPath rootedPath = getRootedPathFromLabel(label, env);
SkyKey fileSkyKey = FileValue.key(rootedPath);
FileValue fileValue = null;
try {
fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class);
} catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
throw new EvalException(Location.BUILTIN, e);
}
if (fileValue == null) {
throw SkylarkRepositoryFunction.restart();
}
if (!fileValue.isFile()) {
throw new EvalException(Location.BUILTIN, "Not a file: " + rootedPath.asPath().getPathString());
}
// A label do not contains space so it safe to use as a key.
markerData.put("FILE:" + label, Integer.toString(fileValue.realFileStateValue().hashCode()));
return new SkylarkPath(rootedPath.asPath());
}
use of com.google.devtools.build.lib.vfs.RootedPath 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);
}
Aggregations