// 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. * *

The workspace is the top-level directory in the user's client (possibly * read-only). The execRoot is the working directory for all spawned tools, which is * generally below the outputBase. * *

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. * *

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. * *

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); } }