Remove treeArtifactContents from OutputStore.
treeArtifactContents acts as a temporary cache prior to the full tree artifact being constructed and is optional - if not populated, ActionMetadataHandler reads tree artifact contents from disk. In fact, even if it is populated, disk reads are performed to verify expected contents.
Callers can use injectRemoteDirectory to establish known tree artifact contents instead. In this case, we assume that the contents are correct, so there is no need to check for a match on disk.
PiperOrigin-RevId: 311241850
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
index d339f5a..6e06891 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.actions.cache;
+import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
@@ -34,7 +35,7 @@
void setDigestForVirtualArtifact(Artifact artifact, byte[] digest);
/** Retrieves the artifacts inside the TreeArtifact, without injecting its digest. */
- Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact);
+ ImmutableSet<TreeFileArtifact> getExpandedOutputs(Artifact artifact);
/**
* Returns true iff artifact was intentionally omitted (not saved).
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataInjector.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataInjector.java
index ee95136..afbba6a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataInjector.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataInjector.java
@@ -15,10 +15,10 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue;
import com.google.devtools.build.lib.vfs.FileStatus;
-import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;
/** Supports metadata injection of action outputs into skyframe. */
@@ -39,12 +39,11 @@
/**
* Inject the metadata of a tree artifact whose contents are stored remotely.
*
- * @param output an output directory.
- * @param children the metadata of the files stored in the directory. The paths must be relative
- * to the path of {@code output}.
+ * @param output an output directory
+ * @param children the metadata of the files stored in the directory
*/
void injectRemoteDirectory(
- Artifact.SpecialArtifact output, Map<PathFragment, RemoteFileArtifactValue> children);
+ SpecialArtifact output, Map<TreeFileArtifact, RemoteFileArtifactValue> children);
/**
* Marks an {@link Artifact} as intentionally omitted.
@@ -55,12 +54,6 @@
void markOmitted(ActionInput output);
/**
- * Registers the given output as contents of a TreeArtifact, without injecting its digest. Prefer
- * {@link #injectDigest} when the digest is available.
- */
- void addExpandedTreeOutput(TreeFileArtifact output);
-
- /**
* Injects provided digest into the metadata handler, simultaneously caching lstat() data as well.
*/
void injectDigest(ActionInput output, FileStatus statNoFollow, byte[] digest);
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
index 157d5d4..d03ccbe 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteCache.java
@@ -39,7 +39,10 @@
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
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.Artifact.SpecialArtifact;
+import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.EnvironmentalExecException;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue;
@@ -611,20 +614,21 @@
"Symlinks in action outputs are not yet supported by "
+ "--experimental_remote_download_outputs=minimal");
}
- ImmutableMap.Builder<PathFragment, RemoteFileArtifactValue> childMetadata =
- ImmutableMap.builder();
+ SpecialArtifact parent = (SpecialArtifact) output;
+ ImmutableMap.Builder<TreeFileArtifact, RemoteFileArtifactValue> childMetadata =
+ ImmutableMap.builderWithExpectedSize(directory.files.size());
for (FileMetadata file : directory.files()) {
- PathFragment p = file.path().relativeTo(output.getPath());
- RemoteFileArtifactValue r =
+ TreeFileArtifact child =
+ ActionInputHelper.treeFileArtifact(parent, file.path().relativeTo(parent.getPath()));
+ RemoteFileArtifactValue value =
new RemoteFileArtifactValue(
DigestUtil.toBinaryDigest(file.digest()),
file.digest().getSizeBytes(),
- /* locationIndex= */ 1,
+ /*locationIndex=*/ 1,
actionId);
- childMetadata.put(p, r);
+ childMetadata.put(child, value);
}
- metadataInjector.injectRemoteDirectory(
- (Artifact.SpecialArtifact) output, childMetadata.build());
+ metadataInjector.injectRemoteDirectory(parent, childMetadata.build());
} else {
FileMetadata outputMetadata = metadata.file(execRoot.getRelative(output.getExecPathString()));
if (outputMetadata == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index 25d6718..bc5a13d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -246,7 +246,6 @@
// Calling code depends on this particular exception.
throw new FileNotFoundException(artifact + " not found");
}
- // Fallthrough: the artifact must be a non-tree, non-middleman output artifact.
// Don't store metadata for output artifacts that are not declared outputs of the action.
if (!isKnownOutput(artifact)) {
@@ -264,6 +263,18 @@
if (fileMetadata != null) {
return metadataFromValue(fileMetadata);
}
+ // This artifact was not injected directly to the store, but it may have been injected as part
+ // of a tree artifact.
+ // TODO(jhorvitz): We can skip this for action template expansion artifacts.
+ if (artifact.hasParent()) {
+ TreeArtifactValue tree = store.getTreeArtifactData(artifact.getParent());
+ if (tree != null) {
+ fileMetadata = tree.getChildValues().get(artifact);
+ if (fileMetadata != null) {
+ return metadataFromValue(fileMetadata);
+ }
+ }
+ }
// No existing metadata; this can happen if the output metadata is not injected after a spawn
// is executed. SkyframeActionExecutor.checkOutputs calls this method for every output file of
@@ -367,48 +378,7 @@
}
}
- Set<TreeFileArtifact> registeredContents = store.getTreeArtifactContents(artifact);
- if (registeredContents != null) {
- // Check that our registered outputs matches on-disk outputs. Only perform this check
- // when contents were explicitly registered.
- // TODO(bazel-team): Provide a way for actions to register empty TreeArtifacts.
-
- // By the time we're constructing TreeArtifactValues, use of the metadata handler
- // should be single threaded and there should be no race condition.
- // The current design of ActionMetadataHandler makes this hard to enforce.
- Set<PathFragment> paths =
- TreeArtifactValue.explodeDirectory(artifactPathResolver.toPath(artifact));
- Set<TreeFileArtifact> diskFiles = ActionInputHelper.asTreeFileArtifacts(artifact, paths);
- if (!diskFiles.equals(registeredContents)) {
- // There might be more than one error here. We first look for missing output files.
- Set<TreeFileArtifact> missingFiles = Sets.difference(registeredContents, diskFiles);
- if (!missingFiles.isEmpty()) {
- // Don't throw IOException--getMetadataMaybe() eats them.
- // TODO(bazel-team): Report this error in a better way when called by checkOutputs()
- // Currently it's hard to report this error without refactoring, since checkOutputs()
- // likes to substitute its own error messages upon catching IOException, and falls
- // through to unrecoverable error behavior on any other exception.
- throw new IOException(
- "Output file "
- + missingFiles.iterator().next()
- + " was registered, but not present on disk");
- }
-
- Set<TreeFileArtifact> extraFiles = Sets.difference(diskFiles, registeredContents);
- // extraFiles cannot be empty
- throw new IOException(
- "File "
- + extraFiles.iterator().next().getParentRelativePath()
- + ", present in TreeArtifact "
- + artifact
- + ", was not registered");
- }
-
- value = constructTreeArtifactValue(registeredContents);
- } else {
- value = constructTreeArtifactValueFromFilesystem(artifact);
- }
-
+ value = constructTreeArtifactValueFromFilesystem(artifact);
store.putTreeArtifactData(artifact, value);
return value;
}
@@ -462,27 +432,13 @@
Set<PathFragment> paths =
TreeArtifactValue.explodeDirectory(artifactPathResolver.toPath(artifact));
- // If you're reading tree artifacts from disk while tree artifact contents are being injected,
- // something has gone terribly wrong.
- Object previousContents = store.getTreeArtifactContents(artifact);
- Preconditions.checkState(
- previousContents == null,
- "Race condition while constructing TreeArtifactValue: %s, %s",
- artifact,
- previousContents);
return constructTreeArtifactValue(ActionInputHelper.asTreeFileArtifacts(artifact, paths));
}
@Override
- public void addExpandedTreeOutput(TreeFileArtifact output) {
- Preconditions.checkState(executionMode.get());
- store.addTreeArtifactContents(output.getParent(), output);
- }
-
- @Override
- public Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
- Set<TreeFileArtifact> contents = store.getTreeArtifactContents(artifact);
- return contents != null ? ImmutableSet.copyOf(contents) : ImmutableSet.of();
+ public ImmutableSet<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
+ TreeArtifactValue treeArtifact = store.getTreeArtifactData(artifact);
+ return treeArtifact != null ? treeArtifact.getChildren() : ImmutableSet.of();
}
@Override
@@ -557,22 +513,13 @@
@Override
public void injectRemoteDirectory(
- SpecialArtifact output, Map<PathFragment, RemoteFileArtifactValue> children) {
+ SpecialArtifact output, Map<TreeFileArtifact, RemoteFileArtifactValue> children) {
Preconditions.checkArgument(
- isKnownOutput(output), output + " is not a declared output of this action");
+ isKnownOutput(output), "%s is not a declared output of this action", output);
Preconditions.checkArgument(output.isTreeArtifact(), "output must be a tree artifact");
Preconditions.checkState(
- executionMode.get(), "Tried to inject %s outside of execution.", output);
-
- ImmutableMap.Builder<TreeFileArtifact, FileArtifactValue> childFileValues =
- ImmutableMap.builder();
- for (Map.Entry<PathFragment, RemoteFileArtifactValue> child : children.entrySet()) {
- childFileValues.put(
- ActionInputHelper.treeFileArtifact(output, child.getKey()), child.getValue());
- }
-
- TreeArtifactValue treeArtifactValue = TreeArtifactValue.create(childFileValues.build());
- store.putTreeArtifactData(output, treeArtifactValue);
+ executionMode.get(), "Tried to inject %s outside of execution", output);
+ store.putTreeArtifactData(output, TreeArtifactValue.create(children));
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/MinimalOutputStore.java b/src/main/java/com/google/devtools/build/lib/skyframe/MinimalOutputStore.java
index d12823b..1d9fcdc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/MinimalOutputStore.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/MinimalOutputStore.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.skyframe;
import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
/**
@@ -31,7 +30,4 @@
super.putArtifactData(artifact, value);
}
}
-
- @Override
- void addTreeArtifactContents(Artifact artifact, TreeFileArtifact contents) {}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java b/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
index 914b8b5..d69b309 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/OutputStore.java
@@ -13,11 +13,9 @@
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import java.util.Set;
@@ -28,10 +26,9 @@
/**
* Storage layer for data associated with outputs of an action.
*
- * <p>Data is mainly stored in three maps, {@link #artifactData}, {@link #treeArtifactData} and
- * {@link #treeArtifactContents}, all of which are keyed on an {@link Artifact}. For each of these
- * maps, this class exposes standard methods such as {@code get}, {@code put}, {@code add}, and
- * {@code getAll}.
+ * <p>Data is mainly stored in two maps, {@link #artifactData} and {@link #treeArtifactData}, both
+ * of which are keyed on an {@link Artifact}. For each of these maps, this class exposes standard
+ * methods such as {@code get}, {@code put}, {@code add}, and {@code getAll}.
*
* <p>This implementation aggressively stores all data. Subclasses may override mutating methods to
* avoid storing unnecessary data.
@@ -44,9 +41,6 @@
private final ConcurrentMap<Artifact, TreeArtifactValue> treeArtifactData =
new ConcurrentHashMap<>();
- private final ConcurrentMap<Artifact, Set<TreeFileArtifact>> treeArtifactContents =
- new ConcurrentHashMap<>();
-
private final Set<Artifact> injectedFiles = Sets.newConcurrentHashSet();
@Nullable
@@ -79,22 +73,6 @@
return ImmutableMap.copyOf(treeArtifactData);
}
- /**
- * Returns a set of the given tree artifact's contents.
- *
- * <p>If the return value is {@code null}, this means nothing was injected, and the output
- * TreeArtifact is to have its values read from disk instead.
- */
- @Nullable
- final Set<TreeFileArtifact> getTreeArtifactContents(Artifact artifact) {
- return treeArtifactContents.get(artifact);
- }
-
- void addTreeArtifactContents(Artifact artifact, TreeFileArtifact contents) {
- Preconditions.checkArgument(artifact.isTreeArtifact(), artifact);
- treeArtifactContents.computeIfAbsent(artifact, a -> Sets.newConcurrentHashSet()).add(contents);
- }
-
void injectRemoteFile(
Artifact output, byte[] digest, long size, int locationIndex, String actionId) {
injectOutputData(
@@ -116,7 +94,6 @@
final void clear() {
artifactData.clear();
treeArtifactData.clear();
- treeArtifactContents.clear();
injectedFiles.clear();
}
@@ -124,7 +101,6 @@
final void remove(Artifact artifact) {
artifactData.remove(artifact);
treeArtifactData.remove(artifact);
- treeArtifactContents.remove(artifact);
injectedFiles.remove(artifact);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
index 5b74906..2e3ca1c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
@@ -69,14 +69,15 @@
* Returns a TreeArtifactValue out of the given Artifact-relative path fragments and their
* corresponding FileArtifactValues.
*/
- static TreeArtifactValue create(Map<TreeFileArtifact, FileArtifactValue> childFileValues) {
+ static TreeArtifactValue create(
+ Map<TreeFileArtifact, ? extends FileArtifactValue> childFileValues) {
if (childFileValues.isEmpty()) {
return EMPTY;
}
Map<String, FileArtifactValue> digestBuilder =
Maps.newHashMapWithExpectedSize(childFileValues.size());
boolean remote = true;
- for (Map.Entry<TreeFileArtifact, FileArtifactValue> e : childFileValues.entrySet()) {
+ for (Map.Entry<TreeFileArtifact, ? extends FileArtifactValue> e : childFileValues.entrySet()) {
FileArtifactValue value = e.getValue();
// TODO(buchgr): Enforce that all children in a tree artifact are either remote or local
// once b/70354083 is fixed.
@@ -109,7 +110,7 @@
return digest.clone();
}
- public Iterable<TreeFileArtifact> getChildren() {
+ public ImmutableSet<TreeFileArtifact> getChildren() {
return childData.keySet();
}
@@ -165,7 +166,7 @@
}
@Override
- public Iterable<TreeFileArtifact> getChildren() {
+ public ImmutableSet<TreeFileArtifact> getChildren() {
throw new UnsupportedOperationException();
}
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
index bc5af45..4506c2e 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
@@ -952,12 +952,7 @@
}
@Override
- public void addExpandedTreeOutput(TreeFileArtifact output) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
+ public ImmutableSet<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
throw new UnsupportedOperationException();
}
@@ -974,7 +969,7 @@
@Override
public void injectRemoteDirectory(
- SpecialArtifact treeArtifact, Map<PathFragment, RemoteFileArtifactValue> children) {
+ SpecialArtifact treeArtifact, Map<TreeFileArtifact, RemoteFileArtifactValue> children) {
throw new UnsupportedOperationException();
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
index cfbbe7d..8f0c7af 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
@@ -44,6 +44,7 @@
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.ActionLookupData;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifactType;
@@ -973,6 +974,7 @@
PathFragment.create("outputs/dir"),
ActionsTestUtil.NULL_ARTIFACT_OWNER,
SpecialArtifactType.TREE);
+ dir.setGeneratingActionKey(ActionLookupData.create(ActionsTestUtil.NULL_ARTIFACT_OWNER, 0));
MetadataInjector injector = mock(MetadataInjector.class);
@@ -991,15 +993,12 @@
// assert
assertThat(inMemoryOutput).isNull();
- Map<PathFragment, RemoteFileArtifactValue> m =
- ImmutableMap.<PathFragment, RemoteFileArtifactValue>builder()
- .put(
- PathFragment.create("file1"),
- new RemoteFileArtifactValue(toBinaryDigest(d1), d1.getSizeBytes(), 1, "action-id"))
- .put(
- PathFragment.create("a/file2"),
- new RemoteFileArtifactValue(toBinaryDigest(d2), d2.getSizeBytes(), 1, "action-id"))
- .build();
+ Map<Artifact.TreeFileArtifact, RemoteFileArtifactValue> m =
+ ImmutableMap.of(
+ ActionInputHelper.treeFileArtifact(dir, "file1"),
+ new RemoteFileArtifactValue(toBinaryDigest(d1), d1.getSizeBytes(), 1, "action-id"),
+ ActionInputHelper.treeFileArtifact(dir, "a/file2"),
+ new RemoteFileArtifactValue(toBinaryDigest(d2), d2.getSizeBytes(), 1, "action-id"));
verify(injector).injectRemoteDirectory(eq(dir), eq(m));
Path outputBase = artifactRoot.getRoot().asPath();
@@ -1045,6 +1044,7 @@
PathFragment.create("outputs/dir"),
ActionsTestUtil.NULL_ARTIFACT_OWNER,
SpecialArtifactType.TREE);
+ dir.setGeneratingActionKey(ActionLookupData.create(ActionsTestUtil.NULL_ARTIFACT_OWNER, 0));
MetadataInjector injector = mock(MetadataInjector.class);
// act
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
index 1f35eba..8e44093 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
@@ -187,7 +187,7 @@
@Override
public void injectRemoteDirectory(
Artifact.SpecialArtifact output,
- Map<PathFragment, RemoteFileArtifactValue> children) {
+ Map<TreeFileArtifact, RemoteFileArtifactValue> children) {
throw new UnsupportedOperationException();
}
@@ -197,11 +197,6 @@
}
@Override
- public void addExpandedTreeOutput(TreeFileArtifact output) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public void injectDigest(ActionInput output, FileStatus statNoFollow, byte[] digest) {
throw new UnsupportedOperationException();
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
index 6b7ac85..7196fee 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
@@ -139,7 +139,8 @@
@Override
public void injectRemoteDirectory(
- Artifact.SpecialArtifact output, Map<PathFragment, RemoteFileArtifactValue> children) {
+ Artifact.SpecialArtifact output,
+ Map<TreeFileArtifact, RemoteFileArtifactValue> children) {
throw new UnsupportedOperationException();
}
@@ -149,11 +150,6 @@
}
@Override
- public void addExpandedTreeOutput(TreeFileArtifact output) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public void injectDigest(ActionInput output, FileStatus statNoFollow, byte[] digest) {
throw new UnsupportedOperationException();
}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
index 47c4576..9ee4dc3 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
@@ -390,11 +390,10 @@
new RemoteFileArtifactValue(new byte[] {1, 2, 3}, 5, 1, "foo");
RemoteFileArtifactValue barValue =
new RemoteFileArtifactValue(new byte[] {4, 5, 6}, 10, 1, "bar");
- Map<PathFragment, RemoteFileArtifactValue> children =
- ImmutableMap.<PathFragment, RemoteFileArtifactValue>builder()
- .put(PathFragment.create("foo"), fooValue)
- .put(PathFragment.create("bar"), barValue)
- .build();
+ Map<TreeFileArtifact, RemoteFileArtifactValue> children =
+ ImmutableMap.of(
+ ActionInputHelper.treeFileArtifact(treeArtifact, "foo"), fooValue,
+ ActionInputHelper.treeFileArtifact(treeArtifact, "bar"), barValue);
handler.injectRemoteDirectory(treeArtifact, children);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
index 876a9e6..3d1a9ed 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
@@ -14,12 +14,10 @@
package com.google.devtools.build.lib.skyframe;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.devtools.build.lib.actions.ActionInputHelper.treeFileArtifact;
import static org.junit.Assert.assertThrows;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -86,13 +84,6 @@
@RunWith(JUnit4.class)
public class TreeArtifactBuildTest extends TimestampBuilderTestCase {
- private static final Predicate<Event> IS_ERROR_EVENT = new Predicate<Event>() {
- @Override
- public boolean apply(Event event) {
- return event.getKind().equals(EventKind.ERROR);
- }
- };
-
// Common Artifacts, TreeFileArtifact, and Buttons. These aren't all used in all tests, but
// they're used often enough that we can save ourselves a lot of copy-pasted code by creating them
// in setUp().
@@ -108,6 +99,7 @@
TreeFileArtifact outTwoFileOne;
TreeFileArtifact outTwoFileTwo;
Button buttonTwo = new Button();
+
@Before
public void setUp() throws Exception {
in = createSourceArtifact("input");
@@ -155,9 +147,10 @@
TouchingTestAction actionOne = new TouchingTestAction(outOneFileOne, outOneFileTwo);
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- ImmutableList.of(outOneFileOne, outOneFileTwo),
- ImmutableList.of(outTwoFileOne, outTwoFileTwo));
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(
+ ImmutableList.of(outOneFileOne, outOneFileTwo),
+ ImmutableList.of(outTwoFileOne, outTwoFileTwo));
registerAction(actionTwo);
buildArtifact(outTwo);
@@ -209,12 +202,12 @@
SpecialArtifact outTwo = createTreeArtifact("outputTwo");
Button buttonTwo = new Button();
- TouchingTestAction actionOne = new TouchingTestAction(
- buttonOne, outOne, "file_one", "file_two");
+ TouchingTestAction actionOne =
+ new TouchingTestAction(buttonOne, outOne, "file_one", "file_two");
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- buttonTwo, outOne, outTwo, "file_one", "file_two");
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(buttonTwo, outOne, outTwo, "file_one", "file_two");
registerAction(actionTwo);
buttonOne.pressed = buttonTwo.pressed = false;
@@ -228,22 +221,18 @@
assertThat(buttonTwo.pressed).isFalse(); // not built
}
- /**
- * Test rebuilding TreeArtifacts for inputs, outputs, and dependents.
- * Also a test for caching.
- */
+ /** Test rebuilding TreeArtifacts for inputs, outputs, and dependents. Also a test for caching. */
@Test
public void testTransitiveReexecutionForTreeArtifacts() throws Exception {
- WriteInputToFilesAction actionOne = new WriteInputToFilesAction(
- buttonOne,
- in,
- outOneFileOne, outOneFileTwo);
+ WriteInputToFilesAction actionOne =
+ new WriteInputToFilesAction(buttonOne, in, outOneFileOne, outOneFileTwo);
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- buttonTwo,
- ImmutableList.of(outOneFileOne, outOneFileTwo),
- ImmutableList.of(outTwoFileOne, outTwoFileTwo));
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(
+ buttonTwo,
+ ImmutableList.of(outOneFileOne, outOneFileTwo),
+ ImmutableList.of(outTwoFileOne, outTwoFileTwo));
registerAction(actionTwo);
buttonOne.pressed = buttonTwo.pressed = false;
@@ -273,16 +262,15 @@
/** Tests that changing a TreeArtifact directory should cause reexeuction. */
@Test
public void testDirectoryContentsCachingForTreeArtifacts() throws Exception {
- WriteInputToFilesAction actionOne = new WriteInputToFilesAction(
- buttonOne,
- in,
- outOneFileOne, outOneFileTwo);
+ WriteInputToFilesAction actionOne =
+ new WriteInputToFilesAction(buttonOne, in, outOneFileOne, outOneFileTwo);
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- buttonTwo,
- ImmutableList.of(outOneFileOne, outOneFileTwo),
- ImmutableList.of(outTwoFileOne, outTwoFileTwo));
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(
+ buttonTwo,
+ ImmutableList.of(outOneFileOne, outOneFileTwo),
+ ImmutableList.of(outTwoFileOne, outTwoFileTwo));
registerAction(actionTwo);
buttonOne.pressed = buttonTwo.pressed = false;
@@ -332,16 +320,15 @@
Artifact in = createSourceArtifact("touchable_input");
touchFile(in);
- WriteInputToFilesAction actionOne = new WriteInputToFilesAction(
- buttonOne,
- in,
- outOneFileOne, outOneFileTwo);
+ WriteInputToFilesAction actionOne =
+ new WriteInputToFilesAction(buttonOne, in, outOneFileOne, outOneFileTwo);
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- buttonTwo,
- ImmutableList.of(outOneFileOne, outOneFileTwo),
- ImmutableList.of(outTwoFileOne, outTwoFileTwo));
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(
+ buttonTwo,
+ ImmutableList.of(outOneFileOne, outOneFileTwo),
+ ImmutableList.of(outTwoFileOne, outTwoFileTwo));
registerAction(actionTwo);
buttonOne.pressed = buttonTwo.pressed = false;
@@ -375,16 +362,19 @@
/** Tests that the declared order of TreeArtifact contents does not matter. */
@Test
public void testOrderIndependenceOfTreeArtifactContents() throws Exception {
- WriteInputToFilesAction actionOne = new WriteInputToFilesAction(
- in,
- // The design of WritingTestAction is s.t.
- // these files will be registered in the given order.
- outOneFileTwo, outOneFileOne);
+ WriteInputToFilesAction actionOne =
+ new WriteInputToFilesAction(
+ in,
+ // The design of WritingTestAction is s.t.
+ // these files will be registered in the given order.
+ outOneFileTwo,
+ outOneFileOne);
registerAction(actionOne);
- CopyTreeAction actionTwo = new CopyTreeAction(
- ImmutableList.of(outOneFileOne, outOneFileTwo),
- ImmutableList.of(outTwoFileOne, outTwoFileTwo));
+ CopyTreeAction actionTwo =
+ new CopyTreeAction(
+ ImmutableList.of(outOneFileOne, outOneFileTwo),
+ ImmutableList.of(outTwoFileOne, outTwoFileTwo));
registerAction(actionTwo);
buildArtifact(outTwo);
@@ -418,82 +408,6 @@
buildArtifact(outTwo); // should not fail
}
- @Test
- public void testInvalidOutputRegistrations() throws Exception {
- // Failure expected
- StoredEventHandler storingEventHandler = new StoredEventHandler();
- reporter.removeHandler(failFastHandler);
- reporter.addHandler(storingEventHandler);
-
- SpecialArtifact outOne = createTreeArtifact("outputOne");
- TreeFileArtifact outOneFileOne =
- ActionInputHelper.treeFileArtifactWithNoGeneratingActionSet(
- outOne, PathFragment.create("out_one_file_one"), ACTION_LOOKUP_KEY);
- TreeFileArtifact outOneFileTwo =
- ActionInputHelper.treeFileArtifactWithNoGeneratingActionSet(
- outOne, PathFragment.create("out_one_file_two"), ACTION_LOOKUP_KEY);
- TreeArtifactTestAction failureOne = new TreeArtifactTestAction(
- Runnables.doNothing(), outOneFileOne, outOneFileTwo) {
- @Override
- public void executeTestBehavior(ActionExecutionContext actionExecutionContext) {
- try {
- writeFile(outOneFileOne, "one");
- writeFile(outOneFileTwo, "two");
- // In this test case, we only register one output. This will fail.
- registerOutput(actionExecutionContext, "one");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- registerAction(failureOne);
- outOneFileOne.setGeneratingActionKey(outOne.getGeneratingActionKey());
- outOneFileTwo.setGeneratingActionKey(outOne.getGeneratingActionKey());
- assertThrows(BuildFailedException.class, () -> buildArtifact(outOne));
- // not all outputs were created
- List<Event> errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains("not present on disk");
- assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
-
- SpecialArtifact outTwo = createTreeArtifact("outputTwo");
- TreeFileArtifact outTwoFileOne =
- ActionInputHelper.treeFileArtifactWithNoGeneratingActionSet(
- outTwo, PathFragment.create("out_two_file_one"), ACTION_LOOKUP_KEY);
- TreeFileArtifact outTwoFileTwo =
- ActionInputHelper.treeFileArtifactWithNoGeneratingActionSet(
- outTwo, PathFragment.create("out_two_file_two"), ACTION_LOOKUP_KEY);
- TreeArtifactTestAction failureTwo = new TreeArtifactTestAction(
- Runnables.doNothing(), outTwoFileOne, outTwoFileTwo) {
- @Override
- public void executeTestBehavior(ActionExecutionContext actionExecutionContext) {
- try {
- writeFile(outTwoFileOne, "one");
- writeFile(outTwoFileTwo, "two");
- // In this test case, register too many outputs. This will fail.
- registerOutput(actionExecutionContext, "one");
- registerOutput(actionExecutionContext, "two");
- registerOutput(actionExecutionContext, "three");
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- };
-
- registerAction(failureTwo);
- outTwoFileOne.setGeneratingActionKey(outTwo.getGeneratingActionKey());
- outTwoFileTwo.setGeneratingActionKey(outTwo.getGeneratingActionKey());
- storingEventHandler.clear();
- assertThrows(BuildFailedException.class, () -> buildArtifact(outTwo));
- errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains("not present on disk");
- assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
- }
-
private static void checkDirectoryPermissions(Path path) throws IOException {
assertThat(path.isDirectory()).isTrue();
assertThat(path.isExecutable()).isTrue();
@@ -520,9 +434,6 @@
writeFile(out.getPath().getChild("one"), "one");
writeFile(out.getPath().getChild("two"), "two");
writeFile(out.getPath().getChild("three").getChild("four"), "three/four");
- registerOutput(actionExecutionContext, "one");
- registerOutput(actionExecutionContext, "two");
- registerOutput(actionExecutionContext, "three/four");
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -595,10 +506,10 @@
assertThrows(BuildFailedException.class, () -> buildArtifact(action.getSoleOutput()));
List<Event> errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains(
- "Failed to resolve relative path links/link");
+ ImmutableList.copyOf(
+ Iterables.filter(storingEventHandler.getEvents(), TreeArtifactBuildTest::isErrorEvent));
+ assertThat(errors).hasSize(2);
+ assertThat(errors.get(0).getMessage()).contains("Failed to resolve relative path links/link");
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
}
@@ -631,9 +542,10 @@
assertThrows(BuildFailedException.class, () -> buildArtifact(action.getSoleOutput()));
List<Event> errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains("Failed to resolve relative path links/link");
+ ImmutableList.copyOf(
+ Iterables.filter(storingEventHandler.getEvents(), TreeArtifactBuildTest::isErrorEvent));
+ assertThat(errors).hasSize(2);
+ assertThat(errors.get(0).getMessage()).contains("Failed to resolve relative path links/link");
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
}
@@ -693,11 +605,13 @@
assertThrows(BuildFailedException.class, () -> buildArtifact(action.getSoleOutput()));
List<Event> errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains(
- "A TreeArtifact may not contain relative symlinks whose target paths traverse "
- + "outside of the TreeArtifact");
+ ImmutableList.copyOf(
+ Iterables.filter(storingEventHandler.getEvents(), TreeArtifactBuildTest::isErrorEvent));
+ assertThat(errors).hasSize(2);
+ assertThat(errors.get(0).getMessage())
+ .contains(
+ "A TreeArtifact may not contain relative symlinks whose target paths traverse "
+ + "outside of the TreeArtifact");
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
}
@@ -733,11 +647,13 @@
assertThrows(BuildFailedException.class, () -> buildArtifact(action.getSoleOutput()));
List<Event> errors =
- ImmutableList.copyOf(Iterables.filter(storingEventHandler.getEvents(), IS_ERROR_EVENT));
- assertThat(errors).hasSize(2);
- assertThat(errors.get(0).getMessage()).contains(
- "A TreeArtifact may not contain relative symlinks whose target paths traverse "
- + "outside of the TreeArtifact");
+ ImmutableList.copyOf(
+ Iterables.filter(storingEventHandler.getEvents(), TreeArtifactBuildTest::isErrorEvent));
+ assertThat(errors).hasSize(2);
+ assertThat(errors.get(0).getMessage())
+ .contains(
+ "A TreeArtifact may not contain relative symlinks whose target paths traverse "
+ + "outside of the TreeArtifact");
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
}
@@ -798,8 +714,8 @@
// artifact2 is a tree artifact generated by an action template.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
// We mock out the action template function to expand into two actions that just touch the
@@ -846,8 +762,8 @@
// artifact2 is a tree artifact generated by an action template.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
// We mock out the action template function to expand into two actions:
@@ -897,8 +813,8 @@
// artifact2 is a tree artifact generated by an action template.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
// We mock out the action template function to expand into two actions:
@@ -951,8 +867,8 @@
// artifact2 is a tree artifact generated by an action template.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
// We mock out the action template function to expand into two actions that throw when executed.
@@ -994,8 +910,8 @@
// artifact2 is a tree artifact generated by an action template.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
BuildFailedException e =
@@ -1013,8 +929,8 @@
// artifact2 is a tree artifact generated by an action template that takes artifact1 as input.
SpecialArtifact artifact2 = createTreeArtifact("treeArtifact2");
- SpawnActionTemplate actionTemplate = ActionsTestUtil.createDummySpawnActionTemplate(
- artifact1, artifact2);
+ SpawnActionTemplate actionTemplate =
+ ActionsTestUtil.createDummySpawnActionTemplate(artifact1, artifact2);
registerAction(actionTemplate);
buildArtifact(artifact2);
@@ -1025,16 +941,21 @@
assertThat(artifact2.getPath().getDirectoryEntries()).isEmpty();
}
+ private static boolean isErrorEvent(Event event) {
+ return event.getKind().equals(EventKind.ERROR);
+ }
+
/**
- * A generic test action that takes at most one input TreeArtifact,
- * exactly one output TreeArtifact, and some path fragment inputs/outputs.
+ * A generic test action that takes at most one input TreeArtifact, exactly one output
+ * TreeArtifact, and some path fragment inputs/outputs.
*/
private abstract static class TreeArtifactTestAction extends TestAction {
final Iterable<TreeFileArtifact> inputFiles;
final Iterable<TreeFileArtifact> outputFiles;
TreeArtifactTestAction(final SpecialArtifact output, final String... subOutputs) {
- this(Runnables.doNothing(),
+ this(
+ Runnables.doNothing(),
null,
ImmutableList.<TreeFileArtifact>of(),
output,
@@ -1054,20 +975,34 @@
}
TreeArtifactTestAction(Runnable effect, Collection<TreeFileArtifact> outputFiles) {
- this(effect, null, ImmutableList.<TreeFileArtifact>of(),
- outputFiles.iterator().next().getParent(), outputFiles);
+ this(
+ effect,
+ null,
+ ImmutableList.<TreeFileArtifact>of(),
+ outputFiles.iterator().next().getParent(),
+ outputFiles);
}
- TreeArtifactTestAction(Runnable effect, Artifact inputFile,
- Collection<TreeFileArtifact> outputFiles) {
- this(effect, inputFile, ImmutableList.<TreeFileArtifact>of(),
- outputFiles.iterator().next().getParent(), outputFiles);
+ TreeArtifactTestAction(
+ Runnable effect, Artifact inputFile, Collection<TreeFileArtifact> outputFiles) {
+ this(
+ effect,
+ inputFile,
+ ImmutableList.<TreeFileArtifact>of(),
+ outputFiles.iterator().next().getParent(),
+ outputFiles);
}
- TreeArtifactTestAction(Runnable effect, Collection<TreeFileArtifact> inputFiles,
+ TreeArtifactTestAction(
+ Runnable effect,
+ Collection<TreeFileArtifact> inputFiles,
Collection<TreeFileArtifact> outputFiles) {
- this(effect, inputFiles.iterator().next().getParent(), inputFiles,
- outputFiles.iterator().next().getParent(), outputFiles);
+ this(
+ effect,
+ inputFiles.iterator().next().getParent(),
+ inputFiles,
+ outputFiles.iterator().next().getParent(),
+ outputFiles);
}
TreeArtifactTestAction(
@@ -1102,8 +1037,8 @@
// Sanity check--verify all inputs exist.
Artifact input = getInputs().getSingleton();
if (!input.getPath().exists()) {
- throw new IllegalStateException("action's input Artifact does not exist: "
- + input.getPath());
+ throw new IllegalStateException(
+ "action's input Artifact does not exist: " + input.getPath());
}
for (Artifact inputFile : inputFiles) {
if (!inputFile.getPath().exists()) {
@@ -1117,9 +1052,6 @@
try {
effect.call();
executeTestBehavior(actionExecutionContext);
- for (TreeFileArtifact outputFile : outputFiles) {
- actionExecutionContext.getMetadataHandler().addExpandedTreeOutput(outputFile);
- }
} catch (RuntimeException e) {
throw new RuntimeException(e);
} catch (Exception e) {
@@ -1143,11 +1075,6 @@
return r;
}
- void registerOutput(ActionExecutionContext context, String outputName) throws IOException {
- context.getMetadataHandler().addExpandedTreeOutput(
- treeFileArtifact(getSoleOutput(), PathFragment.create(outputName)));
- }
-
static List<TreeFileArtifact> asTreeFileArtifacts(
final SpecialArtifact parent, String... files) {
return Lists.transform(
@@ -1192,10 +1119,7 @@
this(Runnables.doNothing(), input, outputs);
}
- WriteInputToFilesAction(
- Runnable effect,
- Artifact input,
- TreeFileArtifact... outputs) {
+ WriteInputToFilesAction(Runnable effect, Artifact input, TreeFileArtifact... outputs) {
super(effect, input, Arrays.asList(outputs));
Preconditions.checkArgument(!input.isTreeArtifact());
}
@@ -1219,13 +1143,16 @@
CopyTreeAction(
Runnable effect, SpecialArtifact input, SpecialArtifact output, String... sourcesAndDests) {
- super(effect, input, asTreeFileArtifacts(input, sourcesAndDests), output,
+ super(
+ effect,
+ input,
+ asTreeFileArtifacts(input, sourcesAndDests),
+ output,
asTreeFileArtifacts(output, sourcesAndDests));
}
CopyTreeAction(
- Collection<TreeFileArtifact> inputPaths,
- Collection<TreeFileArtifact> outputPaths) {
+ Collection<TreeFileArtifact> inputPaths, Collection<TreeFileArtifact> outputPaths) {
super(Runnables.doNothing(), inputPaths, outputPaths);
}