blob: ac4a005e0a0e3065eed2d4b4b611af1a283f7f36 [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.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));
}
}