From 5e60e3868b4bafacc138f786b304589dbd687016 Mon Sep 17 00:00:00 2001 From: Yue Gan Date: Mon, 20 Mar 2017 10:58:11 +0000 Subject: [PATCH] Rollback of commit 59180a4ea66b1395b5b85defd732859ecae919ea. *** Reason for rollback *** Break bazel-tests and many other jobs on CI. http://ci.bazel.io/job/bazel-tests/BAZEL_VERSION=HEAD,PLATFORM_NAME=linux-x86_64/651/console *** Original change description *** Add SpawnInputExpander helper class to arrange runfiles for spawn strategies This new class is a combination of SpawnHelper and our internal code; the plan is to migrate all spawn strategies to the new class. The strict flag should be enabled by default, but that's a breaking change, so we need to do it later. - Use it in SandboxStrategy. - Add ActionInput.getExecPath to return a PathFragment; this avoids lots of back and forth between path fragments and strings. This is a step towards #159... *** -- PiperOrigin-RevId: 150610616 MOS_MIGRATED_REVID=150610616 --- .../build/lib/actions/ActionInput.java | 13 +- .../build/lib/actions/ActionInputHelper.java | 6 - .../devtools/build/lib/actions/Artifact.java | 1 - .../devtools/build/lib/analysis/Runfiles.java | 24 +- .../build/lib/exec/SpawnInputExpander.java | 205 --------------- .../build/lib/sandbox/SandboxStrategy.java | 39 +-- .../devtools/build/lib/exec/DigestTest.java | 8 +- .../lib/exec/SpawnInputExpanderTest.java | 242 ------------------ 8 files changed, 17 insertions(+), 521 deletions(-) delete mode 100644 src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java delete mode 100644 src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java b/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java index 90d4c5ff004d1d..a21ed8519f58ee 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java +++ b/src/main/java/com/google/devtools/build/lib/actions/ActionInput.java @@ -14,8 +14,6 @@ package com.google.devtools.build.lib.actions; -import com.google.devtools.build.lib.vfs.PathFragment; - /** * Represents an input file to a build action, with an appropriate relative path and digest * value. @@ -34,13 +32,6 @@ */ public interface ActionInput { - /** - * @return the relative path to the input file. - */ - String getExecPathString(); - - /** - * @return the relative path to the input file. - */ - PathFragment getExecPath(); + /** @return the relative path to the input file. */ + public String getExecPathString(); } diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java b/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java index c63fd5b93a65c4..4ca4a1b162396c 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java +++ b/src/main/java/com/google/devtools/build/lib/actions/ActionInputHelper.java @@ -69,7 +69,6 @@ public void expand(Artifact mm, Collection output) { */ private static class BasicActionInput implements ActionInput { private final String path; - public BasicActionInput(String path) { this.path = Preconditions.checkNotNull(path); } @@ -79,11 +78,6 @@ public String getExecPathString() { return path; } - @Override - public PathFragment getExecPath() { - return new PathFragment(path); - } - @Override public int hashCode() { return path.hashCode(); diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java index bd54b0f8e7dec2..4b7adbc40622a2 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java +++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java @@ -344,7 +344,6 @@ public final Root getRoot() { return root; } - @Override public final PathFragment getExecPath() { return execPath; } diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java index 04eaf1287bdba2..a087ee4ff837e2 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/Runfiles.java @@ -134,8 +134,8 @@ private static final class SymlinkEntry implements SkylarkValue { private final Artifact artifact; private SymlinkEntry(PathFragment path, Artifact artifact) { - this.path = Preconditions.checkNotNull(path); - this.artifact = Preconditions.checkNotNull(artifact); + this.path = path; + this.artifact = artifact; } public PathFragment getPath() { @@ -146,12 +146,10 @@ public Artifact getArtifact() { return artifact; } - @Override public boolean isImmutable() { return true; } - @Override public void write(Appendable buffer, char quotationMark) { Printer.append(buffer, "SymlinkEntry(path = "); Printer.write(buffer, getPath().toString(), quotationMark); @@ -433,11 +431,11 @@ static Map filterListForObscuringSymlinks( * Returns the symlinks as a map from PathFragment to Artifact. * * @param eventHandler Used for throwing an error if we have an obscuring runlink within the - * normal source tree entries, or runfile conflicts. May be null, in which case obscuring - * symlinks are silently discarded, and conflicts are overwritten. + * normal source tree entries, or runfile conflicts. May be null, in which case obscuring + * symlinks are silently discarded, and conflicts are overwritten. * @param location Location for eventHandler warnings. Ignored if eventHandler is null. * @return Map path fragment to artifact, of normal source tree entries - * and elements that live outside the source tree. Null values represent empty input files. + * and elements that live outside the source tree. Null values represent empty input files. */ public Map getRunfilesInputs(EventHandler eventHandler, Location location) throws IOException { @@ -852,13 +850,13 @@ public Builder addTransitiveArtifacts(NestedSet artifacts) { * Adds a symlink. */ public Builder addSymlink(PathFragment link, Artifact target) { + Preconditions.checkNotNull(link); + Preconditions.checkNotNull(target); symlinksBuilder.add(new SymlinkEntry(link, target)); return this; } - /** - * Adds several symlinks. Neither keys nor values may be null. - */ + /** Adds several symlinks. */ public Builder addSymlinks(Map symlinks) { for (Map.Entry symlink : symlinks.entrySet()) { symlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue())); @@ -878,13 +876,13 @@ public Builder addSymlinks(NestedSet symlinks) { * Adds a root symlink. */ public Builder addRootSymlink(PathFragment link, Artifact target) { + Preconditions.checkNotNull(link); + Preconditions.checkNotNull(target); rootSymlinksBuilder.add(new SymlinkEntry(link, target)); return this; } - /** - * Adds several root symlinks. Neither keys nor values may be null. - */ + /** Adds several root symlinks. */ public Builder addRootSymlinks(Map symlinks) { for (Map.Entry symlink : symlinks.entrySet()) { rootSymlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue())); diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java deleted file mode 100644 index 4c45aa962515db..00000000000000 --- a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2017 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 static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.io.LineProcessor; -import com.google.devtools.build.lib.actions.ActionInput; -import com.google.devtools.build.lib.actions.ActionInputFileCache; -import com.google.devtools.build.lib.actions.ActionInputHelper; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; -import com.google.devtools.build.lib.actions.RunfilesSupplier; -import com.google.devtools.build.lib.actions.Spawn; -import com.google.devtools.build.lib.analysis.AnalysisUtils; -import com.google.devtools.build.lib.rules.fileset.FilesetActionContext; -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.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * A helper class for spawn strategies to turn runfiles suppliers into input mappings. This class - * performs no I/O operations, but only rearranges the files according to how the runfiles should be - * laid out. - */ -public class SpawnInputExpander { - public static final ActionInput EMPTY_FILE = ActionInputHelper.fromPath("/dev/null"); - - private final boolean strict; - - /** - * Creates a new instance. If strict is true, then the expander checks for directories in runfiles - * and throws an exception if it finds any. Otherwise it silently ignores directories in runfiles - * and adds a mapping for them. At this time, directories in filesets are always silently added - * as mappings. - * - *

Directories in inputs are a correctness issue: Bazel only tracks dependencies at the action - * level, and it does not track dependencies on directories. Making a directory available to a - * spawn even though it's contents are not tracked as dependencies leads to incorrect incremental - * builds, since changes to the contents do not trigger action invalidation. - * - *

As such, all spawn strategies should always be strict and not make directories available to - * the subprocess. However, that's a breaking change, and therefore we make it depend on this flag - * for now. - */ - public SpawnInputExpander(boolean strict) { - this.strict = strict; - } - - private void addMapping( - Map inputMappings, - PathFragment targetLocation, - ActionInput input) { - if (!inputMappings.containsKey(targetLocation)) { - inputMappings.put(targetLocation, input); - } - } - - /** Adds runfiles inputs from runfilesSupplier to inputMappings. */ - @VisibleForTesting - void addRunfilesToInputs( - Map inputMap, - RunfilesSupplier runfilesSupplier, - ActionInputFileCache actionFileCache) throws IOException { - Map> rootsAndMappings = null; - rootsAndMappings = runfilesSupplier.getMappings(); - - for (Entry> rootAndMappings : - rootsAndMappings.entrySet()) { - PathFragment root = rootAndMappings.getKey(); - for (Entry mapping : rootAndMappings.getValue().entrySet()) { - PathFragment targetPrefix = root.getRelative(mapping.getKey()); - Artifact localArtifact = mapping.getValue(); - if (localArtifact != null) { - if (strict && !actionFileCache.isFile(localArtifact)) { - throw new IOException("Not a file: " + localArtifact.getPath().getPathString()); - } - addMapping(inputMap, targetPrefix, localArtifact); - } else { - addMapping(inputMap, targetPrefix, EMPTY_FILE); - } - } - } - } - - /** - * Parses the fileset manifest file, adding to the inputMappings where - * appropriate. Lines referring to directories are recursed. - */ - @VisibleForTesting - void parseFilesetManifest( - Map inputMappings, Artifact manifest, String workspaceName) - throws IOException { - Path file = manifest.getRoot().getPath().getRelative( - AnalysisUtils.getManifestPathFromFilesetPath(manifest.getExecPath()).getPathString()); - FileSystemUtils.asByteSource(file).asCharSource(UTF_8) - .readLines(new ManifestLineProcessor(inputMappings, workspaceName, manifest.getExecPath())); - } - - private final class ManifestLineProcessor implements LineProcessor { - private final Map inputMappings; - private final String workspaceName; - private final PathFragment targetPrefix; - private int lineNum = 0; - - ManifestLineProcessor( - Map inputMappings, - String workspaceName, - PathFragment targetPrefix) { - this.inputMappings = inputMappings; - this.workspaceName = workspaceName; - this.targetPrefix = targetPrefix; - } - - @Override - public boolean processLine(String line) throws IOException { - if (++lineNum % 2 == 0) { - // Digest line, skip. - return true; - } - if (line.isEmpty()) { - return true; - } - - ActionInput artifact; - PathFragment location; - int pos = line.indexOf(' '); - if (pos == -1) { - location = new PathFragment(line); - artifact = EMPTY_FILE; - } else { - String targetPath = line.substring(pos + 1); - if (targetPath.charAt(0) != '/') { - throw new IOException(String.format("runfiles target is not absolute: %s", targetPath)); - } - artifact = targetPath.isEmpty() ? EMPTY_FILE : ActionInputHelper.fromPath(targetPath); - - location = new PathFragment(line.substring(0, pos)); - if (!workspaceName.isEmpty()) { - if (!location.getSegment(0).equals(workspaceName)) { - throw new IOException( - String.format( - "fileset manifest line must start with '%s': '%s'", workspaceName, location)); - } else { - // Erase "/". - location = location.subFragment(1, location.segmentCount()); - } - } - } - - addMapping(inputMappings, targetPrefix.getRelative(location), artifact); - return true; - } - - @Override - public Object getResult() { - return null; // Unused. - } - } - - private void addInputs( - Map inputMap, Spawn spawn, ArtifactExpander artifactExpander) { - List inputs = - ActionInputHelper.expandArtifacts(spawn.getInputFiles(), artifactExpander); - for (ActionInput input : inputs) { - inputMap.put(input.getExecPath(), input); - } - } - - /** - * Convert the inputs of the given spawn to a map from exec-root relative paths to action inputs. - * In some cases, this generates empty files, for which it uses {@link #EMPTY_FILE}. - */ - public SortedMap getInputMapping( - Spawn spawn, ArtifactExpander artifactExpander, ActionInputFileCache actionInputFileCache, - FilesetActionContext filesetContext) - throws IOException { - TreeMap inputMap = new TreeMap<>(); - addInputs(inputMap, spawn, artifactExpander); - addRunfilesToInputs( - inputMap, spawn.getRunfilesSupplier(), actionInputFileCache); - for (Artifact manifest : spawn.getFilesetManifests()) { - parseFilesetManifest(inputMap, manifest, filesetContext.getWorkspaceName()); - } - return inputMap; - } -} diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java index a8da48e4f5dd84..274377831b3d2d 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxStrategy.java @@ -17,8 +17,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import com.google.devtools.build.lib.actions.ActionExecutionContext; -import com.google.devtools.build.lib.actions.ActionInput; -import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.EnvironmentalExecException; import com.google.devtools.build.lib.actions.ExecException; import com.google.devtools.build.lib.actions.SandboxedSpawnActionContext; @@ -30,17 +28,12 @@ import com.google.devtools.build.lib.buildtool.BuildRequest; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; -import com.google.devtools.build.lib.exec.SpawnInputExpander; -import com.google.devtools.build.lib.rules.fileset.FilesetActionContext; import com.google.devtools.build.lib.util.io.OutErr; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; import java.util.concurrent.atomic.AtomicReference; /** Abstract common ancestor for sandbox strategies implementing the common parts. */ @@ -51,7 +44,7 @@ abstract class SandboxStrategy implements SandboxedSpawnActionContext { private final Path execRoot; private final boolean verboseFailures; private final SandboxOptions sandboxOptions; - private final SpawnInputExpander spawnInputExpander; + private final SpawnHelpers spawnHelpers; public SandboxStrategy( BuildRequest buildRequest, @@ -63,7 +56,7 @@ public SandboxStrategy( this.execRoot = blazeDirs.getExecRoot(); this.verboseFailures = verboseFailures; this.sandboxOptions = sandboxOptions; - this.spawnInputExpander = new SpawnInputExpander(/*strict=*/false); + this.spawnHelpers = new SpawnHelpers(blazeDirs.getExecRoot()); } protected void runSpawn( @@ -152,33 +145,7 @@ public boolean shouldPropagateExecException() { public Map getMounts(Spawn spawn, ActionExecutionContext executionContext) throws ExecException { try { - Map inputMap = spawnInputExpander - .getInputMapping( - spawn, - executionContext.getArtifactExpander(), - executionContext.getActionInputFileCache(), - executionContext.getExecutor().getContext(FilesetActionContext.class)); - Map mounts = new TreeMap<>(); - for (Map.Entry e : inputMap.entrySet()) { - mounts.put(e.getKey(), execRoot.getRelative(e.getValue().getExecPath())); - } - - // ActionInputHelper#expandArtifacts above expands empty TreeArtifacts into an empty list. - // However, actions that accept TreeArtifacts as inputs generally expect that the empty - // directory is created. So here we explicitly mount the directories of the TreeArtifacts as - // inputs. - for (ActionInput input : spawn.getInputFiles()) { - if (input instanceof Artifact && ((Artifact) input).isTreeArtifact()) { - List containedArtifacts = new ArrayList<>(); - executionContext.getArtifactExpander().expand((Artifact) input, containedArtifacts); - // Attempting to mount a non-empty directory results in ERR_DIRECTORY_NOT_EMPTY, so we - // only mount empty TreeArtifacts as directories. - if (containedArtifacts.isEmpty()) { - inputMap.put(input.getExecPath(), input); - } - } - } - return mounts; + return spawnHelpers.getMounts(spawn, executionContext); } catch (IOException e) { throw new EnvironmentalExecException("Could not prepare mounts for sandbox execution", e); } diff --git a/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java b/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java index 27edca06d2da4f..b6106411442a51 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/DigestTest.java @@ -23,7 +23,6 @@ import com.google.devtools.build.lib.testutil.Suite; import com.google.devtools.build.lib.testutil.TestSpec; import com.google.devtools.build.lib.util.Pair; -import com.google.devtools.build.lib.vfs.PathFragment; import com.google.protobuf.ByteString; import java.io.IOException; import java.io.OutputStream; @@ -62,7 +61,7 @@ public void testFromString() { "8b1a9953c4611296a827abf8c47804d7", Digest.fromContent("Hello".getBytes(UTF_8)).toStringUtf8()); - assertEquals(UGLY_DIGEST, Digest.fromContent(UGLY.getBytes(UTF_8)).toStringUtf8()); + assertEquals(UGLY_DIGEST, Digest.fromContent(UGLY.getBytes()).toStringUtf8()); // ByteBuffer digest not idempotent because ByteBuffer manages a "position" internally. ByteBuffer buffer = ByteBuffer.wrap(UGLY.getBytes(UTF_8)); @@ -107,11 +106,6 @@ public void writeTo(OutputStream out) throws IOException { public String getExecPathString() { throw new UnsupportedOperationException(); } - - @Override - public PathFragment getExecPath() { - throw new UnsupportedOperationException(); - } }); assertEquals(UGLY_DIGEST, result.first.toStringUtf8()); assertEquals(UGLY.length(), result.second.longValue()); diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java deleted file mode 100644 index fdfa65537178ef..00000000000000 --- a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2017 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 static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; - -import com.google.common.collect.Maps; -import com.google.devtools.build.lib.actions.ActionInput; -import com.google.devtools.build.lib.actions.ActionInputFileCache; -import com.google.devtools.build.lib.actions.ActionInputHelper; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.EmptyRunfilesSupplier; -import com.google.devtools.build.lib.actions.Root; -import com.google.devtools.build.lib.actions.RunfilesSupplier; -import com.google.devtools.build.lib.analysis.Runfiles; -import com.google.devtools.build.lib.analysis.RunfilesSupplierImpl; -import com.google.devtools.build.lib.vfs.FileSystem; -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.inmemoryfs.InMemoryFileSystem; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mockito; - -/** - * Tests for {@link SpawnInputExpander}. - */ -@RunWith(JUnit4.class) -public class SpawnInputExpanderTest { - private FileSystem fs; - private SpawnInputExpander expander; - private Map inputMappings; - - @Before - public final void createSpawnInputExpander() throws Exception { - fs = new InMemoryFileSystem(); - expander = new SpawnInputExpander(/*strict=*/true); - inputMappings = Maps.newHashMap(); - } - - private void scratchFile(String file, String... lines) throws Exception { - Path path = fs.getPath(file); - FileSystemUtils.createDirectoryAndParents(path.getParentDirectory()); - FileSystemUtils.writeLinesAs(path, StandardCharsets.UTF_8, lines); - } - - @Test - public void testEmptyRunfiles() throws Exception { - RunfilesSupplier supplier = EmptyRunfilesSupplier.INSTANCE; - expander.addRunfilesToInputs(inputMappings, supplier, null); - assertThat(inputMappings).isEmpty(); - } - - @Test - public void testRunfilesSingleFile() throws Exception { - Artifact artifact = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact)).thenReturn(true); - - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - assertThat(inputMappings).hasSize(1); - assertThat(inputMappings) - .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact); - } - - @Test - public void testRunfilesDirectoryStrict() throws Exception { - Artifact artifact = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact)).thenReturn(false); - - try { - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - fail(); - } catch (IOException expected) { - assertThat(expected.getMessage().contains("Not a file: /root/dir/file")).isTrue(); - } - } - - @Test - public void testRunfilesDirectoryNonStrict() throws Exception { - Artifact artifact = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact)).thenReturn(false); - - expander = new SpawnInputExpander(/*strict=*/false); - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - assertThat(inputMappings).hasSize(1); - assertThat(inputMappings) - .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact); - } - - @Test - public void testRunfilesTwoFiles() throws Exception { - Artifact artifact1 = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Artifact artifact2 = - new Artifact(fs.getPath("/root/dir/baz"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace") - .addArtifact(artifact1) - .addArtifact(artifact2) - .build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact1)).thenReturn(true); - Mockito.when(mockCache.isFile(artifact2)).thenReturn(true); - - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - assertThat(inputMappings).hasSize(2); - assertThat(inputMappings) - .containsEntry(new PathFragment("runfiles/workspace/dir/file"), artifact1); - assertThat(inputMappings) - .containsEntry(new PathFragment("runfiles/workspace/dir/baz"), artifact2); - } - - @Test - public void testRunfilesSymlink() throws Exception { - Artifact artifact = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace") - .addSymlink(new PathFragment("symlink"), artifact).build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact)).thenReturn(true); - - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - assertThat(inputMappings).hasSize(1); - assertThat(inputMappings) - .containsEntry(new PathFragment("runfiles/workspace/symlink"), artifact); - } - - @Test - public void testRunfilesRootSymlink() throws Exception { - Artifact artifact = - new Artifact(fs.getPath("/root/dir/file"), Root.asSourceRoot(fs.getPath("/root"))); - Runfiles runfiles = new Runfiles.Builder("workspace") - .addRootSymlink(new PathFragment("symlink"), artifact).build(); - RunfilesSupplier supplier = new RunfilesSupplierImpl(new PathFragment("runfiles"), runfiles); - ActionInputFileCache mockCache = Mockito.mock(ActionInputFileCache.class); - Mockito.when(mockCache.isFile(artifact)).thenReturn(true); - - expander.addRunfilesToInputs(inputMappings, supplier, mockCache); - assertThat(inputMappings).hasSize(2); - assertThat(inputMappings).containsEntry(new PathFragment("runfiles/symlink"), artifact); - // If there's no other entry, Runfiles adds an empty file in the workspace to make sure the - // directory gets created. - assertThat(inputMappings) - .containsEntry( - new PathFragment("runfiles/workspace/.runfile"), SpawnInputExpander.EMPTY_FILE); - } - - @Test - public void testEmptyManifest() throws Exception { - // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST". - scratchFile("/root/_foo/MANIFEST"); - - Artifact artifact = - new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root"))); - expander.parseFilesetManifest(inputMappings, artifact, "workspace"); - assertThat(inputMappings).isEmpty(); - } - - @Test - public void testManifestWithSingleFile() throws Exception { - // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST". - scratchFile( - "/root/_foo/MANIFEST", - "workspace/bar /dir/file", - ""); - - Artifact artifact = - new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root"))); - expander.parseFilesetManifest(inputMappings, artifact, "workspace"); - assertThat(inputMappings).hasSize(1); - assertThat(inputMappings) - .containsEntry(new PathFragment("foo/bar"), ActionInputHelper.fromPath("/dir/file")); - } - - @Test - public void testManifestWithTwoFiles() throws Exception { - // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST". - scratchFile( - "/root/_foo/MANIFEST", - "workspace/bar /dir/file", - "", - "workspace/baz /dir/file", - ""); - - Artifact artifact = - new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root"))); - expander.parseFilesetManifest(inputMappings, artifact, "workspace"); - assertThat(inputMappings).hasSize(2); - assertThat(inputMappings) - .containsEntry(new PathFragment("foo/bar"), ActionInputHelper.fromPath("/dir/file")); - assertThat(inputMappings) - .containsEntry(new PathFragment("foo/baz"), ActionInputHelper.fromPath("/dir/file")); - } - - @Test - public void testManifestWithDirectory() throws Exception { - // See AnalysisUtils for the mapping from "foo" to "_foo/MANIFEST". - scratchFile( - "/root/_foo/MANIFEST", - "workspace/bar /some", - ""); - - Artifact artifact = - new Artifact(fs.getPath("/root/foo"), Root.asSourceRoot(fs.getPath("/root"))); - expander.parseFilesetManifest(inputMappings, artifact, "workspace"); - assertThat(inputMappings).hasSize(1); - assertThat(inputMappings) - .containsEntry( - new PathFragment("foo/bar"), ActionInputHelper.fromPath("/some")); - } -}