blob: 3c903976839e18bd314510f28acf37861641ad86 [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.base.Ascii;
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;
import javax.annotation.Nullable;
/**
* 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. Only one segment allowed.
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 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;
@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();
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 = serverDirectories.getExecRootBase().getChild(DEFAULT_EXEC_ROOT);
} else {
this.blazeExecRoot = serverDirectories.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 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();
}
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 serverDirectories.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), 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();
}
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() {
// 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);
}
}