aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/actions/Artifact.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/actions/Artifact.java654
1 files changed, 654 insertions, 0 deletions
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
new file mode 100644
index 0000000000..2f2272b378
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -0,0 +1,654 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.actions;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action.MiddlemanType;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.SkylarkCallable;
+import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+/**
+ * An Artifact represents a file used by the build system, whether it's a source
+ * file or a derived (output) file. Not all Artifacts have a corresponding
+ * FileTarget object in the <code>build.packages</code> API: for example,
+ * low-level intermediaries internal to a given rule, such as a Java class files
+ * or C++ object files. However all FileTargets have a corresponding Artifact.
+ *
+ * <p>In any given call to Builder#buildArtifacts(), no two Artifacts in the
+ * action graph may refer to the same path.
+ *
+ * <p>Artifacts generally fall into two classifications, source and derived, but
+ * there exist a few other cases that are fuzzy and difficult to classify. The
+ * following cases exist:
+ * <ul>
+ * <li>Well-formed source Artifacts will have null generating Actions and a root
+ * that is orthogonal to execRoot. (With the root coming from the package path.)
+ * <li>Well-formed derived Artifacts will have non-null generating Actions, and
+ * a root that is below execRoot.
+ * <li>Symlinked include source Artifacts under the output/include tree will
+ * appear to be derived artifacts with null generating Actions.
+ * <li>Some derived Artifacts, mostly in the genfiles tree and mostly discovered
+ * during include validation, will also have null generating Actions.
+ * </ul>
+ *
+ * <p>This class is "theoretically" final; it should not be subclassed except by
+ * {@link SpecialArtifact}.
+ */
+@Immutable
+@SkylarkModule(name = "File",
+ doc = "This type represents a file used by the build system. It can be "
+ + "either a source file or a derived file produced by a rule.")
+public class Artifact implements FileType.HasFilename, Comparable<Artifact>, ActionInput {
+
+ /** An object that can expand middleman artifacts. */
+ public interface MiddlemanExpander {
+
+ /**
+ * Expands the middleman artifact "mm", and populates "output" with the result.
+ *
+ * <p>{@code mm.isMiddlemanArtifact()} must be true. Only aggregating middlemen are expanded.
+ */
+ void expand(Artifact mm, Collection<? super Artifact> output);
+ }
+
+ public static final ImmutableList<Artifact> NO_ARTIFACTS = ImmutableList.of();
+
+ /**
+ * A Predicate that evaluates to true if the Artifact is not a middleman artifact.
+ */
+ public static final Predicate<Artifact> MIDDLEMAN_FILTER = new Predicate<Artifact>() {
+ @Override
+ public boolean apply(Artifact input) {
+ return !input.isMiddlemanArtifact();
+ }
+ };
+
+ private final Path path;
+ private final Root root;
+ private final PathFragment execPath;
+ private final PathFragment rootRelativePath;
+ // Non-final only for use when dealing with deserialized artifacts.
+ private ArtifactOwner owner;
+
+ /**
+ * Constructs an artifact for the specified path, root and execPath. The root must be an ancestor
+ * of path, and execPath must be a non-absolute tail of path. Outside of testing, this method
+ * should only be called by ArtifactFactory. The ArtifactOwner may be null.
+ *
+ * <p>In a source Artifact, the path tail after the root will be identical to the execPath, but
+ * the root will be orthogonal to execRoot.
+ * <pre>
+ * [path] == [/root/][execPath]
+ * </pre>
+ *
+ * <p>In a derived Artifact, the execPath will overlap with part of the root, which in turn will
+ * be below of the execRoot.
+ * <pre>
+ * [path] == [/root][pathTail] == [/execRoot][execPath] == [/execRoot][rootPrefix][pathTail]
+ * <pre>
+ */
+ @VisibleForTesting
+ public Artifact(Path path, Root root, PathFragment execPath, ArtifactOwner owner) {
+ if (root == null || !path.startsWith(root.getPath())) {
+ throw new IllegalArgumentException(root + ": illegal root for " + path);
+ }
+ if (execPath == null || execPath.isAbsolute() || !path.asFragment().endsWith(execPath)) {
+ throw new IllegalArgumentException(execPath + ": illegal execPath for " + path);
+ }
+ this.path = path;
+ this.root = root;
+ this.execPath = execPath;
+ // These two lines establish the invariant that
+ // execPath == rootRelativePath <=> execPath.equals(rootRelativePath)
+ // This is important for isSourceArtifact.
+ PathFragment rootRel = path.relativeTo(root.getPath());
+ if (!execPath.endsWith(rootRel)) {
+ throw new IllegalArgumentException(execPath + ": illegal execPath doesn't end with "
+ + rootRel + " at " + path + " with root " + root);
+ }
+ this.rootRelativePath = rootRel.equals(execPath) ? execPath : rootRel;
+ this.owner = Preconditions.checkNotNull(owner, path);
+ }
+
+ /**
+ * Constructs an artifact for the specified path, root and execPath. The root must be an ancestor
+ * of path, and execPath must be a non-absolute tail of path. Should only be called for testing.
+ *
+ * <p>In a source Artifact, the path tail after the root will be identical to the execPath, but
+ * the root will be orthogonal to execRoot.
+ * <pre>
+ * [path] == [/root/][execPath]
+ * </pre>
+ *
+ * <p>In a derived Artifact, the execPath will overlap with part of the root, which in turn will
+ * be below of the execRoot.
+ * <pre>
+ * [path] == [/root][pathTail] == [/execRoot][execPath] == [/execRoot][rootPrefix][pathTail]
+ * <pre>
+ */
+ @VisibleForTesting
+ public Artifact(Path path, Root root, PathFragment execPath) {
+ this(path, root, execPath, ArtifactOwner.NULL_OWNER);
+ }
+
+ /**
+ * Constructs a source or derived Artifact for the specified path and specified root. The root
+ * must be an ancestor of the path.
+ */
+ @VisibleForTesting // Only exists for testing.
+ public Artifact(Path path, Root root) {
+ this(path, root, root.getExecPath().getRelative(path.relativeTo(root.getPath())),
+ ArtifactOwner.NULL_OWNER);
+ }
+
+ /**
+ * Constructs a source or derived Artifact for the specified root-relative path and root.
+ */
+ @VisibleForTesting // Only exists for testing.
+ public Artifact(PathFragment rootRelativePath, Root root) {
+ this(root.getPath().getRelative(rootRelativePath), root,
+ root.getExecPath().getRelative(rootRelativePath), ArtifactOwner.NULL_OWNER);
+ }
+
+ /**
+ * Returns the location of this Artifact on the filesystem.
+ */
+ public final Path getPath() {
+ return path;
+ }
+
+ /**
+ * Returns the base file name of this artifact.
+ */
+ @Override
+ public final String getFilename() {
+ return getExecPath().getBaseName();
+ }
+
+ /**
+ * Returns the artifact owner. May be null.
+ */
+ @Nullable public final Label getOwner() {
+ return owner.getLabel();
+ }
+
+ /**
+ * Get the {@code LabelAndConfiguration} of the {@code ConfiguredTarget} that owns this artifact,
+ * if it was set. Otherwise, this should be a dummy value -- either {@link
+ * ArtifactOwner#NULL_OWNER} or a dummy owner set in tests. Such a dummy value should only occur
+ * for source artifacts if created without specifying the owner, or for special derived artifacts,
+ * such as target completion middleman artifacts, build info artifacts, and the like.
+ *
+ * When deserializing artifacts we end up with a dummy owner. In that case, it must be set using
+ * {@link #setArtifactOwner} before this method is called.
+ */
+ public final ArtifactOwner getArtifactOwner() {
+ Preconditions.checkState(owner != DESERIALIZED_MARKER_OWNER, this);
+ return owner;
+ }
+
+ /**
+ * Sets the artifact owner of this artifact. Should only be called for artifacts that were created
+ * through deserialization, and so their owner was unknown at the time of creation.
+ */
+ public final void setArtifactOwner(ArtifactOwner owner) {
+ if (this.owner == DESERIALIZED_MARKER_OWNER) {
+ // We tolerate multiple calls of this method to accommodate shared actions.
+ this.owner = Preconditions.checkNotNull(owner, this);
+ }
+ }
+
+ /**
+ * Returns the root beneath which this Artifact resides, if any. This may be one of the
+ * package-path entries (for source Artifacts), or one of the bin, genfiles or includes dirs
+ * (for derived Artifacts). It will always be an ancestor of getPath().
+ */
+ public final Root getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns the exec path of this Artifact. The exec path is a relative path
+ * that is suitable for accessing this artifact relative to the execution
+ * directory for this build.
+ */
+ public final PathFragment getExecPath() {
+ return execPath;
+ }
+
+ /**
+ * Returns true iff this is a source Artifact as determined by its path and
+ * root relationships. Note that this will report all Artifacts in the output
+ * tree, including in the include symlink tree, as non-source.
+ */
+ public final boolean isSourceArtifact() {
+ return execPath == rootRelativePath;
+ }
+
+ /**
+ * Returns true iff this is a middleman Artifact as determined by its root.
+ */
+ public final boolean isMiddlemanArtifact() {
+ return getRoot().isMiddlemanRoot();
+ }
+
+ /**
+ * Returns whether the artifact represents a Fileset.
+ */
+ public boolean isFileset() {
+ return false;
+ }
+
+ /**
+ * Returns true iff metadata cache must return constant metadata for the
+ * given artifact.
+ */
+ public boolean isConstantMetadata() {
+ return false;
+ }
+
+ /**
+ * Special artifact types.
+ *
+ * @see SpecialArtifact
+ */
+ static enum SpecialArtifactType {
+ FILESET,
+ CONSTANT_METADATA,
+ }
+
+ /**
+ * A special kind of artifact that either is a fileset or needs special metadata caching behavior.
+ *
+ * <p>We subclass {@link Artifact} instead of storing the special attributes inside in order
+ * to save memory. The proportion of artifacts that are special is very small, and by not having
+ * to keep around the attribute for the rest we save some memory.
+ */
+ @Immutable
+ @VisibleForTesting
+ public static final class SpecialArtifact extends Artifact {
+ private final SpecialArtifactType type;
+
+ SpecialArtifact(Path path, Root root, PathFragment execPath, ArtifactOwner owner,
+ SpecialArtifactType type) {
+ super(path, root, execPath, owner);
+ this.type = type;
+ }
+
+ @Override
+ public final boolean isFileset() {
+ return type == SpecialArtifactType.FILESET;
+ }
+
+ @Override
+ public boolean isConstantMetadata() {
+ return type == SpecialArtifactType.CONSTANT_METADATA;
+ }
+ }
+
+ /**
+ * Returns the relative path to this artifact relative to its root. (Useful
+ * when deriving output filenames from input files, etc.)
+ */
+ public final PathFragment getRootRelativePath() {
+ return rootRelativePath;
+ }
+
+ /**
+ * Returns this.getExecPath().getPathString().
+ */
+ @Override
+ @SkylarkCallable(name = "path", structField = true,
+ doc = "The execution path of this file, relative to the execution directory.")
+ public final String getExecPathString() {
+ return getExecPath().getPathString();
+ }
+
+ @SkylarkCallable(name = "short_path", structField = true,
+ doc = "The path of this file relative to its root.")
+ public final String getRootRelativePathString() {
+ return getRootRelativePath().getPathString();
+ }
+
+ /**
+ * Returns a pretty string representation of the path denoted by this artifact, suitable for use
+ * in user error messages. Artifacts beneath a root will be printed relative to that root; other
+ * artifacts will be printed as an absolute path.
+ *
+ * <p>(The toString method is intended for developer messages since its more informative.)
+ */
+ public final String prettyPrint() {
+ // toDetailString would probably be more useful to users, but lots of tests rely on the
+ // current values.
+ return rootRelativePath.toString();
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ if (!(other instanceof Artifact)) {
+ return false;
+ }
+ // We don't bother to check root in the equivalence relation, because we
+ // assume that 'root' is an ancestor of 'path', and that all possible roots
+ // are disjoint, so unless things are really screwed up, it's ok.
+ Artifact that = (Artifact) other;
+ return this.path.equals(that.path);
+ }
+
+ @Override
+ public final int compareTo(Artifact o) {
+ // The artifact factory ensures that there is a unique artifact for a given path.
+ return this.path.compareTo(o.path);
+ }
+
+ @Override
+ public final int hashCode() {
+ return path.hashCode();
+ }
+
+ @Override
+ public final String toString() {
+ return "Artifact:" + toDetailString();
+ }
+
+ /**
+ * Returns the root-part of a given path by trimming off the end specified by
+ * a given tail. Assumes that the tail is known to match, and simply relies on
+ * the segment lengths.
+ */
+ private static PathFragment trimTail(PathFragment path, PathFragment tail) {
+ return path.subFragment(0, path.segmentCount() - tail.segmentCount());
+ }
+
+ /**
+ * Returns a string representing the complete artifact path information.
+ */
+ public final String toDetailString() {
+ if (isSourceArtifact()) {
+ // Source Artifact: relPath == execPath, & real path is not under execRoot
+ return "[" + root + "]" + rootRelativePath;
+ } else {
+ // Derived Artifact: path and root are under execRoot
+ PathFragment execRoot = trimTail(path.asFragment(), execPath);
+ return "[[" + execRoot + "]" + root.getPath().asFragment().relativeTo(execRoot) + "]"
+ + rootRelativePath;
+ }
+ }
+
+ /**
+ * Serializes this artifact to a string that has enough data to reconstruct the artifact.
+ */
+ public final String serializeToString() {
+ // In theory, it should be enough to serialize execPath and rootRelativePath (which is a suffix
+ // of execPath). However, in practice there is code around that uses other attributes which
+ // needs cleaning up.
+ String result = execPath + " /" + rootRelativePath.toString().length();
+ if (getOwner() != null) {
+ result += " " + getOwner();
+ }
+ return result;
+ }
+
+ //---------------------------------------------------------------------------
+ // Static methods to assist in working with Artifacts
+
+ /**
+ * Formatter for execPath PathFragment output.
+ */
+ private static final Function<Artifact, PathFragment> EXEC_PATH_FORMATTER =
+ new Function<Artifact, PathFragment>() {
+ @Override
+ public PathFragment apply(Artifact input) {
+ return input.getExecPath();
+ }
+ };
+
+ private static final Function<Artifact, String> ROOT_RELATIVE_PATH_STRING =
+ new Function<Artifact, String>() {
+ @Override
+ public String apply(Artifact artifact) {
+ return artifact.getRootRelativePath().getPathString();
+ }
+ };
+
+ /**
+ * Converts a collection of artifacts into execution-time path strings, and
+ * adds those to a given collection. Middleman artifacts are ignored by this
+ * method.
+ */
+ public static void addExecPaths(Iterable<Artifact> artifacts, Collection<String> output) {
+ addNonMiddlemanArtifacts(artifacts, output, ActionInputHelper.EXEC_PATH_STRING_FORMATTER);
+ }
+
+ /**
+ * Converts a collection of artifacts into the outputs computed by
+ * outputFormatter and adds them to a given collection. Middleman artifacts
+ * are ignored.
+ */
+ static <E> void addNonMiddlemanArtifacts(Iterable<Artifact> artifacts,
+ Collection<? super E> output, Function<? super Artifact, E> outputFormatter) {
+ for (Artifact artifact : artifacts) {
+ if (MIDDLEMAN_FILTER.apply(artifact)) {
+ output.add(outputFormatter.apply(artifact));
+ }
+ }
+ }
+
+ /**
+ * Lazily converts artifacts into root-relative path strings. Middleman artifacts are ignored by
+ * this method.
+ */
+ public static Iterable<String> toRootRelativePaths(Iterable<Artifact> artifacts) {
+ return Iterables.transform(
+ Iterables.filter(artifacts, MIDDLEMAN_FILTER),
+ ROOT_RELATIVE_PATH_STRING);
+ }
+
+ /**
+ * Lazily converts artifacts into execution-time path strings. Middleman artifacts are ignored by
+ * this method.
+ */
+ public static Iterable<String> toExecPaths(Iterable<Artifact> artifacts) {
+ return ActionInputHelper.toExecPaths(Iterables.filter(artifacts, MIDDLEMAN_FILTER));
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path strings, and
+ * returns those as an immutable list. Middleman artifacts are ignored by this method.
+ */
+ public static List<String> asExecPaths(Iterable<Artifact> artifacts) {
+ return ImmutableList.copyOf(toExecPaths(artifacts));
+ }
+
+ /**
+ * Renders a collection of artifacts as execution-time paths and joins
+ * them into a single string. Middleman artifacts are ignored by this method.
+ */
+ public static String joinExecPaths(String delimiter, Iterable<Artifact> artifacts) {
+ return Joiner.on(delimiter).join(toExecPaths(artifacts));
+ }
+
+ /**
+ * Renders a collection of artifacts as root-relative paths and joins
+ * them into a single string. Middleman artifacts are ignored by this method.
+ */
+ public static String joinRootRelativePaths(String delimiter, Iterable<Artifact> artifacts) {
+ return Joiner.on(delimiter).join(toRootRelativePaths(artifacts));
+ }
+
+ /**
+ * Adds a collection of artifacts to a given collection, with
+ * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions expanded once.
+ */
+ public static void addExpandedArtifacts(Iterable<Artifact> artifacts,
+ Collection<? super Artifact> output, MiddlemanExpander middlemanExpander) {
+ addExpandedArtifacts(artifacts, output, Functions.<Artifact>identity(), middlemanExpander);
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path strings, and
+ * adds those to a given collection. Middleman artifacts for
+ * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions are expanded
+ * once.
+ */
+ @VisibleForTesting
+ public static void addExpandedExecPathStrings(Iterable<Artifact> artifacts,
+ Collection<String> output,
+ MiddlemanExpander middlemanExpander) {
+ addExpandedArtifacts(artifacts, output, ActionInputHelper.EXEC_PATH_STRING_FORMATTER,
+ middlemanExpander);
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path fragments, and
+ * adds those to a given collection. Middleman artifacts for
+ * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions are expanded
+ * once.
+ */
+ public static void addExpandedExecPaths(Iterable<Artifact> artifacts,
+ Collection<PathFragment> output, MiddlemanExpander middlemanExpander) {
+ addExpandedArtifacts(artifacts, output, EXEC_PATH_FORMATTER, middlemanExpander);
+ }
+
+ /**
+ * Converts a collection of artifacts into the outputs computed by
+ * outputFormatter and adds them to a given collection. Middleman artifacts
+ * are expanded once.
+ */
+ private static <E> void addExpandedArtifacts(Iterable<Artifact> artifacts,
+ Collection<? super E> output,
+ Function<? super Artifact, E> outputFormatter,
+ MiddlemanExpander middlemanExpander) {
+ for (Artifact artifact : artifacts) {
+ if (artifact.isMiddlemanArtifact()) {
+ expandMiddlemanArtifact(artifact, output, outputFormatter, middlemanExpander);
+ } else {
+ output.add(outputFormatter.apply(artifact));
+ }
+ }
+ }
+
+ private static <E> void expandMiddlemanArtifact(Artifact middleman,
+ Collection<? super E> output,
+ Function<? super Artifact, E> outputFormatter,
+ MiddlemanExpander middlemanExpander) {
+ Preconditions.checkArgument(middleman.isMiddlemanArtifact());
+ List<Artifact> artifacts = new ArrayList<>();
+ middlemanExpander.expand(middleman, artifacts);
+ for (Artifact artifact : artifacts) {
+ output.add(outputFormatter.apply(artifact));
+ }
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path strings, and
+ * returns those as a list. Middleman artifacts are expanded once. The
+ * returned list is mutable.
+ */
+ public static List<String> asExpandedExecPathStrings(Iterable<Artifact> artifacts,
+ MiddlemanExpander middlemanExpander) {
+ List<String> result = new ArrayList<>();
+ addExpandedExecPathStrings(artifacts, result, middlemanExpander);
+ return result;
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path fragments, and
+ * returns those as a list. Middleman artifacts are expanded once. The
+ * returned list is mutable.
+ */
+ public static List<PathFragment> asExpandedExecPaths(Iterable<Artifact> artifacts,
+ MiddlemanExpander middlemanExpander) {
+ List<PathFragment> result = new ArrayList<>();
+ addExpandedExecPaths(artifacts, result, middlemanExpander);
+ return result;
+ }
+
+ /**
+ * Converts a collection of artifacts into execution-time path strings with
+ * the root-break delimited with a colon ':', and adds those to a given list.
+ * <pre>
+ * Source: sourceRoot/rootRelative => :rootRelative
+ * Derived: execRoot/rootPrefix/rootRelative => rootPrefix:rootRelative
+ * </pre>
+ */
+ public static void addRootPrefixedExecPaths(Iterable<Artifact> artifacts,
+ List<String> output) {
+ for (Artifact artifact : artifacts) {
+ output.add(asRootPrefixedExecPath(artifact));
+ }
+ }
+
+ /**
+ * Convenience method to filter the files to build for a certain filetype.
+ *
+ * @param artifacts the files to filter
+ * @param allowedType the allowed filetype
+ * @return all members of filesToBuild that are of one of the
+ * allowed filetypes
+ */
+ public static List<Artifact> filterFiles(Iterable<Artifact> artifacts, FileType allowedType) {
+ List<Artifact> filesToBuild = new ArrayList<>();
+ for (Artifact artifact : artifacts) {
+ if (allowedType.matches(artifact.getFilename())) {
+ filesToBuild.add(artifact);
+ }
+ }
+ return filesToBuild;
+ }
+
+ @VisibleForTesting
+ static String asRootPrefixedExecPath(Artifact artifact) {
+ PathFragment execPath = artifact.getExecPath();
+ PathFragment rootRel = artifact.getRootRelativePath();
+ if (execPath.equals(rootRel)) {
+ return ":" + rootRel.getPathString();
+ } else { //if (execPath.endsWith(rootRel)) {
+ PathFragment rootPrefix = trimTail(execPath, rootRel);
+ return rootPrefix.getPathString() + ":" + rootRel.getPathString();
+ }
+ }
+
+ /**
+ * Converts artifacts into their exec paths. Returns an immutable list.
+ */
+ public static List<PathFragment> asPathFragments(Iterable<Artifact> artifacts) {
+ return ImmutableList.copyOf(Iterables.transform(artifacts, EXEC_PATH_FORMATTER));
+ }
+
+ static final ArtifactOwner DESERIALIZED_MARKER_OWNER = new ArtifactOwner() {
+ @Override
+ public Label getLabel() {
+ return null;
+ }};
+}