// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.pkgcache; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.BuildFileNotFoundException; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.UnixGlob; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; /** * A mapping from the name of a package to the location of its BUILD file. * The implementation composes an ordered sequence of directories according to * the package-path rules. * *
All methods are thread-safe, and (assuming no change to the underlying
* filesystem) idempotent.
*/
public class PathPackageLocator implements Serializable {
public static final Set The package's root directory may be computed by calling getParentFile()
* on the result of this function.
*
* Instances of this interface do not attempt to do any caching, nor
* implement checks for package-boundary crossing logic; the PackageCache
* does that.
*
* If the same package exists beneath multiple package path entries, the
* first path that matches always wins.
*/
public Path getPackageBuildFile(PackageIdentifier packageName) throws NoSuchPackageException {
Path buildFile = getPackageBuildFileNullable(packageName, UnixGlob.DEFAULT_SYSCALLS_REF);
if (buildFile == null) {
throw new BuildFileNotFoundException(packageName, "BUILD file not found on package path");
}
return buildFile;
}
/**
* Like #getPackageBuildFile(), but returns null instead of throwing.
* @param packageIdentifier the name of the package.
* @param cache a filesystem-level cache of stat() calls.
*/
public Path getPackageBuildFileNullable(PackageIdentifier packageIdentifier,
AtomicReference extends UnixGlob.FilesystemCalls> cache) {
Preconditions.checkArgument(!packageIdentifier.getRepository().isDefault());
if (packageIdentifier.getRepository().isMain()) {
return getFilePath(packageIdentifier.getPackageFragment().getRelative("BUILD"), cache);
} else {
Verify.verify(outputBase != null, String.format(
"External package '%s' needs to be loaded but this PathPackageLocator instance does not "
+ "support external packages", packageIdentifier));
// This works only to some degree, because it relies on the presence of the repository under
// $OUTPUT_BASE/external, which is created by the appropriate RepositoryDirectoryValue. This
// is true for the invocation in GlobCache, but not for the locator.getBuildFileForPackage()
// invocation in Parser#include().
Path buildFile = outputBase.getRelative(
packageIdentifier.getSourceRoot()).getRelative("BUILD");
FileStatus stat = cache.get().statNullable(buildFile, Symlinks.FOLLOW);
if (stat != null && stat.isFile()) {
return buildFile;
} else {
return null;
}
}
}
/**
* Returns an immutable ordered list of the directories on the package path.
*/
public ImmutableList If there are WORKSPACE files beneath multiple package path entries, the first one always
* wins.
*/
public Path getWorkspaceFile() {
AtomicReference extends UnixGlob.FilesystemCalls> cache = UnixGlob.DEFAULT_SYSCALLS_REF;
// TODO(bazel-team): correctness in the presence of changes to the location of the WORKSPACE
// file.
return getFilePath(new PathFragment("WORKSPACE"), cache);
}
private Path getFilePath(PathFragment suffix,
AtomicReference extends UnixGlob.FilesystemCalls> cache) {
for (Path pathEntry : pathEntries) {
Path buildFile = pathEntry.getRelative(suffix);
FileStatus stat = cache.get().statNullable(buildFile, Symlinks.FOLLOW);
if (stat != null && stat.isFile()) {
return buildFile;
}
}
return null;
}
@Override
public int hashCode() {
return Objects.hash(pathEntries, outputBase);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PathPackageLocator)) {
return false;
}
PathPackageLocator pathPackageLocator = (PathPackageLocator) other;
return Objects.equals(getPathEntries(), pathPackageLocator.getPathEntries())
&& Objects.equals(outputBase, pathPackageLocator.outputBase);
}
public Path getOutputBase() {
return outputBase;
}
}