diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java new file mode 100644 index 0000000000..6d2477d67f --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java @@ -0,0 +1,191 @@ +// Copyright 2014 Google Inc. 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.config; + +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.Artifact; +import com.google.devtools.build.lib.actions.ArtifactFactory; +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 BlazeDirectories directories; + private final Path binDir; // the working bin directory under execRoot + private final ImmutableList<String> embeddedTools; + + private BinTools(BlazeDirectories directories, ImmutableList<String> tools) { + this.directories = directories; + this.binDir = directories.getExecRoot().getRelative("_bin"); + this.embeddedTools = tools; + } + + /** + * 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()); + } + + /** + * 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)); + } + + /** + * Populates the _bin directory by symlinking the necessary files from the given + * srcDir, and returns the corresponding BinTools. + */ + @VisibleForTesting + public static BinTools forIntegrationTesting( + BlazeDirectories directories, String srcDir, Iterable<String> tools) + throws IOException { + Path srcPath = directories.getOutputBase().getFileSystem().getPath(srcDir); + for (String embedded : tools) { + Path runfilesPath = srcPath.getRelative(embedded); + if (!runfilesPath.isFile()) { + // The file isn't there - nothing to symlink! + // + // Note: This path is usually taken by the tests using the in-memory + // file system. They can't run the embedded scripts anyhow, so there isn't + // much point in creating a symlink to a non-existent binary here. + continue; + } + Path outputPath = directories.getExecRoot().getChild("_bin").getChild(embedded); + if (outputPath.exists()) { + outputPath.delete(); + } + FileSystemUtils.createDirectoryAndParents(outputPath.getParentDirectory()); + outputPath.createSymbolicLink(runfilesPath); + } + + return new BinTools(directories, ImmutableList.copyOf(tools)); + } + + 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) { + Preconditions.checkState(embeddedTools.contains(embedPath), "%s not in %s", embedPath, + embeddedTools); + return new PathFragment("_bin").getRelative(new PathFragment(embedPath).getBaseName()); + } + + public Artifact getEmbeddedArtifact(String embedPath, ArtifactFactory artifactFactory) { + return artifactFactory.getDerivedArtifact(getExecPath(embedPath)); + } + + public ImmutableList<Artifact> getAllEmbeddedArtifacts(ArtifactFactory artifactFactory) { + ImmutableList.Builder<Artifact> builder = ImmutableList.builder(); + for (String embeddedTool : embeddedTools) { + builder.add(getEmbeddedArtifact(embeddedTool, artifactFactory)); + } + return builder.build(); + } + + /** + * Initializes the build tools not available at absolute paths. Note that + * these must be constant across all configurations. + */ + public void setupBuildTools() throws ExecException { + try { + FileSystemUtils.createDirectoryAndParents(binDir); + } 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 { + Path sourcePath = directories.getEmbeddedBinariesRoot().getRelative(embeddedPath); + Path linkPath = binDir.getRelative(new PathFragment(embeddedPath).getBaseName()); + linkTool(sourcePath, linkPath); + } + + private void linkTool(Path sourcePath, Path linkPath) throws ExecException { + if (linkPath.getFileSystem().supportsSymbolicLinks()) { + 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); + } + } + } +} |