blob: 7cb16981cb713908f9838a192eb15731675c3fbe [file] [log] [blame]
// 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.actions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.Serializable;
import java.util.Objects;
import javax.annotation.Nullable;
/**
* A root for an artifact. The roots are the directories containing artifacts, and they are mapped
* together into a single directory tree to form the execution environment. There are two kinds of
* roots, source roots and derived roots. Source roots correspond to entries of the package path,
* and they can be anywhere on disk. Derived roots correspond to output directories; there are
* generally different output directories for different configurations, and different types of
* output (bin, genfiles, includes, etc.).
*
* <p>When mapping the roots into a single directory tree, the source roots are merged, such that
* each package is accessed in its entirety from a single source root. The package cache is
* responsible for determining that mapping. The derived roots, on the other hand, have to be
* distinct. (It is currently allowed to have a derived root that is the prefix of another one.)
*
* <p>The derived roots must have paths that point inside the exec root, i.e. below the directory
* that is the root of the merged directory tree.
*/
@SkylarkModule(name = "root",
category = SkylarkModuleCategory.BUILTIN,
doc = "A root for files. The roots are the directories containing files, and they are mapped "
+ "together into a single directory tree to form the execution environment.")
@Immutable
public final class Root implements Comparable<Root>, Serializable, SkylarkValue {
/**
* Returns the given path as a source root. The path may not be {@code null}.
*/
// TODO(kchodorow): remove once roots don't need to know if they're in the main repo.
public static Root asSourceRoot(Path path, boolean isMainRepo) {
return new Root(null, path, false, isMainRepo);
}
// This must always be consistent with Package.getSourceRoot; otherwise computing source roots
// from exec paths does not work, which can break the action cache for input-discovering actions.
public static Root computeSourceRoot(Path packageRoot, RepositoryName repository) {
if (repository.isMain()) {
return Root.asSourceRoot(packageRoot, true);
} else {
Path actualRoot = packageRoot;
for (int i = 0; i < repository.getSourceRoot().segmentCount(); i++) {
actualRoot = actualRoot.getParentDirectory();
}
return Root.asSourceRoot(actualRoot, false);
}
}
/**
* testonly until {@link #asSourceRoot(Path, boolean)} is deleted.
*/
public static Root asSourceRoot(Path path) {
return asSourceRoot(path, true);
}
/**
* DO NOT USE IN PRODUCTION CODE!
*
* <p>Returns the given path as a derived root. This method only exists as a convenience for
* tests, which don't need a proper Root object.
*/
@VisibleForTesting
public static Root asDerivedRoot(Path path) {
return new Root(path, path, true);
}
/**
* Returns the given path as a derived root, relative to the given exec root. The root must be a
* proper sub-directory of the exec root (i.e. not equal). Neither may be {@code null}.
*
* <p>Be careful with this method - all derived roots must be registered with the artifact factory
* before the analysis phase.
*/
// TODO(kchodorow): remove once roots don't need to know if they're in the main repo.
public static Root asDerivedRoot(Path execRoot, Path root, boolean isMainRepo) {
Preconditions.checkArgument(root.startsWith(execRoot));
Preconditions.checkArgument(!root.equals(execRoot));
return new Root(execRoot, root, false, isMainRepo);
}
/**
* testonly until {@link #asDerivedRoot(Path, Path, boolean)} is deleted.
*/
public static Root asDerivedRoot(Path execRoot, Path root) {
return Root.asDerivedRoot(execRoot, root, true);
}
// TODO(kchodorow): remove once roots don't need to know if they're in the main repo.
public static Root middlemanRoot(Path execRoot, Path outputDir, boolean isMainRepo) {
Path root = outputDir.getRelative("internal");
Preconditions.checkArgument(root.startsWith(execRoot));
Preconditions.checkArgument(!root.equals(execRoot));
return new Root(execRoot, root, true, isMainRepo);
}
/**
* testonly until {@link #middlemanRoot(Path, Path, boolean)} is deleted.
*/
public static Root middlemanRoot(Path execRoot, Path outputDir) {
return Root.middlemanRoot(execRoot, outputDir, true);
}
/**
* Returns the exec root as a derived root. The exec root should never be treated as a derived
* root, but this is currently allowed. Do not add any further uses besides the ones that already
* exist!
*/
// TODO(kchodorow): remove isMainRepo once roots don't need to know if they're in the main repo.
static Root execRootAsDerivedRoot(Path execRoot, boolean isMainRepo) {
return new Root(execRoot, execRoot, false, isMainRepo);
}
@Nullable private final Path execRoot;
private final Path path;
private final boolean isMiddlemanRoot;
private final boolean isMainRepo;
private final PathFragment execPath;
private Root(@Nullable Path execRoot, Path path, boolean isMiddlemanRoot, boolean isMainRepo) {
this.execRoot = execRoot;
this.path = Preconditions.checkNotNull(path);
this.isMiddlemanRoot = isMiddlemanRoot;
this.isMainRepo = isMainRepo;
this.execPath = isSourceRoot() ? PathFragment.EMPTY_FRAGMENT : path.relativeTo(execRoot);
}
private Root(@Nullable Path execRoot, Path path, boolean isMainRepo) {
this(execRoot, path, false, isMainRepo);
}
public Path getPath() {
return path;
}
/**
* Returns the path fragment from the exec root to the actual root. For source roots, this returns
* the empty fragment.
*/
public PathFragment getExecPath() {
return execPath;
}
@SkylarkCallable(name = "path", structField = true,
doc = "Returns the relative path from the exec root to the actual root.")
public String getExecPathString() {
return getExecPath().getPathString();
}
@Nullable
public Path getExecRoot() {
return execRoot;
}
public boolean isSourceRoot() {
return execRoot == null;
}
boolean isMiddlemanRoot() {
return isMiddlemanRoot;
}
public boolean isMainRepo() {
return isMainRepo;
}
@Override
public int compareTo(Root o) {
return path.compareTo(o.path);
}
@Override
public int hashCode() {
return Objects.hash(execRoot, path.hashCode(), isMainRepo);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Root)) {
return false;
}
Root r = (Root) o;
return path.equals(r.path) && Objects.equals(execRoot, r.execRoot)
&& Objects.equals(isMainRepo, r.isMainRepo);
}
@Override
public String toString() {
return path + (isSourceRoot() ? "[source]" : "[derived]");
}
@Override
public void repr(SkylarkPrinter printer) {
printer.append(isSourceRoot() ? "<source root>" : "<derived root>");
}
}