blob: c2e9721b2c2d4bc7750aa27adecf438af1d2332d [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.ArtifactRoot;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.devtools.build.lib.vfs.Path;
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!
*/
@AutoCodec
@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 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 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;
@AutoCodec.Instantiator
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();
Path execRootBase = outputBase.getChild("execroot");
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.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 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();
}
/**
* 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 ArtifactRoot getBuildDataDirectory(String workspaceName) {
return ArtifactRoot.asDerivedRoot(getExecRoot(workspaceName), getOutputPath(workspaceName));
}
/**
* 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);
}
}