| // 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.analysis; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Ascii; |
| import com.google.common.hash.HashCode; |
| import com.google.devtools.build.lib.actions.ArtifactRoot; |
| import com.google.devtools.build.lib.actions.ArtifactRoot.RootType; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.util.StringCanonicalizer; |
| import com.google.devtools.build.lib.vfs.Path; |
| import java.util.Objects; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Encapsulates the directories related to a workspace. |
| * |
| * <p>A {@code workspace>} is a directory tree containing the source files you want to build. |
| * |
| * <p>The {@code workspace Path} object this class stores is the workspace's root directory, which |
| * contains a {@code WORKSPACE} file that marks and configures the workspace. When you build {@code |
| * //my:project}, this signifies a target named {@code project} in a {@code BUILD} file in the |
| * {@code my} subdirectory under the workspace root. You can find the workspace root directory by |
| * running {@code $ bazel info | grep workspace}. |
| * |
| * <p>The {@code outputBase} is where all workspace output is written. This includes both build |
| * outputs and internal files Bazel uses to support builds (like the action cache, log files, and |
| * external repository mappings). This path is only meaningful for core Bazel devs: it's not part of |
| * the public user API. This path is not under the workspace root (since its purpose isn't to host |
| * workspace source files). This appears as {@code _bazel_$USER/$SOME_HASH/} under some local file |
| * system root. Exact paths vary depending on what machine you're running Bazel on. You can find |
| * this path by running {@code $ bazel info | grep output_base}. |
| * |
| * <p>The {@code execRoot} is the working directory for all spawned tools. It includes both the |
| * subdirectory where Bazel writes build outputs (the {@code outputPath}) and the symlink forest |
| * Bazel constructs to map workspace source files the spawned tool can access when it runs. It |
| * generally looks like {@code $OUTPUT_BASE/execroot/$WORKSPACE_IDENTIFIER}. You can find this path |
| * by running {@code $ bazel info | grep execution_root}. |
| * |
| * <p>The {@code outputPath} (confusingly similar name to {@code outputBase}, alas) is the root path |
| * where Bazel writes build outputs. In other words, any action transforming a source file into a |
| * generated output writes that output under this path. It generally looks like {@code |
| * $OUTPUT_BASE/execroot/$WORKSPACE_IDENTIFIER/bazel-out}. You can find this path by running {@code |
| * $ bazel info | grep output_path}. |
| * |
| * <p>Care must be taken to avoid multiple Bazel instances trying to write to the same output tree. |
| * This is enforced by requiring a 1:1 correspondence between a running Bazel instance and an output |
| * base. |
| * |
| * <p>If the user does not qualify an output base directory, the startup code will derive it |
| * deterministically from the workspace. Note also that while the Bazel server process runs with the |
| * workspace directory as its working directory, the client process may have a different working |
| * directory, typically a subdirectory. |
| * |
| * <p>Do not put shortcuts to specific files here! |
| */ |
| @Immutable |
| public final class BlazeDirectories { |
| // TODO(bazel-team): is there actually any reason to put build-info files here? Can we at least |
| // give the directory a better name? |
| // Build-info directory, relative to execRoot/blaze-out/configuration. Only one segment allowed. |
| public static final String RELATIVE_BUILD_INFO_DIR = StringCanonicalizer.intern("include"); |
| @VisibleForTesting static final String DEFAULT_EXEC_ROOT = "default-exec-root"; |
| |
| private final ServerDirectories serverDirectories; |
| /** Workspace root and server CWD. */ |
| private final Path workspace; |
| /** |
| * The root of the user's local JDK install, to be used as the default target javabase and as a |
| * fall-back host_javabase. This is not the embedded JDK. |
| */ |
| private final Path defaultSystemJavabase; |
| /** The root of all build actions. */ |
| private final Path blazeExecRoot; |
| |
| // These two are kept to avoid creating new objects every time they are accessed. This showed up |
| // in a profiler. |
| private final Path blazeOutputPath; |
| private final Path localOutputPath; |
| private final String productName; |
| |
| public BlazeDirectories( |
| ServerDirectories serverDirectories, |
| Path workspace, |
| Path defaultSystemJavabase, |
| String productName) { |
| this.serverDirectories = serverDirectories; |
| this.workspace = workspace; |
| this.defaultSystemJavabase = defaultSystemJavabase; |
| this.productName = productName; |
| Path outputBase = serverDirectories.getOutputBase(); |
| if (Ascii.equalsIgnoreCase(productName, "blaze")) { |
| boolean useDefaultExecRootName = |
| this.workspace == null || this.workspace.getParentDirectory() == null; |
| if (useDefaultExecRootName) { |
| // TODO(bazel-team): if workspace is null execRoot should be null, but at the moment there |
| // is a lot of code that depends on it being non-null. |
| this.blazeExecRoot = getExecRootBase().getChild(DEFAULT_EXEC_ROOT); |
| } else { |
| this.blazeExecRoot = getExecRootBase().getChild(workspace.getBaseName()); |
| } |
| this.blazeOutputPath = blazeExecRoot.getRelative(getRelativeOutputPath()); |
| } else { |
| this.blazeExecRoot = null; |
| this.blazeOutputPath = null; |
| } |
| this.localOutputPath = outputBase.getRelative(getRelativeOutputPath()); |
| } |
| |
| public ServerDirectories getServerDirectories() { |
| return serverDirectories; |
| } |
| |
| /** Returns the installation base directory. */ |
| public Path getInstallBase() { |
| return serverDirectories.getInstallBase(); |
| } |
| |
| /** |
| * Returns the workspace directory to use for build artifacts. |
| * |
| * <p>It may effectively differ from the working directory. Please use {@link |
| * #getWorkingDirectory()} for writes within the working directory. |
| */ |
| @Nullable |
| public Path getWorkspace() { |
| // Make sure to use the same file system as exec root. |
| return workspace != null |
| ? getExecRootBase().getFileSystem().getPath(workspace.asFragment()) |
| : null; |
| } |
| |
| /** Returns working directory of the server. */ |
| public Path getWorkingDirectory() { |
| return workspace; |
| } |
| |
| /** Returns the root of the user's local JDK install (not the embedded JDK). */ |
| public Path getLocalJavabase() { |
| return defaultSystemJavabase; |
| } |
| |
| /** Returns if the workspace directory is a valid workspace. */ |
| public boolean inWorkspace() { |
| return this.workspace != null; |
| } |
| |
| /** |
| * Returns the base of the output tree, which hosts all build and scratch output for a user and |
| * workspace. |
| */ |
| public Path getOutputBase() { |
| return serverDirectories.getOutputBase(); |
| } |
| |
| public Path getExecRootBase() { |
| return serverDirectories.getExecRootBase(); |
| } |
| |
| /** |
| * Returns the execution root of Blaze. |
| * |
| * @deprecated Avoid using this method as it will only work if your workspace is named like |
| * Google's internal workspace. This method will not work in Bazel. Use {@link |
| * #getExecRoot(String)} instead. |
| * <p><em>AVOID USING THIS METHOD</em> |
| */ |
| @Nullable |
| @Deprecated |
| public Path getBlazeExecRoot() { |
| return blazeExecRoot; |
| } |
| |
| /** |
| * Returns the execution root for a particular repository. This is the directory underneath which |
| * Blaze builds the source symlink forest, to represent the merged view of different workspaces |
| * specified with --package_path. |
| */ |
| public Path getExecRoot(String workspaceName) { |
| return getExecRootBase().getRelative(workspaceName); |
| } |
| |
| /** |
| * Returns the output path of Blaze. |
| * |
| * @deprecated Avoid using this method as it will only work if your workspace is named like |
| * Google's internal workspace. This method will not work in Bazel. Use {@link |
| * #getOutputPath(String)} instead. |
| * <p><em>AVOID USING THIS METHOD</em> |
| */ |
| @Nullable |
| @Deprecated |
| public Path getBlazeOutputPath() { |
| return blazeOutputPath; |
| } |
| |
| /** Returns the output path used by this Blaze instance. */ |
| public Path getOutputPath(String workspaceName) { |
| return getExecRoot(workspaceName).getRelative(getRelativeOutputPath()); |
| } |
| |
| /** Returns the local output path used by this Blaze instance. */ |
| public Path getLocalOutputPath() { |
| return localOutputPath; |
| } |
| |
| /** |
| * Returns the directory where actions can store temporary files (such as their stdout and stderr) |
| * during a build. If the directory already exists, the directory is cleaned. |
| */ |
| public Path getActionTempsDirectory(Path execRoot) { |
| return execRoot.getRelative(getRelativeOutputPath()).getRelative("_tmp/actions"); |
| } |
| |
| public Path getPersistentActionOutsDirectory(Path execRoot) { |
| return execRoot.getRelative(getRelativeOutputPath()).getRelative("_actions"); |
| } |
| |
| /** Returns the installed embedded binaries directory, under the shared installBase location. */ |
| public Path getEmbeddedBinariesRoot() { |
| return serverDirectories.getEmbeddedBinariesRoot(); |
| } |
| |
| /** |
| * Returns the configuration-independent root where the build-data should be placed, given the |
| * {@link BlazeDirectories} of this server instance. Nothing else should be placed here. |
| */ |
| public ArtifactRoot getBuildDataDirectory(String workspaceName) { |
| return ArtifactRoot.asDerivedRoot( |
| getExecRoot(workspaceName), RootType.Output, getRelativeOutputPath(productName)); |
| } |
| |
| /** |
| * Returns the MD5 content hash of the blaze binary (includes deploy JAR, embedded binaries, and |
| * anything else that ends up in the install_base). |
| */ |
| public HashCode getInstallMD5() { |
| return serverDirectories.getInstallMD5(); |
| } |
| |
| /** |
| * Returns the directory where Bazel writes build outputs, relative to the execRoot. |
| * |
| * <p>For example: {@code "bazel-out"}. |
| */ |
| public String getRelativeOutputPath() { |
| return BlazeDirectories.getRelativeOutputPath(productName); |
| } |
| |
| /** |
| * Returns the directory where Bazel writes build outputs, relative to the execRoot. |
| * |
| * <p>For example: {@code "bazel-out"}. |
| */ |
| public static String getRelativeOutputPath(String productName) { |
| return StringCanonicalizer.intern(productName + "-out"); |
| } |
| |
| public String getProductName() { |
| return productName; |
| } |
| |
| @Override |
| public int hashCode() { |
| // blazeExecRoot is derivable from other fields, but better safe than sorry. |
| return Objects.hash(serverDirectories, workspace, productName); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof BlazeDirectories)) { |
| return false; |
| } |
| BlazeDirectories that = (BlazeDirectories) obj; |
| return this.serverDirectories.equals(that.serverDirectories) |
| && this.workspace.equals(that.workspace) |
| && this.productName.equals(that.productName); |
| } |
| } |