| // 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.hash.HashCode; |
| import com.google.devtools.build.lib.actions.Root; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.skyframe.serialization.PathCodec; |
| import com.google.devtools.build.lib.skyframe.serialization.SerializationException; |
| import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs; |
| import com.google.devtools.build.lib.util.StringCanonicalizer; |
| import com.google.devtools.build.lib.vfs.Path; |
| import com.google.protobuf.CodedInputStream; |
| import com.google.protobuf.CodedOutputStream; |
| import java.io.IOException; |
| import java.util.Objects; |
| |
| /** |
| * Encapsulates the directories related to a workspace. |
| * |
| * <p>The <code>workspace</code> is the top-level directory in the user's client (possibly |
| * read-only). The <code>execRoot</code> is the working directory for all spawned tools, which is |
| * generally below the <code>outputBase</code>. |
| * |
| * <p>Care must be taken to avoid multiple Bazel instances trying to write to the same output |
| * directory. At this time, this is enforced by requiring a 1:1 correspondence between a running |
| * Bazel instance and an output base directory, though this requirement may be softened in the |
| * future. |
| * |
| * <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 { |
| |
| // Include directory name, relative to execRoot/blaze-out/configuration. |
| public static final String RELATIVE_INCLUDE_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 all build actions. */ |
| private final Path execRoot; |
| |
| // These two are kept to avoid creating new objects every time they are accessed. This showed up |
| // in a profiler. |
| private final Path outputPath; |
| private final Path localOutputPath; |
| private final String productName; |
| |
| public BlazeDirectories( |
| ServerDirectories serverDirectories, |
| Path workspace, |
| String productName) { |
| this.serverDirectories = serverDirectories; |
| this.workspace = workspace; |
| this.productName = productName; |
| Path outputBase = serverDirectories.getOutputBase(); |
| Path execRootBase = outputBase.getChild("execroot"); |
| boolean useDefaultExecRootName = this.workspace == null || this.workspace.isRootDirectory(); |
| 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.execRoot = execRootBase.getChild(DEFAULT_EXEC_ROOT); |
| } else { |
| this.execRoot = execRootBase.getChild(workspace.getBaseName()); |
| } |
| String relativeOutputPath = getRelativeOutputPath(productName); |
| this.outputPath = execRoot.getRelative(getRelativeOutputPath()); |
| this.localOutputPath = outputBase.getRelative(relativeOutputPath); |
| } |
| |
| public ServerDirectories getServerDirectories() { |
| return serverDirectories; |
| } |
| |
| /** |
| * Returns the base of the output tree, which hosts all build and scratch |
| * output for a user and workspace. |
| */ |
| public Path getInstallBase() { |
| return serverDirectories.getInstallBase(); |
| } |
| |
| /** |
| * Returns the workspace directory, which is also the working dir of the server. |
| */ |
| public Path getWorkspace() { |
| return workspace; |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| /** |
| * Returns the execution root for the main package. This is created before the workspace file |
| * has been read, so it has an incorrect path. Use {@link #getExecRoot(String)} instead. |
| */ |
| @Deprecated |
| public Path getExecRoot() { |
| return execRoot; |
| } |
| |
| /** |
| * 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 execRoot.getParentDirectory().getRelative(workspaceName); |
| } |
| |
| /** |
| * Returns the output path for the main repository using the workspace's directory name. Use |
| * {@link #getOutputPath(String)}, instead. |
| */ |
| @Deprecated |
| public Path getOutputPath() { |
| return outputPath; |
| } |
| |
| /** |
| * 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 the stdout/stderr for actions can be stored temporarily for a |
| * build. If the directory already exists, the directory is cleaned. |
| */ |
| public Path getActionConsoleOutputDirectory(Path execRoot) { |
| return execRoot.getRelative(getRelativeOutputPath()).getRelative("_tmp/action_outs"); |
| } |
| |
| /** |
| * 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 Root getBuildDataDirectory(String workspaceName) { |
| return Root |
| .asDerivedRoot(getExecRoot(workspaceName), getOutputPath(workspaceName), true); |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| public String getRelativeOutputPath() { |
| return BlazeDirectories.getRelativeOutputPath(productName); |
| } |
| |
| public String getProductName() { |
| return productName; |
| } |
| |
| /** |
| * Returns the output directory name, relative to the execRoot. |
| * TODO(bazel-team): (2011) make this private? |
| */ |
| public static String getRelativeOutputPath(String productName) { |
| return StringCanonicalizer.intern(productName + "-out"); |
| } |
| |
| @Override |
| public int hashCode() { |
| // execRoot is derivable from other fields, but better safe than sorry. |
| return Objects.hash(serverDirectories, workspace, productName, execRoot); |
| } |
| |
| @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) |
| // execRoot is derivable from other fields, but better safe than sorry. |
| && this.execRoot.equals(that.execRoot); |
| } |
| |
| void serialize(CodedOutputStream codedOut, PathCodec pathCodec) |
| throws IOException, SerializationException { |
| serverDirectories.serialize(codedOut, pathCodec); |
| pathCodec.serialize(workspace, codedOut); |
| StringCodecs.asciiOptimized().serialize(productName, codedOut); |
| } |
| |
| static BlazeDirectories deserialize(CodedInputStream codedIn, PathCodec pathCodec) |
| throws IOException, SerializationException { |
| return new BlazeDirectories( |
| ServerDirectories.deserialize(codedIn, pathCodec), |
| pathCodec.deserialize(codedIn), |
| StringCodecs.asciiOptimized().deserialize(codedIn)); |
| } |
| } |