aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar philwo <philwo@google.com>2018-04-24 09:12:03 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-04-24 09:13:17 -0700
commit59368559a1fc42a4e7a81f6892173ea086e34852 (patch)
treed63d41cd51c069db6501bac29a687f5a91bc4ee9 /src/main/java/com/google/devtools/build
parenta392ebafb8aedf85ace36e3a0565874d44139b5e (diff)
sandbox: Create base class for sandbox building strategies.
The CopyingSandboxedSpawn will be used by the upcoming Docker sandbox. This is otherwise a no-op change. RELNOTES: None. PiperOrigin-RevId: 194096413
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java149
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/CopyingSandboxedSpawn.java54
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawn.java109
3 files changed, 207 insertions, 105 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java
new file mode 100644
index 0000000000..6ee10f5b3e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java
@@ -0,0 +1,149 @@
+// Copyright 2018 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.base.Preconditions;
+import com.google.common.collect.Iterables;
+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 java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Implements the general flow of a sandboxed spawn that uses a container directory to build an
+ * execution root for a spawn.
+ */
+public abstract class AbstractContainerizingSandboxedSpawn implements SandboxedSpawn {
+ private final Path sandboxPath;
+ private final Path sandboxExecRoot;
+ private final List<String> arguments;
+ private final Map<String, String> environment;
+ private final Map<PathFragment, Path> inputs;
+ private final Collection<PathFragment> outputs;
+ private final Set<Path> writableDirs;
+
+ public AbstractContainerizingSandboxedSpawn(
+ Path sandboxPath,
+ Path sandboxExecRoot,
+ List<String> arguments,
+ Map<String, String> environment,
+ Map<PathFragment, Path> inputs,
+ Collection<PathFragment> outputs,
+ Set<Path> writableDirs) {
+ this.sandboxPath = sandboxPath;
+ this.sandboxExecRoot = sandboxExecRoot;
+ this.arguments = arguments;
+ this.environment = environment;
+ this.inputs = inputs;
+ this.outputs = outputs;
+ this.writableDirs = writableDirs;
+ }
+
+ @Override
+ public Path getSandboxExecRoot() {
+ return sandboxExecRoot;
+ }
+
+ @Override
+ public List<String> getArguments() {
+ return arguments;
+ }
+
+ @Override
+ public Map<String, String> getEnvironment() {
+ return environment;
+ }
+
+ @Override
+ public void createFileSystem() throws IOException {
+ createDirectories();
+ createInputs(inputs);
+ }
+
+ /**
+ * No input can be a child of another input, because otherwise we might try to create a symlink
+ * below another symlink we created earlier - which means we'd actually end up writing somewhere
+ * in the workspace.
+ *
+ * <p>If all inputs were regular files, this situation could naturally not happen - but
+ * unfortunately, we might get the occasional action that has directories in its inputs.
+ *
+ * <p>Creating all parent directories first ensures that we can safely create symlinks to
+ * directories, too, because we'll get an IOException with EEXIST if inputs happen to be nested
+ * once we start creating the symlinks for all inputs.
+ */
+ private void createDirectories() throws IOException {
+ LinkedHashSet<Path> dirsToCreate = new LinkedHashSet<>();
+
+ for (PathFragment path : Iterables.concat(inputs.keySet(), outputs)) {
+ Preconditions.checkArgument(!path.isAbsolute());
+ Preconditions.checkArgument(!path.containsUplevelReferences());
+ for (int i = 0; i < path.segmentCount(); i++) {
+ dirsToCreate.add(sandboxExecRoot.getRelative(path.subFragment(0, i)));
+ }
+ }
+
+ for (Path path : dirsToCreate) {
+ path.createDirectory();
+ }
+
+ for (Path dir : writableDirs) {
+ if (dir.startsWith(sandboxExecRoot)) {
+ dir.createDirectoryAndParents();
+ }
+ }
+ }
+
+ protected void createInputs(Map<PathFragment, Path> inputs) throws IOException {
+ // All input files are relative to the execroot.
+ for (Entry<PathFragment, Path> entry : inputs.entrySet()) {
+ Path key = sandboxExecRoot.getRelative(entry.getKey());
+ // A null value means that we're supposed to create an empty file as the input.
+ if (entry.getValue() != null) {
+ copyFile(entry.getValue(), key);
+ } else {
+ FileSystemUtils.createEmptyFile(key);
+ }
+ }
+ }
+
+ protected abstract void copyFile(Path source, Path target) throws IOException;
+
+ @Override
+ public void copyOutputs(Path execRoot) throws IOException {
+ SandboxedSpawn.moveOutputs(outputs, sandboxExecRoot, execRoot);
+ }
+
+ @Override
+ public void delete() {
+ try {
+ FileSystemUtils.deleteTree(sandboxPath);
+ } catch (IOException e) {
+ // This usually means that the Spawn itself exited, but still has children running that
+ // we couldn't wait for, which now block deletion of the sandbox directory. On Linux this
+ // should never happen, as we use PID namespaces and where they are not available the
+ // subreaper feature to make sure all children have been reliably killed before returning,
+ // but on other OS this might not always work. The SandboxModule will try to delete them
+ // again when the build is all done, at which point it hopefully works, so let's just go
+ // on here.
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/CopyingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/CopyingSandboxedSpawn.java
new file mode 100644
index 0000000000..6086515a35
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/CopyingSandboxedSpawn.java
@@ -0,0 +1,54 @@
+// Copyright 2018 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.devtools.build.lib.vfs.FileStatus;
+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;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Creates an execRoot for a Spawn that contains input files as copies from their original source.
+ */
+public class CopyingSandboxedSpawn extends AbstractContainerizingSandboxedSpawn {
+
+ public CopyingSandboxedSpawn(
+ Path sandboxPath,
+ Path sandboxExecRoot,
+ List<String> arguments,
+ Map<String, String> environment,
+ Map<PathFragment, Path> inputs,
+ Collection<PathFragment> outputs,
+ Set<Path> writableDirs) {
+ super(sandboxPath, sandboxExecRoot, arguments, environment, inputs, outputs, writableDirs);
+ }
+
+ @Override
+ protected void copyFile(Path source, Path target) throws IOException {
+ FileStatus stat = source.stat(Symlinks.NOFOLLOW);
+ if (stat.isSymbolicLink() || stat.isFile()) {
+ FileSystemUtils.copyFile(source, target);
+ } else if (stat.isDirectory()) {
+ target.createDirectory();
+ FileSystemUtils.copyTreesBelow(source, target, Symlinks.NOFOLLOW);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawn.java
index d2806db1a1..548d598dec 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawn.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawn.java
@@ -14,31 +14,19 @@
package com.google.devtools.build.lib.sandbox;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-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 java.io.IOException;
import java.util.Collection;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
/**
* Creates an execRoot for a Spawn that contains input files as symlinks to their original
* destination.
*/
-public class SymlinkedSandboxedSpawn implements SandboxedSpawn {
- private final Path sandboxPath;
- private final Path sandboxExecRoot;
- private final List<String> arguments;
- private final Map<String, String> environment;
- private final Map<PathFragment, Path> inputs;
- private final Collection<PathFragment> outputs;
- private final Set<Path> writableDirs;
+public class SymlinkedSandboxedSpawn extends AbstractContainerizingSandboxedSpawn {
public SymlinkedSandboxedSpawn(
Path sandboxPath,
@@ -48,100 +36,11 @@ public class SymlinkedSandboxedSpawn implements SandboxedSpawn {
Map<PathFragment, Path> inputs,
Collection<PathFragment> outputs,
Set<Path> writableDirs) {
- this.sandboxPath = sandboxPath;
- this.sandboxExecRoot = sandboxExecRoot;
- this.arguments = arguments;
- this.environment = environment;
- this.inputs = inputs;
- this.outputs = outputs;
- this.writableDirs = writableDirs;
+ super(sandboxPath, sandboxExecRoot, arguments, environment, inputs, outputs, writableDirs);
}
@Override
- public Path getSandboxExecRoot() {
- return sandboxExecRoot;
- }
-
- @Override
- public List<String> getArguments() {
- return arguments;
- }
-
- @Override
- public Map<String, String> getEnvironment() {
- return environment;
- }
-
- @Override
- public void createFileSystem() throws IOException {
- createDirectories();
- createInputs(inputs);
- }
-
- /**
- * No input can be a child of another input, because otherwise we might try to create a symlink
- * below another symlink we created earlier - which means we'd actually end up writing somewhere
- * in the workspace.
- *
- * <p>If all inputs were regular files, this situation could naturally not happen - but
- * unfortunately, we might get the occasional action that has directories in its inputs.
- *
- * <p>Creating all parent directories first ensures that we can safely create symlinks to
- * directories, too, because we'll get an IOException with EEXIST if inputs happen to be nested
- * once we start creating the symlinks for all inputs.
- */
- private void createDirectories() throws IOException {
- LinkedHashSet<Path> dirsToCreate = new LinkedHashSet<>();
-
- for (PathFragment path : Iterables.concat(inputs.keySet(), outputs)) {
- Preconditions.checkArgument(!path.isAbsolute());
- Preconditions.checkArgument(!path.containsUplevelReferences());
- for (int i = 0; i < path.segmentCount(); i++) {
- dirsToCreate.add(sandboxExecRoot.getRelative(path.subFragment(0, i)));
- }
- }
-
- for (Path path : dirsToCreate) {
- path.createDirectory();
- }
-
- for (Path dir : writableDirs) {
- if (dir.startsWith(sandboxExecRoot)) {
- dir.createDirectoryAndParents();
- }
- }
- }
-
- protected void createInputs(Map<PathFragment, Path> inputs) throws IOException {
- // All input files are relative to the execroot.
- for (Entry<PathFragment, Path> entry : inputs.entrySet()) {
- Path key = sandboxExecRoot.getRelative(entry.getKey());
- // A null value means that we're supposed to create an empty file as the input.
- if (entry.getValue() != null) {
- key.createSymbolicLink(entry.getValue());
- } else {
- FileSystemUtils.createEmptyFile(key);
- }
- }
- }
-
- @Override
- public void copyOutputs(Path execRoot) throws IOException {
- SandboxedSpawn.moveOutputs(outputs, sandboxExecRoot, execRoot);
- }
-
- @Override
- public void delete() {
- try {
- FileSystemUtils.deleteTree(sandboxPath);
- } catch (IOException e) {
- // This usually means that the Spawn itself exited, but still has children running that
- // we couldn't wait for, which now block deletion of the sandbox directory. On Linux this
- // should never happen, as we use PID namespaces and where they are not available the
- // subreaper feature to make sure all children have been reliably killed before returning,
- // but on other OS this might not always work. The SandboxModule will try to delete them
- // again when the build is all done, at which point it hopefully works, so let's just go
- // on here.
- }
+ protected void copyFile(Path source, Path target) throws IOException {
+ target.createSymbolicLink(source);
}
}