| // 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.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.util.Preconditions; |
| 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>"); |
| } |
| |
| @Override |
| public void reprLegacy(SkylarkPrinter printer) { |
| printer.append(toString()); |
| } |
| } |