blob: 4b7adbc40622a24d9406850ac5c09a86199e4b25 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.actions;
16
17import com.google.common.annotations.VisibleForTesting;
18import com.google.common.base.Function;
19import com.google.common.base.Functions;
20import com.google.common.base.Joiner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.base.Predicate;
22import com.google.common.collect.ImmutableList;
23import com.google.common.collect.Iterables;
Lukacs Berki2fd05062016-02-19 15:15:57 +000024import com.google.common.collect.Ordering;
Rumou Duan33bab462016-04-25 17:55:12 +000025import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000026import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010027import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Googler263f57d2015-06-26 00:37:49 +000028import com.google.devtools.build.lib.shell.ShellUtils;
John Field585d1a02015-12-16 16:03:52 +000029import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
30import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
Dmitry Lomov34cdae32016-06-28 16:13:35 +000031import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory;
John Field585d1a02015-12-16 16:03:52 +000032import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideauab049e02016-02-17 16:13:46 +000033import com.google.devtools.build.lib.syntax.EvalUtils;
Vladimir Moskva7f0cd622017-02-16 13:48:37 +000034import com.google.devtools.build.lib.syntax.EvalUtils.ComparisonException;
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +000035import com.google.devtools.build.lib.syntax.Printer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036import com.google.devtools.build.lib.util.FileType;
Mark Schaller6df81792015-12-10 18:47:47 +000037import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.vfs.Path;
39import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import java.util.ArrayList;
41import java.util.Collection;
Lukacs Berki8ae34f12015-04-10 14:54:19 +000042import java.util.Comparator;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import java.util.List;
Kristina Chodorowf57730b2017-01-05 19:43:03 +000044import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import javax.annotation.Nullable;
46
47/**
48 * An Artifact represents a file used by the build system, whether it's a source
49 * file or a derived (output) file. Not all Artifacts have a corresponding
Kristina Chodorowf57730b2017-01-05 19:43:03 +000050 * FileTarget object in the <code>build.lib.packages</code> API: for example,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051 * low-level intermediaries internal to a given rule, such as a Java class files
52 * or C++ object files. However all FileTargets have a corresponding Artifact.
53 *
Kristina Chodorowf57730b2017-01-05 19:43:03 +000054 * <p>In any given call to SkyframeExecutor#buildArtifacts(), no two Artifacts in the
Laszlo Csomor8539a122016-09-20 15:40:42 +000055 * action graph may refer to the same path.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056 *
57 * <p>Artifacts generally fall into two classifications, source and derived, but
58 * there exist a few other cases that are fuzzy and difficult to classify. The
59 * following cases exist:
60 * <ul>
61 * <li>Well-formed source Artifacts will have null generating Actions and a root
62 * that is orthogonal to execRoot. (With the root coming from the package path.)
63 * <li>Well-formed derived Artifacts will have non-null generating Actions, and
64 * a root that is below execRoot.
65 * <li>Symlinked include source Artifacts under the output/include tree will
66 * appear to be derived artifacts with null generating Actions.
67 * <li>Some derived Artifacts, mostly in the genfiles tree and mostly discovered
68 * during include validation, will also have null generating Actions.
69 * </ul>
70 *
Michael Thvedte3b1cb72016-02-08 23:32:27 +000071 * In the usual case, an Artifact represents a single file. However, an Artifact may
72 * also represent the following:
73 * <ul>
Rumou Duana77f32c2016-04-13 21:59:21 +000074 * <li>A TreeArtifact, which is a directory containing a tree of unknown {@link Artifact}s.
Michael Thvedte3b1cb72016-02-08 23:32:27 +000075 * In the future, Actions will be able to examine these files as inputs and declare them as outputs
76 * at execution time, but this is not yet implemented. This is used for Actions where
77 * the inputs and/or outputs might not be discoverable except during Action execution.
78 * <li>A directory of unknown contents, but not a TreeArtifact.
79 * This is a legacy facility and should not be used by any new rule implementations.
80 * In particular, the file system cache integrity checks fail for directories.
81 * <li>An 'aggregating middleman' special Artifact, which may be expanded using a
Michael Thvedt434e68e2016-02-09 00:57:46 +000082 * {@link ArtifactExpander} at Action execution time. This is used by a handful of rules to save
Michael Thvedte3b1cb72016-02-08 23:32:27 +000083 * memory.
84 * <li>A 'constant metadata' special Artifact. These represent real files, changes to which are
85 * ignored by the build system. They are useful for files which change frequently but do not affect
86 * the result of a build, such as timestamp files.
87 * <li>A 'Fileset' special Artifact. This is a legacy type of Artifact and should not be used
88 * by new rule implementations.
89 * </ul>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090 * <p>This class is "theoretically" final; it should not be subclassed except by
91 * {@link SpecialArtifact}.
92 */
93@Immutable
94@SkylarkModule(name = "File",
Dmitry Lomov34cdae32016-06-28 16:13:35 +000095 category = SkylarkModuleCategory.BUILTIN,
Kristina Chodorow740cb392016-06-21 14:36:58 +000096 doc = "<p>This type represents a file used by the build system. It can be "
97 + "either a source file or a derived file produced by a rule.</p>"
98 + "<p>The File constructor is private, so you cannot call it directly to create new "
99 + "Files. If you have a Skylark rule that needs to create a new File, you might need to "
100 + "add the label to the attrs (if it's an input) or the outputs (if it's an output). Then "
101 + "you can access the File through the rule's <a href='ctx.html'>context</a>. You can "
102 + "also use <a href='ctx.html#new_file'>ctx.new_file</a> to create a new file in the rule "
103 + "implementation.</p>")
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000104public class Artifact
Rumou Duana77f32c2016-04-13 21:59:21 +0000105 implements FileType.HasFilename, ActionInput, SkylarkValue, Comparable<Object> {
Lukacs Berki8ae34f12015-04-10 14:54:19 +0000106
107 /**
108 * Compares artifact according to their exec paths. Sorts null values first.
109 */
110 public static final Comparator<Artifact> EXEC_PATH_COMPARATOR = new Comparator<Artifact>() {
111 @Override
112 public int compare(Artifact a, Artifact b) {
113 if (a == b) {
114 return 0;
115 } else if (a == null) {
116 return -1;
117 } else if (b == null) {
118 return -1;
119 } else {
120 return a.execPath.compareTo(b.execPath);
121 }
122 }
123 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100124
Pedro Liberal Fernandeze6736532016-10-05 18:56:31 +0000125 /** Compares artifacts according to their root relative paths. */
126 public static final Comparator<Artifact> ROOT_RELATIVE_PATH_COMPARATOR =
127 new Comparator<Artifact>() {
128 @Override
129 public int compare(Artifact lhs, Artifact rhs) {
130 return lhs.getRootRelativePath().compareTo(rhs.getRootRelativePath());
131 }
132 };
133
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000134 @Override
135 public int compareTo(Object o) {
136 if (o instanceof Artifact) {
137 return EXEC_PATH_COMPARATOR.compare(this, (Artifact) o);
138 }
Vladimir Moskva7f0cd622017-02-16 13:48:37 +0000139 throw new ComparisonException("Cannot compare artifact with " + EvalUtils.getDataTypeName(o));
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000140 }
141
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000142
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 /** An object that can expand middleman artifacts. */
Michael Thvedt434e68e2016-02-09 00:57:46 +0000144 public interface ArtifactExpander {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100145
146 /**
Michael Thvedt434e68e2016-02-09 00:57:46 +0000147 * Expands the given artifact, and populates "output" with the result.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100148 *
Michael Thvedt434e68e2016-02-09 00:57:46 +0000149 * <p>{@code artifact.isMiddlemanArtifact() || artifact.isTreeArtifact()} must be true.
150 * Only aggregating middlemen and tree artifacts are expanded.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100151 */
Rumou Duana77f32c2016-04-13 21:59:21 +0000152 void expand(Artifact artifact, Collection<? super Artifact> output);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 }
154
155 public static final ImmutableList<Artifact> NO_ARTIFACTS = ImmutableList.of();
156
157 /**
158 * A Predicate that evaluates to true if the Artifact is not a middleman artifact.
159 */
160 public static final Predicate<Artifact> MIDDLEMAN_FILTER = new Predicate<Artifact>() {
161 @Override
162 public boolean apply(Artifact input) {
163 return !input.isMiddlemanArtifact();
164 }
165 };
166
Rumou Duan009e0592016-07-27 15:14:29 +0000167 /**
168 * A Predicate that evaluates to true if the Artifact <b>is</b> a tree artifact.
169 */
170 public static final Predicate<Artifact> IS_TREE_ARTIFACT = new Predicate<Artifact>() {
171 @Override
172 public boolean apply(Artifact input) {
173 return input.isTreeArtifact();
174 }
175 };
176
Googler0e170462016-06-16 00:49:06 +0000177 private final int hashCode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 private final Path path;
179 private final Root root;
180 private final PathFragment execPath;
181 private final PathFragment rootRelativePath;
Lukacs Berkide62b4e2016-02-26 13:13:39 +0000182 private final ArtifactOwner owner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183
184 /**
185 * Constructs an artifact for the specified path, root and execPath. The root must be an ancestor
186 * of path, and execPath must be a non-absolute tail of path. Outside of testing, this method
187 * should only be called by ArtifactFactory. The ArtifactOwner may be null.
188 *
189 * <p>In a source Artifact, the path tail after the root will be identical to the execPath, but
190 * the root will be orthogonal to execRoot.
191 * <pre>
192 * [path] == [/root/][execPath]
193 * </pre>
194 *
195 * <p>In a derived Artifact, the execPath will overlap with part of the root, which in turn will
Kristina Chodorowf57730b2017-01-05 19:43:03 +0000196 * be below the execRoot.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100197 * <pre>
198 * [path] == [/root][pathTail] == [/execRoot][execPath] == [/execRoot][rootPrefix][pathTail]
Kristina Chodorowf57730b2017-01-05 19:43:03 +0000199 * </pre>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 */
201 @VisibleForTesting
202 public Artifact(Path path, Root root, PathFragment execPath, ArtifactOwner owner) {
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000203 if (root == null || !path.startsWith(root.getPath())) {
Janak8f1a9322015-05-04 19:29:08 +0000204 throw new IllegalArgumentException(root + ": illegal root for " + path
205 + " (execPath: " + execPath + ")");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100206 }
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000207 if (execPath == null || execPath.isAbsolute() || !path.asFragment().endsWith(execPath)) {
Janak8f1a9322015-05-04 19:29:08 +0000208 throw new IllegalArgumentException(execPath + ": illegal execPath for " + path
209 + " (root: " + root + ")");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 }
Googler0e170462016-06-16 00:49:06 +0000211 this.hashCode = path.hashCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100212 this.path = path;
213 this.root = root;
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000214 this.execPath = execPath;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100215 // These two lines establish the invariant that
216 // execPath == rootRelativePath <=> execPath.equals(rootRelativePath)
217 // This is important for isSourceArtifact.
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000218 PathFragment rootRel = path.relativeTo(root.getPath());
219 if (!execPath.endsWith(rootRel)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100220 throw new IllegalArgumentException(execPath + ": illegal execPath doesn't end with "
221 + rootRel + " at " + path + " with root " + root);
222 }
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000223 this.rootRelativePath = rootRel.equals(execPath) ? execPath : rootRel;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100224 this.owner = Preconditions.checkNotNull(owner, path);
225 }
226
227 /**
228 * Constructs an artifact for the specified path, root and execPath. The root must be an ancestor
229 * of path, and execPath must be a non-absolute tail of path. Should only be called for testing.
230 *
231 * <p>In a source Artifact, the path tail after the root will be identical to the execPath, but
232 * the root will be orthogonal to execRoot.
233 * <pre>
234 * [path] == [/root/][execPath]
235 * </pre>
236 *
237 * <p>In a derived Artifact, the execPath will overlap with part of the root, which in turn will
238 * be below of the execRoot.
239 * <pre>
240 * [path] == [/root][pathTail] == [/execRoot][execPath] == [/execRoot][rootPrefix][pathTail]
241 * <pre>
242 */
243 @VisibleForTesting
244 public Artifact(Path path, Root root, PathFragment execPath) {
245 this(path, root, execPath, ArtifactOwner.NULL_OWNER);
246 }
247
248 /**
249 * Constructs a source or derived Artifact for the specified path and specified root. The root
250 * must be an ancestor of the path.
251 */
252 @VisibleForTesting // Only exists for testing.
253 public Artifact(Path path, Root root) {
254 this(path, root, root.getExecPath().getRelative(path.relativeTo(root.getPath())),
255 ArtifactOwner.NULL_OWNER);
256 }
257
258 /**
259 * Constructs a source or derived Artifact for the specified root-relative path and root.
260 */
261 @VisibleForTesting // Only exists for testing.
262 public Artifact(PathFragment rootRelativePath, Root root) {
263 this(root.getPath().getRelative(rootRelativePath), root,
264 root.getExecPath().getRelative(rootRelativePath), ArtifactOwner.NULL_OWNER);
265 }
266
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 public final Path getPath() {
268 return path;
269 }
270
Rumou Duana77f32c2016-04-13 21:59:21 +0000271 public boolean hasParent() {
272 return getParent() != null;
273 }
274
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100275 /**
Rumou Duana77f32c2016-04-13 21:59:21 +0000276 * Returns the parent Artifact containing this Artifact. Artifacts without parents shall
277 * return null.
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000278 */
Rumou Duana77f32c2016-04-13 21:59:21 +0000279 @Nullable public Artifact getParent() {
280 return null;
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000281 }
282
283 /**
Googler91cfbff2015-05-26 16:35:27 +0000284 * Returns the directory name of this artifact, similar to dirname(1).
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000285 *
Googler91cfbff2015-05-26 16:35:27 +0000286 * <p> The directory name is always a relative path to the execution directory.
287 */
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000288 @SkylarkCallable(name = "dirname", structField = true,
289 doc = "The name of the directory containing this file.")
Googler91cfbff2015-05-26 16:35:27 +0000290 public final String getDirname() {
291 PathFragment parent = getExecPath().getParentDirectory();
Googler91cfbff2015-05-26 16:35:27 +0000292 return (parent == null) ? "/" : parent.getSafePathString();
293 }
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000294
Googler91cfbff2015-05-26 16:35:27 +0000295 /**
296 * Returns the base file name of this artifact, similar to basename(1).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100297 */
298 @Override
Googler91cfbff2015-05-26 16:35:27 +0000299 @SkylarkCallable(name = "basename", structField = true,
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000300 doc = "The base file name of this file.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100301 public final String getFilename() {
302 return getExecPath().getBaseName();
303 }
304
Paul Roberts8c443ef2016-10-18 02:04:25 +0000305 @SkylarkCallable(name = "extension", structField = true, doc = "The file extension of this file.")
306 public final String getExtension() {
307 return getExecPath().getFileExtension();
308 }
309
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100310 /**
311 * Returns the artifact owner. May be null.
312 */
313 @Nullable public final Label getOwner() {
314 return owner.getLabel();
315 }
316
317 /**
318 * Get the {@code LabelAndConfiguration} of the {@code ConfiguredTarget} that owns this artifact,
319 * if it was set. Otherwise, this should be a dummy value -- either {@link
320 * ArtifactOwner#NULL_OWNER} or a dummy owner set in tests. Such a dummy value should only occur
321 * for source artifacts if created without specifying the owner, or for special derived artifacts,
322 * such as target completion middleman artifacts, build info artifacts, and the like.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100323 */
324 public final ArtifactOwner getArtifactOwner() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100325 return owner;
326 }
327
Dmitry Lomov8091b2b2016-03-02 01:24:56 +0000328 @SkylarkCallable(name = "owner", structField = true, allowReturnNones = true,
Adam Michaelf51d5282016-10-06 23:20:20 +0000329 doc = "A label of a target that produces this File."
Dmitry Lomov8091b2b2016-03-02 01:24:56 +0000330 )
331 public Label getOwnerLabel() {
332 return owner.getLabel();
333 }
334
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100336 * Returns the root beneath which this Artifact resides, if any. This may be one of the
337 * package-path entries (for source Artifacts), or one of the bin, genfiles or includes dirs
338 * (for derived Artifacts). It will always be an ancestor of getPath().
339 */
Dmitry Lomovddda06d2016-01-19 15:58:11 +0000340 @SkylarkCallable(name = "root", structField = true,
341 doc = "The root beneath which this file resides."
342 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100343 public final Root getRoot() {
344 return root;
345 }
346
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100347 public final PathFragment getExecPath() {
348 return execPath;
349 }
350
351 /**
Rumou Duana77f32c2016-04-13 21:59:21 +0000352 * Returns the path of this Artifact relative to this containing Artifact. Since
353 * ordinary Artifacts correspond to only one Artifact -- itself -- for ordinary Artifacts,
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000354 * this just returns the empty path. For special Artifacts, throws
Rumou Duana77f32c2016-04-13 21:59:21 +0000355 * {@link UnsupportedOperationException}. See also {@link Artifact#getParentRelativePath()}.
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000356 */
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000357 public PathFragment getParentRelativePath() {
358 return PathFragment.EMPTY_FRAGMENT;
359 }
360
361 /**
John Caterd7928422017-01-03 20:06:59 +0000362 * Returns true iff this is a source Artifact as determined by its path and root relationships.
363 * Note that this will report all Artifacts in the output tree, including in the include symlink
364 * tree, as non-source.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100365 */
John Caterd7928422017-01-03 20:06:59 +0000366 @SkylarkCallable(
367 name = "is_source",
368 structField = true,
369 doc = "Returns true if this is a source file, i.e. it is not generated."
370 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100371 public final boolean isSourceArtifact() {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000372 return execPath == rootRelativePath;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100373 }
374
375 /**
376 * Returns true iff this is a middleman Artifact as determined by its root.
377 */
378 public final boolean isMiddlemanArtifact() {
379 return getRoot().isMiddlemanRoot();
380 }
381
382 /**
Rumou Duana77f32c2016-04-13 21:59:21 +0000383 * Returns true iff this is a TreeArtifact representing a directory tree containing Artifacts.
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000384 */
Rumou Duan62c57c72016-11-03 20:47:46 +0000385 // TODO(rduan): Document this Skylark method once TreeArtifact is no longer experimental.
386 @SkylarkCallable(name = "is_directory", structField = true, documented = false)
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000387 public boolean isTreeArtifact() {
388 return false;
389 }
390
391 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100392 * Returns whether the artifact represents a Fileset.
393 */
394 public boolean isFileset() {
395 return false;
396 }
397
398 /**
399 * Returns true iff metadata cache must return constant metadata for the
400 * given artifact.
401 */
402 public boolean isConstantMetadata() {
403 return false;
404 }
405
406 /**
407 * Special artifact types.
408 *
409 * @see SpecialArtifact
410 */
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000411 @VisibleForTesting
Kristina Chodorowf57730b2017-01-05 19:43:03 +0000412 public enum SpecialArtifactType {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100413 FILESET,
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000414 TREE,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100415 CONSTANT_METADATA,
416 }
417
418 /**
419 * A special kind of artifact that either is a fileset or needs special metadata caching behavior.
420 *
421 * <p>We subclass {@link Artifact} instead of storing the special attributes inside in order
422 * to save memory. The proportion of artifacts that are special is very small, and by not having
423 * to keep around the attribute for the rest we save some memory.
424 */
425 @Immutable
426 @VisibleForTesting
427 public static final class SpecialArtifact extends Artifact {
428 private final SpecialArtifactType type;
429
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000430 @VisibleForTesting
431 public SpecialArtifact(Path path, Root root, PathFragment execPath, ArtifactOwner owner,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100432 SpecialArtifactType type) {
433 super(path, root, execPath, owner);
434 this.type = type;
435 }
436
437 @Override
438 public final boolean isFileset() {
439 return type == SpecialArtifactType.FILESET;
440 }
441
442 @Override
443 public boolean isConstantMetadata() {
444 return type == SpecialArtifactType.CONSTANT_METADATA;
445 }
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000446
447 @Override
448 public boolean isTreeArtifact() {
449 return type == SpecialArtifactType.TREE;
450 }
451
452 @Override
Rumou Duana77f32c2016-04-13 21:59:21 +0000453 public boolean hasParent() {
454 return false;
455 }
456
457 @Override
458 @Nullable
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000459 public Artifact getParent() {
Rumou Duana77f32c2016-04-13 21:59:21 +0000460 return null;
461 }
462
463 @Override
464 @Nullable
465 public PathFragment getParentRelativePath() {
466 return null;
467 }
468 }
469
470 /**
471 * A special kind of artifact that represents a concrete file created at execution time under
472 * its associated TreeArtifact.
473 *
474 * <p> TreeFileArtifacts should be only created during execution time inside some special actions
475 * to support action inputs and outputs that are unpredictable at analysis time.
476 * TreeFileArtifacts should not be created directly by any rules at analysis time.
477 *
478 * <p>We subclass {@link Artifact} instead of storing the extra fields directly inside in order
479 * to save memory. The proportion of TreeFileArtifacts is very small, and by not having to keep
480 * around the extra fields for the rest we save some memory.
481 */
482 @Immutable
483 public static final class TreeFileArtifact extends Artifact {
484 private final Artifact parentTreeArtifact;
485 private final PathFragment parentRelativePath;
486
487 /**
488 * Constructs a TreeFileArtifact with the given parent-relative path under the given parent
489 * TreeArtifact. The {@link ArtifactOwner} of the TreeFileArtifact is the {@link ArtifactOwner}
490 * of the parent TreeArtifact.
491 */
492 TreeFileArtifact(Artifact parent, PathFragment parentRelativePath) {
493 this(parent, parentRelativePath, parent.getArtifactOwner());
494 }
495
496 /**
497 * Constructs a TreeFileArtifact with the given parent-relative path under the given parent
498 * TreeArtifact, owned by the given {@code artifactOwner}.
499 */
500 TreeFileArtifact(Artifact parent, PathFragment parentRelativePath,
501 ArtifactOwner artifactOwner) {
502 super(
503 parent.getPath().getRelative(parentRelativePath),
504 parent.getRoot(),
505 parent.getExecPath().getRelative(parentRelativePath),
506 artifactOwner);
507 Preconditions.checkState(
508 parent.isTreeArtifact(),
509 "The parent of TreeFileArtifact (parent-relative path: %s) is not a TreeArtifact: %s",
510 parentRelativePath,
511 parent);
Rumou Duan54907572017-02-10 17:24:03 +0000512 Preconditions.checkState(
513 parentRelativePath.isNormalized() && !parentRelativePath.isAbsolute(),
514 "%s is not a proper normalized relative path",
515 parentRelativePath);
Rumou Duana77f32c2016-04-13 21:59:21 +0000516 this.parentTreeArtifact = parent;
517 this.parentRelativePath = parentRelativePath;
518 }
519
520 @Override
521 public Artifact getParent() {
522 return parentTreeArtifact;
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000523 }
524
525 @Override
526 public PathFragment getParentRelativePath() {
Rumou Duana77f32c2016-04-13 21:59:21 +0000527 return parentRelativePath;
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000528 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100529 }
530
531 /**
532 * Returns the relative path to this artifact relative to its root. (Useful
533 * when deriving output filenames from input files, etc.)
534 */
535 public final PathFragment getRootRelativePath() {
536 return rootRelativePath;
537 }
538
539 /**
Kristina Chodorow4dd18ec2016-05-04 13:36:48 +0000540 * For targets in external repositories, this returns the path the artifact live at in the
541 * runfiles tree. For local targets, it returns the rootRelativePath.
542 */
543 public final PathFragment getRunfilesPath() {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000544 PathFragment relativePath = rootRelativePath;
545 if (relativePath.segmentCount() > 1
546 && relativePath.getSegment(0).equals(Label.EXTERNAL_PATH_PREFIX)) {
547 // Turn external/repo/foo into ../repo/foo.
548 relativePath = relativePath.relativeTo(Label.EXTERNAL_PATH_PREFIX);
549 relativePath = new PathFragment("..").getRelative(relativePath);
550 }
551 return relativePath;
Kristina Chodorow4dd18ec2016-05-04 13:36:48 +0000552 }
553
Kristina Chodorowd15c5be2016-05-10 22:20:12 +0000554 @SkylarkCallable(
555 name = "short_path",
556 structField = true,
557 doc =
558 "The path of this file relative to its root. This excludes the aforementioned "
559 + "<i>root</i>, i.e. configuration-specific fragments of the path. This is also the "
560 + "path under which the file is mapped if it's in the runfiles of a binary."
561 )
562 public final String getRunfilesPathString() {
563 return getRunfilesPath().getPathString();
564 }
565
Kristina Chodorow4dd18ec2016-05-04 13:36:48 +0000566 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100567 * Returns this.getExecPath().getPathString().
568 */
569 @Override
Dan Fabulichc43f4c42016-02-17 13:44:13 +0000570 @SkylarkCallable(
571 name = "path",
572 structField = true,
573 doc =
574 "The execution path of this file, relative to the workspace's execution directory. It "
575 + "consists of two parts, an optional first part called the <i>root</i> (see also the "
576 + "<a href=\"root.html\">root</a> module), and the second part which is the "
577 + "<code>short_path</code>. The root may be empty, which it usually is for "
578 + "non-generated files. For generated files it usually contains a "
579 + "configuration-specific path fragment that encodes things like the target CPU "
580 + "architecture that was used while building said file. Use the "
581 + "<code>short_path</code> for the path under which the file is mapped if it's in the "
582 + "runfiles of a binary."
583 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100584 public final String getExecPathString() {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000585 return getExecPath().getPathString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100586 }
587
Googler263f57d2015-06-26 00:37:49 +0000588 /*
589 * Returns getExecPathString escaped for potential use in a shell command.
590 */
591 public final String getShellEscapedExecPathString() {
592 return ShellUtils.shellEscape(getExecPathString());
593 }
594
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100595 public final String getRootRelativePathString() {
596 return getRootRelativePath().getPathString();
597 }
598
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 public final String prettyPrint() {
600 // toDetailString would probably be more useful to users, but lots of tests rely on the
601 // current values.
602 return rootRelativePath.toString();
603 }
604
605 @Override
606 public final boolean equals(Object other) {
607 if (!(other instanceof Artifact)) {
608 return false;
609 }
610 // We don't bother to check root in the equivalence relation, because we
Lukacs Berki8ae34f12015-04-10 14:54:19 +0000611 // assume that no root is an ancestor of another one.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100612 Artifact that = (Artifact) other;
Kristina Chodorowf57730b2017-01-05 19:43:03 +0000613 return Objects.equals(this.path, that.path);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100614 }
615
616 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100617 public final int hashCode() {
Googler0e170462016-06-16 00:49:06 +0000618 // This is just path.hashCode(). We cache a copy in the Artifact object to reduce LLC misses
619 // during operations which build a HashSet out of many Artifacts. This is a slight loss for
620 // memory but saves ~1% overall CPU in some real builds.
621 return hashCode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100622 }
623
624 @Override
625 public final String toString() {
626 return "Artifact:" + toDetailString();
627 }
628
629 /**
630 * Returns the root-part of a given path by trimming off the end specified by
631 * a given tail. Assumes that the tail is known to match, and simply relies on
632 * the segment lengths.
633 */
634 private static PathFragment trimTail(PathFragment path, PathFragment tail) {
635 return path.subFragment(0, path.segmentCount() - tail.segmentCount());
636 }
637
638 /**
639 * Returns a string representing the complete artifact path information.
640 */
641 public final String toDetailString() {
642 if (isSourceArtifact()) {
643 // Source Artifact: relPath == execPath, & real path is not under execRoot
644 return "[" + root + "]" + rootRelativePath;
645 } else {
646 // Derived Artifact: path and root are under execRoot
647 PathFragment execRoot = trimTail(path.asFragment(), execPath);
648 return "[[" + execRoot + "]" + root.getPath().asFragment().relativeTo(execRoot) + "]"
649 + rootRelativePath;
650 }
651 }
652
653 /**
654 * Serializes this artifact to a string that has enough data to reconstruct the artifact.
655 */
656 public final String serializeToString() {
657 // In theory, it should be enough to serialize execPath and rootRelativePath (which is a suffix
658 // of execPath). However, in practice there is code around that uses other attributes which
659 // needs cleaning up.
660 String result = execPath + " /" + rootRelativePath.toString().length();
661 if (getOwner() != null) {
662 result += " " + getOwner();
663 }
664 return result;
665 }
666
667 //---------------------------------------------------------------------------
668 // Static methods to assist in working with Artifacts
669
670 /**
671 * Formatter for execPath PathFragment output.
672 */
Rumou Duana77f32c2016-04-13 21:59:21 +0000673 private static final Function<Artifact, PathFragment> EXEC_PATH_FORMATTER =
674 new Function<Artifact, PathFragment>() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100675 @Override
Rumou Duana77f32c2016-04-13 21:59:21 +0000676 public PathFragment apply(Artifact input) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100677 return input.getExecPath();
678 }
679 };
680
Rumou Duana77f32c2016-04-13 21:59:21 +0000681 public static final Function<Artifact, String> ROOT_RELATIVE_PATH_STRING =
682 new Function<Artifact, String>() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100683 @Override
Rumou Duana77f32c2016-04-13 21:59:21 +0000684 public String apply(Artifact artifact) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100685 return artifact.getRootRelativePath().getPathString();
686 }
687 };
688
Rumou Duana77f32c2016-04-13 21:59:21 +0000689 public static final Function<Artifact, String> ABSOLUTE_PATH_STRING =
690 new Function<Artifact, String>() {
Googler5e34a3d2015-12-03 15:17:55 +0000691 @Override
Rumou Duana77f32c2016-04-13 21:59:21 +0000692 public String apply(Artifact artifact) {
Googler5e34a3d2015-12-03 15:17:55 +0000693 return artifact.getPath().getPathString();
694 }
695 };
696
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100697 /**
698 * Converts a collection of artifacts into execution-time path strings, and
699 * adds those to a given collection. Middleman artifacts are ignored by this
700 * method.
701 */
702 public static void addExecPaths(Iterable<Artifact> artifacts, Collection<String> output) {
703 addNonMiddlemanArtifacts(artifacts, output, ActionInputHelper.EXEC_PATH_STRING_FORMATTER);
704 }
705
706 /**
707 * Converts a collection of artifacts into the outputs computed by
708 * outputFormatter and adds them to a given collection. Middleman artifacts
709 * are ignored.
710 */
711 static <E> void addNonMiddlemanArtifacts(Iterable<Artifact> artifacts,
712 Collection<? super E> output, Function<? super Artifact, E> outputFormatter) {
713 for (Artifact artifact : artifacts) {
714 if (MIDDLEMAN_FILTER.apply(artifact)) {
715 output.add(outputFormatter.apply(artifact));
716 }
717 }
718 }
719
720 /**
Googler5e34a3d2015-12-03 15:17:55 +0000721 * Lazily converts artifacts into absolute path strings. Middleman artifacts are ignored by
722 * this method.
723 */
724 public static Iterable<String> toAbsolutePaths(Iterable<Artifact> artifacts) {
725 return Iterables.transform(
726 Iterables.filter(artifacts, MIDDLEMAN_FILTER),
727 ABSOLUTE_PATH_STRING);
728 }
729
730 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100731 * Lazily converts artifacts into root-relative path strings. Middleman artifacts are ignored by
732 * this method.
733 */
734 public static Iterable<String> toRootRelativePaths(Iterable<Artifact> artifacts) {
735 return Iterables.transform(
736 Iterables.filter(artifacts, MIDDLEMAN_FILTER),
737 ROOT_RELATIVE_PATH_STRING);
738 }
739
740 /**
741 * Lazily converts artifacts into execution-time path strings. Middleman artifacts are ignored by
742 * this method.
743 */
744 public static Iterable<String> toExecPaths(Iterable<Artifact> artifacts) {
745 return ActionInputHelper.toExecPaths(Iterables.filter(artifacts, MIDDLEMAN_FILTER));
746 }
747
748 /**
749 * Converts a collection of artifacts into execution-time path strings, and
750 * returns those as an immutable list. Middleman artifacts are ignored by this method.
751 */
752 public static List<String> asExecPaths(Iterable<Artifact> artifacts) {
753 return ImmutableList.copyOf(toExecPaths(artifacts));
754 }
755
756 /**
757 * Renders a collection of artifacts as execution-time paths and joins
758 * them into a single string. Middleman artifacts are ignored by this method.
759 */
760 public static String joinExecPaths(String delimiter, Iterable<Artifact> artifacts) {
761 return Joiner.on(delimiter).join(toExecPaths(artifacts));
762 }
763
764 /**
765 * Renders a collection of artifacts as root-relative paths and joins
766 * them into a single string. Middleman artifacts are ignored by this method.
767 */
768 public static String joinRootRelativePaths(String delimiter, Iterable<Artifact> artifacts) {
769 return Joiner.on(delimiter).join(toRootRelativePaths(artifacts));
770 }
771
772 /**
773 * Adds a collection of artifacts to a given collection, with
774 * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions expanded once.
775 */
776 public static void addExpandedArtifacts(Iterable<Artifact> artifacts,
Rumou Duana77f32c2016-04-13 21:59:21 +0000777 Collection<? super Artifact> output, ArtifactExpander artifactExpander) {
778 addExpandedArtifacts(artifacts, output, Functions.<Artifact>identity(), artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100779 }
780
781 /**
782 * Converts a collection of artifacts into execution-time path strings, and
783 * adds those to a given collection. Middleman artifacts for
784 * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions are expanded
785 * once.
786 */
787 @VisibleForTesting
788 public static void addExpandedExecPathStrings(Iterable<Artifact> artifacts,
789 Collection<String> output,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000790 ArtifactExpander artifactExpander) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100791 addExpandedArtifacts(artifacts, output, ActionInputHelper.EXEC_PATH_STRING_FORMATTER,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000792 artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100793 }
794
795 /**
796 * Converts a collection of artifacts into execution-time path fragments, and
797 * adds those to a given collection. Middleman artifacts for
798 * {@link MiddlemanType#AGGREGATING_MIDDLEMAN} middleman actions are expanded
799 * once.
800 */
801 public static void addExpandedExecPaths(Iterable<Artifact> artifacts,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000802 Collection<PathFragment> output, ArtifactExpander artifactExpander) {
803 addExpandedArtifacts(artifacts, output, EXEC_PATH_FORMATTER, artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100804 }
805
806 /**
807 * Converts a collection of artifacts into the outputs computed by
808 * outputFormatter and adds them to a given collection. Middleman artifacts
809 * are expanded once.
810 */
Michael Thvedt434e68e2016-02-09 00:57:46 +0000811 private static <E> void addExpandedArtifacts(Iterable<? extends Artifact> artifacts,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100812 Collection<? super E> output,
Rumou Duana77f32c2016-04-13 21:59:21 +0000813 Function<? super Artifact, E> outputFormatter,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000814 ArtifactExpander artifactExpander) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100815 for (Artifact artifact : artifacts) {
Michael Thvedt434e68e2016-02-09 00:57:46 +0000816 if (artifact.isMiddlemanArtifact() || artifact.isTreeArtifact()) {
817 expandArtifact(artifact, output, outputFormatter, artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100818 } else {
819 output.add(outputFormatter.apply(artifact));
820 }
821 }
822 }
823
Michael Thvedt434e68e2016-02-09 00:57:46 +0000824 private static <E> void expandArtifact(Artifact middleman,
825 Collection<? super E> output,
Rumou Duana77f32c2016-04-13 21:59:21 +0000826 Function<? super Artifact, E> outputFormatter,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000827 ArtifactExpander artifactExpander) {
828 Preconditions.checkArgument(middleman.isMiddlemanArtifact() || middleman.isTreeArtifact());
Rumou Duana77f32c2016-04-13 21:59:21 +0000829 List<Artifact> artifacts = new ArrayList<>();
Michael Thvedt434e68e2016-02-09 00:57:46 +0000830 artifactExpander.expand(middleman, artifacts);
Rumou Duana77f32c2016-04-13 21:59:21 +0000831 for (Artifact artifact : artifacts) {
Michael Thvedt434e68e2016-02-09 00:57:46 +0000832 output.add(outputFormatter.apply(artifact));
833 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100834 }
835
836 /**
837 * Converts a collection of artifacts into execution-time path strings, and
838 * returns those as a list. Middleman artifacts are expanded once. The
839 * returned list is mutable.
840 */
841 public static List<String> asExpandedExecPathStrings(Iterable<Artifact> artifacts,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000842 ArtifactExpander artifactExpander) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100843 List<String> result = new ArrayList<>();
Michael Thvedt434e68e2016-02-09 00:57:46 +0000844 addExpandedExecPathStrings(artifacts, result, artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100845 return result;
846 }
847
848 /**
849 * Converts a collection of artifacts into execution-time path fragments, and
850 * returns those as a list. Middleman artifacts are expanded once. The
851 * returned list is mutable.
852 */
853 public static List<PathFragment> asExpandedExecPaths(Iterable<Artifact> artifacts,
Michael Thvedt434e68e2016-02-09 00:57:46 +0000854 ArtifactExpander artifactExpander) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100855 List<PathFragment> result = new ArrayList<>();
Michael Thvedt434e68e2016-02-09 00:57:46 +0000856 addExpandedExecPaths(artifacts, result, artifactExpander);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100857 return result;
858 }
859
860 /**
861 * Converts a collection of artifacts into execution-time path strings with
862 * the root-break delimited with a colon ':', and adds those to a given list.
863 * <pre>
864 * Source: sourceRoot/rootRelative => :rootRelative
865 * Derived: execRoot/rootPrefix/rootRelative => rootPrefix:rootRelative
866 * </pre>
867 */
868 public static void addRootPrefixedExecPaths(Iterable<Artifact> artifacts,
869 List<String> output) {
870 for (Artifact artifact : artifacts) {
871 output.add(asRootPrefixedExecPath(artifact));
872 }
873 }
874
875 /**
876 * Convenience method to filter the files to build for a certain filetype.
877 *
878 * @param artifacts the files to filter
879 * @param allowedType the allowed filetype
880 * @return all members of filesToBuild that are of one of the
881 * allowed filetypes
882 */
883 public static List<Artifact> filterFiles(Iterable<Artifact> artifacts, FileType allowedType) {
884 List<Artifact> filesToBuild = new ArrayList<>();
885 for (Artifact artifact : artifacts) {
886 if (allowedType.matches(artifact.getFilename())) {
887 filesToBuild.add(artifact);
888 }
889 }
890 return filesToBuild;
891 }
892
893 @VisibleForTesting
894 static String asRootPrefixedExecPath(Artifact artifact) {
895 PathFragment execPath = artifact.getExecPath();
896 PathFragment rootRel = artifact.getRootRelativePath();
897 if (execPath.equals(rootRel)) {
898 return ":" + rootRel.getPathString();
899 } else { //if (execPath.endsWith(rootRel)) {
900 PathFragment rootPrefix = trimTail(execPath, rootRel);
901 return rootPrefix.getPathString() + ":" + rootRel.getPathString();
902 }
903 }
904
905 /**
906 * Converts artifacts into their exec paths. Returns an immutable list.
907 */
Rumou Duana77f32c2016-04-13 21:59:21 +0000908 public static List<PathFragment> asPathFragments(Iterable<? extends Artifact> artifacts) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100909 return ImmutableList.copyOf(Iterables.transform(artifacts, EXEC_PATH_FORMATTER));
910 }
911
Lukacs Berki2fd05062016-02-19 15:15:57 +0000912 /**
913 * Returns the exec paths of the input artifacts in alphabetical order.
914 */
915 public static ImmutableList<PathFragment> asSortedPathFragments(Iterable<Artifact> input) {
916 return Ordering.natural().immutableSortedCopy(Iterables.transform(
917 input, EXEC_PATH_FORMATTER));
918 }
919
920
Francois-Rene Rideau6c10eac2015-09-17 19:17:20 +0000921 @Override
922 public boolean isImmutable() {
923 return true;
924 }
925
926 @Override
927 public void write(Appendable buffer, char quotationMark) {
928 Printer.append(buffer, toString()); // TODO(bazel-team): implement a readable representation
929 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100930}