aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java
new file mode 100644
index 0000000000..34ddd9e782
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SpawnHelpers.java
@@ -0,0 +1,198 @@
+// Copyright 2016 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.sandbox;
+
+import com.google.common.io.Files;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.analysis.AnalysisUtils;
+import com.google.devtools.build.lib.rules.cpp.CppCompileAction;
+import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
+import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Contains common helper methods that extract information from {@link Spawn} objects. */
+public final class SpawnHelpers {
+
+ private final Path execRoot;
+
+ public SpawnHelpers(Path execRoot) {
+ this.execRoot = execRoot;
+ }
+
+ /**
+ * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
+ * host filesystem where the input files can be found.
+ */
+ public Map<PathFragment, Path> getMounts(Spawn spawn, ActionExecutionContext executionContext)
+ throws IOException {
+ Map<PathFragment, Path> mounts = new HashMap<>();
+ mountRunfilesFromManifests(mounts, spawn);
+ mountRunfilesFromSuppliers(mounts, spawn);
+ mountFilesFromFilesetManifests(mounts, spawn, executionContext);
+ mountInputs(mounts, spawn, executionContext);
+ return mounts;
+ }
+
+ /** Mount all runfiles that the spawn needs as specified in its runfiles manifests. */
+ void mountRunfilesFromManifests(Map<PathFragment, Path> mounts, Spawn spawn) throws IOException {
+ for (Map.Entry<PathFragment, Artifact> manifest : spawn.getRunfilesManifests().entrySet()) {
+ String manifestFilePath = manifest.getValue().getPath().getPathString();
+ Preconditions.checkState(!manifest.getKey().isAbsolute());
+ PathFragment targetDirectory = manifest.getKey();
+
+ parseManifestFile(
+ execRoot.getFileSystem(), mounts, targetDirectory, new File(manifestFilePath), false, "");
+ }
+ }
+
+ /** Mount all files that the spawn needs as specified in its fileset manifests. */
+ void mountFilesFromFilesetManifests(
+ Map<PathFragment, Path> mounts, Spawn spawn, ActionExecutionContext executionContext)
+ throws IOException {
+ final FilesetActionContext filesetContext =
+ executionContext.getExecutor().getContext(FilesetActionContext.class);
+ for (Artifact fileset : spawn.getFilesetManifests()) {
+ File manifestFile =
+ new File(
+ execRoot.getPathString(),
+ AnalysisUtils.getManifestPathFromFilesetPath(fileset.getExecPath()).getPathString());
+ PathFragment targetDirectory = fileset.getExecPath();
+
+ parseManifestFile(
+ execRoot.getFileSystem(),
+ mounts,
+ targetDirectory,
+ manifestFile,
+ true,
+ filesetContext.getWorkspaceName());
+ }
+ }
+
+ /** A parser for the MANIFEST files used by Filesets and runfiles. */
+ static void parseManifestFile(
+ FileSystem fs,
+ Map<PathFragment, Path> mounts,
+ PathFragment targetDirectory,
+ File manifestFile,
+ boolean isFilesetManifest,
+ String workspaceName)
+ throws IOException {
+ int lineNum = 0;
+ for (String line : Files.readLines(manifestFile, StandardCharsets.UTF_8)) {
+ if (isFilesetManifest && (++lineNum % 2 == 0)) {
+ continue;
+ }
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ String[] fields = line.trim().split(" ");
+
+ // The "target" field is always a relative path that is to be interpreted in this way:
+ // (1) If this is a fileset manifest and our workspace name is not empty, the first segment
+ // of each "target" path must be the workspace name, which is then stripped before further
+ // processing.
+ // (2) The "target" path is then appended to the "targetDirectory", which is a path relative
+ // to the execRoot. Together, this results in the full path in the execRoot in which place a
+ // symlink referring to "source" has to be created (see below).
+ PathFragment targetPath;
+ if (isFilesetManifest) {
+ PathFragment targetPathFragment = new PathFragment(fields[0]);
+ if (!workspaceName.isEmpty()) {
+ Preconditions.checkState(
+ targetPathFragment.getSegment(0).equals(workspaceName),
+ "Fileset manifest line must start with workspace name");
+ targetPathFragment = targetPathFragment.subFragment(1, targetPathFragment.segmentCount());
+ }
+ targetPath = targetDirectory.getRelative(targetPathFragment);
+ } else {
+ targetPath = targetDirectory.getRelative(fields[0]);
+ }
+
+ // The "source" field, if it exists, is always an absolute path and may point to any file in
+ // the filesystem (it is not limited to files in the workspace or execroot).
+ Path source;
+ switch (fields.length) {
+ case 1:
+ source = fs.getPath("/dev/null");
+ break;
+ case 2:
+ source = fs.getPath(fields[1]);
+ break;
+ default:
+ throw new IllegalStateException("'" + line + "' splits into more than 2 parts");
+ }
+
+ mounts.put(targetPath, source);
+ }
+ }
+
+ /** Mount all runfiles that the spawn needs as specified via its runfiles suppliers. */
+ void mountRunfilesFromSuppliers(Map<PathFragment, Path> mounts, Spawn spawn) throws IOException {
+ Map<PathFragment, Map<PathFragment, Artifact>> rootsAndMappings =
+ spawn.getRunfilesSupplier().getMappings();
+ for (Map.Entry<PathFragment, Map<PathFragment, Artifact>> rootAndMappings :
+ rootsAndMappings.entrySet()) {
+ PathFragment root = rootAndMappings.getKey();
+ if (root.isAbsolute()) {
+ root = root.relativeTo(execRoot.asFragment());
+ }
+ for (Map.Entry<PathFragment, Artifact> mapping : rootAndMappings.getValue().entrySet()) {
+ Artifact sourceArtifact = mapping.getValue();
+ PathFragment source =
+ (sourceArtifact != null) ? sourceArtifact.getExecPath() : new PathFragment("/dev/null");
+
+ Preconditions.checkArgument(!mapping.getKey().isAbsolute());
+ PathFragment target = root.getRelative(mapping.getKey());
+ mounts.put(target, execRoot.getRelative(source));
+ }
+ }
+ }
+
+ /** Mount all inputs of the spawn. */
+ void mountInputs(
+ Map<PathFragment, Path> mounts, Spawn spawn, ActionExecutionContext actionExecutionContext) {
+ List<ActionInput> inputs =
+ ActionInputHelper.expandArtifacts(
+ spawn.getInputFiles(), actionExecutionContext.getArtifactExpander());
+
+ if (spawn.getResourceOwner() instanceof CppCompileAction) {
+ CppCompileAction action = (CppCompileAction) spawn.getResourceOwner();
+ if (action.shouldScanIncludes()) {
+ inputs.addAll(action.getAdditionalInputs());
+ }
+ }
+
+ for (ActionInput input : inputs) {
+ if (input.getExecPathString().contains("internal/_middlemen/")) {
+ continue;
+ }
+ PathFragment mount = new PathFragment(input.getExecPathString());
+ mounts.put(mount, execRoot.getRelative(mount));
+ }
+ }
+}