| // 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.base.Preconditions; |
| import com.google.common.collect.Interner; |
| import com.google.common.collect.Interners; |
| import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact; |
| import com.google.devtools.build.lib.actions.Artifact.SourceArtifact; |
| import com.google.devtools.build.lib.cmdline.LabelConstants; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.skyframe.serialization.VisibleForSerialization; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import com.google.devtools.build.lib.starlarkbuildapi.FileRootApi; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import com.google.devtools.build.lib.vfs.Root; |
| import java.util.Objects; |
| import net.starlark.java.eval.Printer; |
| |
| /** |
| * 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>Derived roots must have paths that point inside the exec root ({@link |
| * com.google.devtools.build.lib.analysis.BlazeDirectories#getExecRoot}), i.e. below the directory |
| * that is the root of the merged directory tree. |
| * |
| * <p>For example, if |
| * |
| * <ul> |
| * <li>your workspace is {@code /home/my_workspace/} |
| * <li>your package path is {@code /home/my_workspace/} (the norm unless you're customizing {@link |
| * com.google.devtools.build.lib.pkgcache.PackageOptions#packagePath}). |
| * <li>your workspace has a source file at {@code /home/my_workspace/myapp/source.go} |
| * <li>you build a binary that outputs {@code /home/my_workspace/bazel-out/x86-opt/bin/a/mybinary} |
| * </ul> |
| * |
| * <p>then |
| * |
| * <ul> |
| * <li>Bazel creates an "output base" directory {@code $OUTPUT_BASE} which it uses for staging |
| * build work: {@link com.google.devtools.build.lib.analysis.BlazeDirectories#getOutputBase()} |
| * <li>Bazel creates an exec root at {@code $OUTPUT_BASE/execroot/my_workspace/}. This symlinks |
| * all files and directories under {@code /home/my_workspace/}. This is the working directory |
| * where actions run (either directly for local execution or as the base for staging remote |
| * execution paths). This is also the base directory for writing outputs. |
| * <li>{@code /home/my_workspace/myapp/source.go} is a {@link SourceArtifact} with source root |
| * {@code /home/my_workspace/} |
| * <li>{@code /home/my_workspace/bazel-out/x86-opt/bin/a/mybinary} is a {@link DerivedArtifact}. |
| * Because derived artifacts are written under the exec root, {@code |
| * /home/my_workspace/bazel-out} is a symlink to {@code $EXEC_ROOT/bazel-out}. So {@code |
| * mybinary} is actually at {@code $EXEC_ROOT/bazel-out/x86-opt/bin/mybinary}. Its derived |
| * root is therefore {@code $EXEC_ROOT/bazel-out/x86-opt/bin/}. |
| * </ul> |
| * |
| * <p>The "exec path" ({@link Artifact#getExecPath()}, {@link #getExecPath()}, etc.) is an entity's |
| * path relative to the exec root. So {@code /home/my_workspace/myapp/source.go}'s exec path is |
| * {@code myapp/source.go} and {@code /home/my_workspace/bazel-out/x86-opt/bin/a/mybinary}'s exec |
| * path is {@code bazel-out/x86-opt/bin/a/mybinary} |
| * |
| * <p>The "root-relative path" ({@link Artifact#getRootRelativePath()}) is a entity's path relative |
| * to its root. So {@code /home/my_workspace/myapp/source.go}'s root-relative path is {@code |
| * myapp/source.go} and {@code /home/my_workspace/bazel-out/x86-opt/bin/a/mybinary}'s root-relative |
| * path is {@code a/mybinary}. |
| * |
| * <p>For concrete examples, run {@code $ bazel info} in your terminal after a build. Also see <a |
| * href="https://bazel.build/remote/output-directories">Bazel's output directory docs</a>. |
| */ |
| @AutoCodec |
| @Immutable |
| public final class ArtifactRoot implements Comparable<ArtifactRoot>, FileRootApi { |
| private static final Interner<ArtifactRoot> INTERNER = Interners.newWeakInterner(); |
| /** |
| * Do not use except in tests and in {@link |
| * com.google.devtools.build.lib.skyframe.SkyframeExecutor}. |
| * |
| * <p>Returns the given path as a source root. The path may not be {@code null}. |
| */ |
| public static ArtifactRoot asSourceRoot(Root root) { |
| return INTERNER.intern( |
| new ArtifactRoot(root, PathFragment.EMPTY_FRAGMENT, RootType.MainSource)); |
| } |
| |
| /** |
| * Do not use except in tests and in {@link |
| * com.google.devtools.build.lib.skyframe.SkyframeExecutor}. |
| * |
| * <p>Returns the given path as the external source root. The path should end with {@link |
| * LabelConstants.EXTERNAL_REPOSITORY_LOCATION} since the external repository root is always |
| * $OUTPUT_BASE/external regardless of the layout of the exec root. |
| */ |
| public static ArtifactRoot asExternalSourceRoot(Root root) { |
| Preconditions.checkArgument( |
| root.asPath() |
| .asFragment() |
| .getParentDirectory() |
| .endsWith(LabelConstants.EXTERNAL_REPOSITORY_LOCATION)); |
| return INTERNER.intern( |
| new ArtifactRoot(root, PathFragment.EMPTY_FRAGMENT, RootType.ExternalSource)); |
| } |
| |
| /** |
| * Constructs an ArtifactRoot given the output prefixes. (eg, "bin"), and (eg, "testlogs") |
| * relative to the execRoot. |
| * |
| * <p>Be careful with this method - all derived roots must be within the derived artifacts tree, |
| * defined in ArtifactFactory (see {@link ArtifactFactory#isDerivedArtifact(PathFragment)}). |
| * |
| * <p>Call {@link #asDerivedRoot(Path, RootType, PathFragment)} if you already have a {@link |
| * PathFragment} instance for the exec path. |
| */ |
| public static ArtifactRoot asDerivedRoot(Path execRoot, RootType rootType, String... prefixes) { |
| PathFragment execPath = PathFragment.EMPTY_FRAGMENT; |
| for (String prefix : prefixes) { |
| // Tests can have empty segments here, be gentle to them. |
| if (!prefix.isEmpty()) { |
| execPath = execPath.getChild(prefix); |
| } |
| } |
| return asDerivedRoot(execRoot, rootType, execPath); |
| } |
| |
| /** |
| * Constructs an {@link ArtifactRoot} given the execPath, relative to the execRoot. |
| * |
| * <p>Be careful with this method - all derived roots must be within the derived artifacts tree, |
| * defined in ArtifactFactory (see {@link ArtifactFactory#isDerivedArtifact(PathFragment)}). |
| */ |
| public static ArtifactRoot asDerivedRoot( |
| Path execRoot, RootType rootType, PathFragment execPath) { |
| // Make sure that we are not creating a derived artifact under the execRoot. |
| Preconditions.checkArgument(!execPath.isEmpty(), "empty execPath"); |
| Preconditions.checkArgument(!execPath.isAbsolute(), "execPath must be relative: %s", execPath); |
| Preconditions.checkArgument( |
| !execPath.containsUplevelReferences(), |
| "execPath: %s contains parent directory reference (..)", |
| execPath); |
| Preconditions.checkArgument( |
| isOutputRootType(rootType) || isMiddlemanRootType(rootType), |
| "%s is not a derived root type", |
| rootType); |
| Path root = execRoot.getRelative(execPath); |
| return INTERNER.intern(new ArtifactRoot(Root.fromPath(root), execPath, rootType)); |
| } |
| |
| @VisibleForSerialization |
| @AutoCodec.Instantiator |
| static ArtifactRoot createForSerialization( |
| Root rootForSerialization, PathFragment execPath, RootType rootType) { |
| if (!isOutputRootType(rootType)) { |
| return INTERNER.intern(new ArtifactRoot(rootForSerialization, execPath, rootType)); |
| } |
| return asDerivedRoot(rootForSerialization.asPath(), rootType, execPath); |
| } |
| |
| /** |
| * ArtifactRoot types. Callers of asDerivedRoot methods need to specify which type of derived root |
| * artifact they want to create, which is why this enum is public. |
| */ |
| public enum RootType { |
| MainSource, |
| ExternalSource, |
| Output, |
| Middleman, |
| // Sibling root types are in effect when --experimental_sibling_repository_layout is activated. |
| // These will eventually replace the above Output and Middleman types when the flag becomes |
| // the default option and then removed. |
| SiblingMainOutput, |
| SiblingMainMiddleman, |
| SiblingExternalOutput, |
| SiblingExternalMiddleman, |
| } |
| |
| private final Root root; |
| private final PathFragment execPath; |
| private final RootType rootType; |
| |
| private ArtifactRoot(Root root, PathFragment execPath, RootType rootType) { |
| this.root = Preconditions.checkNotNull(root); |
| this.execPath = execPath; |
| this.rootType = rootType; |
| } |
| |
| @Override |
| public boolean isImmutable() { |
| return true; // immutable and Starlark-hashable |
| } |
| |
| public Root getRoot() { |
| return root; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @Override |
| public String getExecPathString() { |
| return execPath.getPathString(); |
| } |
| |
| public boolean isSourceRoot() { |
| return rootType == RootType.MainSource || rootType == RootType.ExternalSource; |
| } |
| |
| private static boolean isOutputRootType(RootType rootType) { |
| return rootType == RootType.SiblingMainOutput |
| || rootType == RootType.SiblingExternalOutput |
| || rootType == RootType.Output; |
| } |
| |
| private static boolean isMiddlemanRootType(RootType rootType) { |
| return rootType == RootType.SiblingMainMiddleman |
| || rootType == RootType.SiblingExternalMiddleman |
| || rootType == RootType.Middleman; |
| } |
| |
| boolean isMiddlemanRoot() { |
| return isMiddlemanRootType(rootType); |
| } |
| |
| public boolean isExternal() { |
| return rootType == RootType.ExternalSource |
| || rootType == RootType.SiblingExternalOutput |
| || rootType == RootType.SiblingExternalMiddleman; |
| } |
| |
| /** |
| * Returns true if the ArtifactRoot is a legacy derived root type, i.e. a derived root type |
| * created without the --experimental_sibling_repository_layout flag set. |
| */ |
| public boolean isLegacy() { |
| return rootType == RootType.Output || rootType == RootType.Middleman; |
| } |
| |
| @Override |
| public int compareTo(ArtifactRoot o) { |
| return root.compareTo(o.root); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(root, execPath, rootType); |
| } |
| |
| /** |
| * The Root of a derived ArtifactRoot contains the exec path. In order to avoid duplicating that |
| * path, and enable the Root to be serialized as a constant, we return the "exec root" Root here, |
| * by stripping the exec path. That Root is likely to be serialized as a constant by {@link |
| * Root.RootCodec}, saving a lot of serialized bytes on the wire. |
| */ |
| @SuppressWarnings("unused") // Used by @AutoCodec. |
| Root getRootForSerialization() { |
| if (!isOutputRootType(rootType)) { |
| return root; |
| } |
| // Find fragment of root that does not include execPath and return just that root. It is likely |
| // to be serialized as a constant by RootCodec. For instance, if the original exec root was |
| // /execroot, and this root was /execroot/bazel-out/bin, with execPath bazel-out/bin, then we |
| // just serialize /execroot and bazel-out/bin separately. |
| // We just want to strip execPath from root, but I don't know a trivial way to do that. |
| PathFragment rootFragment = root.asPath().asFragment(); |
| return Root.fromPath( |
| root.asPath() |
| .getFileSystem() |
| .getPath( |
| rootFragment.subFragment( |
| 0, rootFragment.segmentCount() - execPath.segmentCount()))); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) { |
| return true; |
| } |
| if (!(o instanceof ArtifactRoot r)) { |
| return false; |
| } |
| return root.equals(r.root) && execPath.equals(r.execPath) && rootType == r.rootType; |
| } |
| |
| @Override |
| public String toString() { |
| return root + (isSourceRoot() ? "[source]" : "[derived]"); |
| } |
| |
| @Override |
| public void repr(Printer printer) { |
| printer.append(isSourceRoot() ? "<source root>" : "<derived root>"); |
| } |
| } |