// 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.util.StringCanonicalizer; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; /** * 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, boolean deepExecRoot, String productName) { this.serverDirectories = serverDirectories; this.workspace = workspace; this.productName = productName; Path outputBase = serverDirectories.getOutputBase(); Path execRootBase = deepExecRoot ? outputBase.getChild("execroot") : outputBase; 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); } @VisibleForTesting public BlazeDirectories(ServerDirectories serverDirectories, Path workspace, String productName) { this(serverDirectories, workspace, false, productName); } @VisibleForTesting public BlazeDirectories(Path installBase, Path outputBase, Path workspace, String productName) { this(new ServerDirectories(installBase, outputBase), workspace, false, productName); } /** * Returns the Filesystem that all of our directories belong to. Handy for * resolving absolute paths. */ public FileSystem getFileSystem() { return serverDirectories.getFileSystem(); } 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); } /** * 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"); } }