aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/exec/BinTools.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/exec/BinTools.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/exec/BinTools.java197
1 files changed, 197 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/exec/BinTools.java b/src/main/java/com/google/devtools/build/lib/exec/BinTools.java
new file mode 100644
index 0000000000..e6ca7fb382
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/exec/BinTools.java
@@ -0,0 +1,197 @@
+// 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.exec;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.EnvironmentalExecException;
+import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.vfs.Dirent;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import java.io.IOException;
+
+/**
+ * Initializes the <execRoot>/_bin/ directory that contains auxiliary tools used during action
+ * execution (alarm, etc). The main purpose of this is to make sure that those tools are accessible
+ * using relative paths from the execution root.
+ */
+public final class BinTools {
+ private final Path embeddedBinariesRoot;
+ private final Path execrootParent;
+ private final ImmutableList<String> embeddedTools;
+
+ private Path binDir; // the working bin directory under execRoot
+
+ private BinTools(BlazeDirectories directories, ImmutableList<String> tools) {
+ this(
+ directories.getEmbeddedBinariesRoot(),
+ directories.getExecRoot().getParentDirectory(),
+ tools);
+ }
+
+ private BinTools(Path embeddedBinariesRoot, Path execrootParent, ImmutableList<String> tools) {
+ this.embeddedBinariesRoot = embeddedBinariesRoot;
+ this.execrootParent = execrootParent;
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ // Files under embedded_tools shouldn't be copied to under _bin dir
+ // They won't be used during action execution time.
+ for (String tool : tools) {
+ if (!tool.startsWith("embedded_tools/")) {
+ builder.add(tool);
+ }
+ }
+ this.embeddedTools = builder.build();
+ this.binDir = null;
+ }
+
+ /**
+ * Creates an instance with the list of embedded tools obtained from scanning the directory
+ * into which said binaries were extracted by the launcher.
+ */
+ public static BinTools forProduction(BlazeDirectories directories) throws IOException {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ scanDirectoryRecursively(builder, directories.getEmbeddedBinariesRoot(), "");
+ return new BinTools(directories, builder.build());
+ }
+
+ /**
+ * Creates an empty instance for testing.
+ */
+ @VisibleForTesting
+ public static BinTools empty(BlazeDirectories directories) {
+ return new BinTools(directories, ImmutableList.<String>of())
+ .setBinDir(directories.getWorkspace().getBaseName());
+ }
+
+ /**
+ * Creates an instance for testing without actually symlinking the tools.
+ *
+ * <p>Used for tests that need a set of embedded tools to be present, but not the actual files.
+ */
+ @VisibleForTesting
+ public static BinTools forUnitTesting(BlazeDirectories directories, Iterable<String> tools) {
+ return new BinTools(directories, ImmutableList.copyOf(tools))
+ .setBinDir(directories.getWorkspace().getBaseName());
+ }
+
+ /**
+ * Creates an instance for testing without actually symlinking the tools.
+ *
+ * <p>Used for tests that need a set of embedded tools to be present, but not the actual files.
+ */
+ @VisibleForTesting
+ public static BinTools forUnitTesting(Path execroot, Iterable<String> tools) {
+ return new BinTools(
+ execroot.getRelative("/fake/embedded/tools"),
+ execroot.getParentDirectory(),
+ ImmutableList.copyOf(tools)).setBinDir(execroot.getBaseName());
+ }
+
+ /**
+ * Returns a BinTools instance. Before calling this method, you have to populate the
+ * {@link BlazeDirectories#getEmbeddedBinariesRoot} directory.
+ */
+ @VisibleForTesting
+ public static BinTools forIntegrationTesting(
+ BlazeDirectories directories, Iterable<String> tools, String repositoryName) {
+ return new BinTools(directories, ImmutableList.copyOf(tools)).setBinDir(repositoryName);
+ }
+
+ private static void scanDirectoryRecursively(
+ ImmutableList.Builder<String> result, Path root, String relative) throws IOException {
+ for (Dirent dirent : root.readdir(Symlinks.NOFOLLOW)) {
+ String childRelative = relative.isEmpty()
+ ? dirent.getName()
+ : relative + "/" + dirent.getName();
+ switch (dirent.getType()) {
+ case FILE:
+ result.add(childRelative);
+ break;
+
+ case DIRECTORY:
+ scanDirectoryRecursively(result, root.getChild(dirent.getName()), childRelative);
+ break;
+
+ default:
+ // Nothing to do here -- we ignore symlinks, since they should not be present in the
+ // embedded binaries tree.
+ break;
+ }
+ }
+ }
+
+ public PathFragment getExecPath(String embedPath) {
+ if (!embeddedTools.contains(embedPath)) {
+ return null;
+ }
+ return PathFragment.create("_bin").getRelative(PathFragment.create(embedPath).getBaseName());
+ }
+
+ private BinTools setBinDir(String workspaceName) {
+ binDir = execrootParent.getRelative(workspaceName).getRelative("_bin");
+ return this;
+ }
+
+ /**
+ * Initializes the build tools not available at absolute paths. Note that
+ * these must be constant across all configurations.
+ */
+ public void setupBuildTools(String workspaceName) throws ExecException {
+ setBinDir(workspaceName);
+ try {
+ binDir.createDirectoryAndParents();
+ } catch (IOException e) {
+ throw new EnvironmentalExecException("could not create directory '" + binDir + "'", e);
+ }
+
+ for (String embeddedPath : embeddedTools) {
+ setupTool(embeddedPath);
+ }
+ }
+
+ private void setupTool(String embeddedPath) throws ExecException {
+ Preconditions.checkNotNull(binDir);
+ Path sourcePath = embeddedBinariesRoot.getRelative(embeddedPath);
+ Path linkPath = binDir.getRelative(PathFragment.create(embeddedPath).getBaseName());
+ linkTool(sourcePath, linkPath);
+ }
+
+ private void linkTool(Path sourcePath, Path linkPath) throws ExecException {
+ if (linkPath.getFileSystem().supportsSymbolicLinksNatively(linkPath)) {
+ try {
+ if (!linkPath.isSymbolicLink()) {
+ // ensureSymbolicLink() does not handle the case where there is already
+ // a file with the same name, so we need to handle it here.
+ linkPath.delete();
+ }
+ FileSystemUtils.ensureSymbolicLink(linkPath, sourcePath);
+ } catch (IOException e) {
+ throw new EnvironmentalExecException("failed to link '" + sourcePath + "'", e);
+ }
+ } else {
+ // For file systems that do not support linking, copy.
+ try {
+ FileSystemUtils.copyTool(sourcePath, linkPath);
+ } catch (IOException e) {
+ throw new EnvironmentalExecException("failed to copy '" + sourcePath + "'" , e);
+ }
+ }
+ }
+}