blob: 2c44a32adbb4d00799cead8aef24c2c07996b4e6 [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
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.analysis;
16
17import com.google.common.annotations.VisibleForTesting;
18import com.google.common.base.Function;
19import com.google.common.base.Preconditions;
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000020import com.google.common.collect.ImmutableList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.common.collect.Iterables;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.devtools.build.lib.actions.Artifact;
23import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
24import com.google.devtools.build.lib.collect.nestedset.NestedSet;
25import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
26import com.google.devtools.build.lib.collect.nestedset.Order;
Brian Silverman13891f62015-06-22 16:46:40 +000027import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.events.Event;
29import com.google.devtools.build.lib.events.EventHandler;
30import com.google.devtools.build.lib.events.Location;
31import com.google.devtools.build.lib.packages.Type;
Brian Silverman13891f62015-06-22 16:46:40 +000032import com.google.devtools.build.lib.syntax.SkylarkCallable;
33import com.google.devtools.build.lib.syntax.SkylarkModule;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import com.google.devtools.build.lib.vfs.Path;
35import com.google.devtools.build.lib.vfs.PathFragment;
36
37import java.io.BufferedReader;
38import java.io.IOException;
39import java.io.InputStreamReader;
40import java.util.Collections;
41import java.util.HashMap;
42import java.util.Iterator;
43import java.util.LinkedHashMap;
44import java.util.Map;
45import java.util.Map.Entry;
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000046import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047
48import javax.annotation.Nullable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049
50/**
51 * An object that encapsulates runfiles. Conceptually, the runfiles are a map of paths to files,
52 * forming a symlink tree.
53 *
54 * <p>In order to reduce memory consumption, this map is not explicitly stored here, but instead as
55 * a combination of four parts: artifacts placed at their root-relative paths, source tree symlinks,
56 * root symlinks (outside of the source tree), and artifacts included as parts of "pruning
57 * manifests" (see {@link PruningManifest}).
58 */
59@Immutable
Brian Silverman13891f62015-06-22 16:46:40 +000060@SkylarkModule(name = "runfiles", doc = "An interface for a set of runfiles.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061public final class Runfiles {
Lukacs Berki5ab30d62015-03-11 22:06:04 +000062 private static final Function<SymlinkEntry, Artifact> TO_ARTIFACT =
63 new Function<SymlinkEntry, Artifact>() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064 @Override
Lukacs Berki5ab30d62015-03-11 22:06:04 +000065 public Artifact apply(SymlinkEntry input) {
66 return input.getArtifact();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067 }
68 };
69
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000070 private static final EmptyFilesSupplier DUMMY_EMPTY_FILES_SUPPLIER =
71 new EmptyFilesSupplier() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010072 @Override
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000073 public Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths) {
74 return ImmutableList.of();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075 }
76 };
77
Lukacs Berki5ab30d62015-03-11 22:06:04 +000078 /**
79 * An entry in the runfiles map.
80 */
81 //
82 // O intrepid fixer or bugs and implementor of features, dare not to add a .equals() method
83 // to this class, lest you condemn yourself, or a fellow other developer to spending two
84 // delightful hours in a fancy hotel on a Chromebook that is utterly unsuitable for Java
85 // development to figure out what went wrong, just like I just did.
86 //
87 // The semantics of the symlinks nested set dictates that later entries overwrite earlier
88 // ones. However, the semantics of nested sets dictate that if there are duplicate entries, they
89 // are only returned once in the iterator.
90 //
91 // These two things, innocent when taken alone, result in the effect that when there are three
92 // entries for the same path, the first one and the last one the same, and the middle one
93 // different, the *middle* one will take effect: the middle one overrides the first one, and the
94 // first one prevents the last one from appearing on the iterator.
95 //
96 // The lack of a .equals() method prevents this by making the first entry in the above case not
97 // equals to the third one if they are not the same instance (which they almost never are)
98 //
99 // Goodnight, prince(ss)?, and sweet dreams.
100 private static final class SymlinkEntry {
101 private final PathFragment path;
102 private final Artifact artifact;
103
104 private SymlinkEntry(PathFragment path, Artifact artifact) {
105 this.path = path;
106 this.artifact = artifact;
107 }
108
109 public PathFragment getPath() {
110 return path;
111 }
112
113 public Artifact getArtifact() {
114 return artifact;
115 }
116 }
117
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 // It is important to declare this *after* the DUMMY_SYMLINK_EXPANDER to avoid NPEs
119 public static final Runfiles EMPTY = new Builder().build();
120
Kristina Chodorowb579b942015-03-02 15:51:58 +0000121 /**
122 * The directory to put all runfiles under.
123 *
124 * <p>Using "foo" will put runfiles under &lt;target&gt;.runfiles/foo.</p>
125 */
126 private final String suffix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127
128 /**
129 * The artifacts that should *always* be present in the runfiles directory. These are
130 * differentiated from the artifacts that may or may not be included by a pruning manifest
131 * (see {@link PruningManifest} below).
132 *
133 * <p>This collection may not include any middlemen. These artifacts will be placed at a location
134 * that corresponds to the root-relative path of each artifact. It's possible for several
135 * artifacts to have the same root-relative path, in which case the last one will win.
136 */
137 private final NestedSet<Artifact> unconditionalArtifacts;
138
139 /**
140 * A map of symlinks that should be present in the runfiles directory. In general, the symlink can
141 * be determined from the artifact by using the root-relative path, so this should only be used
142 * for cases where that isn't possible.
143 *
144 * <p>This may include runfiles symlinks from the root of the runfiles tree.
145 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000146 private final NestedSet<SymlinkEntry> symlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147
148 /**
149 * A map of symlinks that should be present above the runfiles directory. These are useful for
150 * certain rule types like AppEngine apps which have root level config files outside of the
151 * regular source tree.
152 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000153 private final NestedSet<SymlinkEntry> rootSymlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100154
155 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000156 * Interface used for adding empty files to the runfiles at the last minute. Mainly to support
157 * python-related rules adding __init__.py files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000159 public interface EmptyFilesSupplier {
160 /** Calculate additional empty files to add based on the existing manifest paths. */
161 Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths);
162 }
163
164 /** Generates extra (empty file) inputs. */
165 private final EmptyFilesSupplier emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100166
167 /**
168 * Defines a set of artifacts that may or may not be included in the runfiles directory and
169 * a manifest file that makes that determination. These are applied on top of any artifacts
170 * specified in {@link #unconditionalArtifacts}.
171 *
172 * <p>The incentive behind this is to enable execution-phase "pruning" of runfiles. Anything
173 * set in unconditionalArtifacts is hard-set in Blaze's analysis phase, and thus unchangeable in
174 * response to execution phase results. This isn't always convenient. For example, say we have an
175 * action that consumes a set of "possible" runtime dependencies for a source file, parses that
176 * file for "import a.b.c" statements, and outputs a manifest of the actual dependencies that are
177 * referenced and thus really needed. This can reduce the size of the runfiles set, but we can't
178 * use this information until the manifest output is available.
179 *
180 * <p>Only artifacts present in the candidate set AND the manifest output make it into the
181 * runfiles tree. The candidate set requirement guarantees that analysis-time dependencies are a
182 * superset of the pruned dependencies, so undeclared inclusions (which can break build
183 * correctness) aren't possible.
184 */
185 public static class PruningManifest {
186 private final NestedSet<Artifact> candidateRunfiles;
187 private final Artifact manifestFile;
188
189 /**
190 * Creates a new pruning manifest.
191 *
192 * @param candidateRunfiles set of possible artifacts that the manifest file may reference
193 * @param manifestFile the manifest file, expected to be a newline-separated list of
194 * source tree root-relative paths (i.e. "my/package/myfile.txt"). Anything that can't be
195 * resolved back to an entry in candidateRunfiles is ignored and will *not* make it into
196 * the runfiles tree.
197 */
198 public PruningManifest(NestedSet<Artifact> candidateRunfiles, Artifact manifestFile) {
199 this.candidateRunfiles = candidateRunfiles;
200 this.manifestFile = manifestFile;
201 }
202
203 public NestedSet<Artifact> getCandidateRunfiles() {
204 return candidateRunfiles;
205 }
206
207 public Artifact getManifestFile() {
208 return manifestFile;
209 }
210 }
211
212 /**
213 * The pruning manifests that should be applied to these runfiles.
214 */
215 private final NestedSet<PruningManifest> pruningManifests;
216
Kristina Chodorowb579b942015-03-02 15:51:58 +0000217 private Runfiles(String suffix,
218 NestedSet<Artifact> artifacts,
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000219 NestedSet<SymlinkEntry> symlinks,
220 NestedSet<SymlinkEntry> rootSymlinks,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100221 NestedSet<PruningManifest> pruningManifests,
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000222 EmptyFilesSupplier emptyFilesSupplier) {
Ulf Adams4226be22015-09-03 17:00:54 +0000223 this.suffix = suffix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100224 this.unconditionalArtifacts = Preconditions.checkNotNull(artifacts);
225 this.symlinks = Preconditions.checkNotNull(symlinks);
226 this.rootSymlinks = Preconditions.checkNotNull(rootSymlinks);
227 this.pruningManifests = Preconditions.checkNotNull(pruningManifests);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000228 this.emptyFilesSupplier = Preconditions.checkNotNull(emptyFilesSupplier);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100229 }
230
231 /**
Kristina Chodorowb579b942015-03-02 15:51:58 +0000232 * Returns the runfiles' suffix.
233 */
234 public String getSuffix() {
235 return suffix;
236 }
237
238 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 * Returns the artifacts that are unconditionally included in the runfiles (as opposed to
240 * pruning manifest candidates, which may or may not be included).
241 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242 public NestedSet<Artifact> getUnconditionalArtifacts() {
243 return unconditionalArtifacts;
244 }
245
246 /**
247 * Returns the artifacts that are unconditionally included in the runfiles (as opposed to
248 * pruning manifest candidates, which may or may not be included). Middleman artifacts are
249 * excluded.
250 */
251 public Iterable<Artifact> getUnconditionalArtifactsWithoutMiddlemen() {
252 return Iterables.filter(unconditionalArtifacts, Artifact.MIDDLEMAN_FILTER);
253 }
254
255 /**
256 * Returns the collection of runfiles as artifacts, including both unconditional artifacts
Michajlo Matijkiwdee781c2015-05-22 23:25:34 +0000257 * and pruning manifest candidates.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100258 */
Brian Silverman13891f62015-06-22 16:46:40 +0000259 @SkylarkCallable(
260 name = "files",
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000261 doc = "Returns the set of runfiles as files",
Brian Silverman13891f62015-06-22 16:46:40 +0000262 structField = true
263 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100264 public NestedSet<Artifact> getArtifacts() {
265 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
266 allArtifacts.addAll(unconditionalArtifacts.toCollection());
267 for (PruningManifest manifest : getPruningManifests()) {
268 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
269 }
270 return allArtifacts.build();
271 }
272
273 /**
274 * Returns the collection of runfiles as artifacts, including both unconditional artifacts
275 * and pruning manifest candidates. Middleman artifacts are excluded.
276 */
277 public Iterable<Artifact> getArtifactsWithoutMiddlemen() {
278 return Iterables.filter(getArtifacts(), Artifact.MIDDLEMAN_FILTER);
279 }
280
281 /**
282 * Returns the symlinks.
283 */
Brian Silverman13891f62015-06-22 16:46:40 +0000284 @SkylarkCallable(name = "symlinks", doc = "Returns the set of symlinks", structField = true)
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000285 public NestedSet<SymlinkEntry> getSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100286 return symlinks;
287 }
288
289 /**
290 * Returns the symlinks as a map from path fragment to artifact.
291 */
292 public Map<PathFragment, Artifact> getSymlinksAsMap() {
293 return entriesToMap(symlinks);
294 }
295
296 /**
297 * @param eventHandler Used for throwing an error if we have an obscuring runlink.
298 * May be null, in which case obscuring symlinks are silently discarded.
299 * @param location Location for reporter. Ignored if reporter is null.
300 * @param workingManifest Manifest to be checked for obscuring symlinks.
301 * @return map of source file names mapped to their location on disk.
302 */
Michajlo Matijkiw0e30a5f2015-04-15 17:28:35 +0000303 @VisibleForTesting
304 static Map<PathFragment, Artifact> filterListForObscuringSymlinks(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100305 EventHandler eventHandler, Location location, Map<PathFragment, Artifact> workingManifest) {
306 Map<PathFragment, Artifact> newManifest = new HashMap<>();
307
308 outer:
309 for (Iterator<Entry<PathFragment, Artifact>> i = workingManifest.entrySet().iterator();
310 i.hasNext(); ) {
311 Entry<PathFragment, Artifact> entry = i.next();
312 PathFragment source = entry.getKey();
313 Artifact symlink = entry.getValue();
314 // drop nested entries; warn if this changes anything
315 int n = source.segmentCount();
316 for (int j = 1; j < n; ++j) {
317 PathFragment prefix = source.subFragment(0, n - j);
318 Artifact ancestor = workingManifest.get(prefix);
319 if (ancestor != null) {
320 // This is an obscuring symlink, so just drop it and move on if there's no reporter.
321 if (eventHandler == null) {
322 continue outer;
323 }
324 PathFragment suffix = source.subFragment(n - j, n);
325 Path viaAncestor = ancestor.getPath().getRelative(suffix);
326 Path expected = symlink.getPath();
327 if (!viaAncestor.equals(expected)) {
328 eventHandler.handle(Event.warn(location, "runfiles symlink " + source + " -> "
329 + expected + " obscured by " + prefix + " -> " + ancestor.getPath()));
330 }
331 continue outer;
332 }
333 }
334 newManifest.put(entry.getKey(), entry.getValue());
335 }
336 return newManifest;
337 }
338
339 /**
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000340 * Returns the symlinks as a map from PathFragment to Artifact.
341 *
342 * @param eventHandler Used for throwing an error if we have an obscuring runlink within the
343 * normal source tree entries. May be null, in which case obscuring symlinks are silently
344 * discarded.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 * @param location Location for eventHandler warnings. Ignored if eventHandler is null.
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000346 * @return Map<PathFragment, Artifact> path fragment to artifact, of normal source tree entries
347 * and elements that live outside the source tree. Null values represent empty input files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100348 */
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000349 public Map<PathFragment, Artifact> getRunfilesInputs(EventHandler eventHandler,
350 Location location) throws IOException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100351 Map<PathFragment, Artifact> manifest = getSymlinksAsMap();
352 // Add unconditional artifacts (committed to inclusion on construction of runfiles).
353 for (Artifact artifact : getUnconditionalArtifactsWithoutMiddlemen()) {
Michajlo Matijkiw94cca592015-03-13 20:37:00 +0000354 manifest.put(artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100355 }
356
357 // Add conditional artifacts (only included if they appear in a pruning manifest).
358 for (Runfiles.PruningManifest pruningManifest : getPruningManifests()) {
359 // This map helps us convert from source tree root-relative paths back to artifacts.
360 Map<String, Artifact> allowedRunfiles = new HashMap<>();
361 for (Artifact artifact : pruningManifest.getCandidateRunfiles()) {
362 allowedRunfiles.put(artifact.getRootRelativePath().getPathString(), artifact);
363 }
364 BufferedReader reader = new BufferedReader(
365 new InputStreamReader(pruningManifest.getManifestFile().getPath().getInputStream()));
366 String line;
367 while ((line = reader.readLine()) != null) {
368 Artifact artifact = allowedRunfiles.get(line);
369 if (artifact != null) {
Michajlo Matijkiw94cca592015-03-13 20:37:00 +0000370 manifest.put(artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100371 }
372 }
373 }
374
375 manifest = filterListForObscuringSymlinks(eventHandler, location, manifest);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000376
377 // TODO(bazel-team): Create /dev/null-like Artifact to avoid nulls?
378 for (PathFragment extraPath : emptyFilesSupplier.getExtraPaths(manifest.keySet())) {
379 manifest.put(extraPath, null);
380 }
Michajlo Matijkiw0e30a5f2015-04-15 17:28:35 +0000381
Kristina Chodorowb579b942015-03-02 15:51:58 +0000382 PathFragment path = new PathFragment(suffix);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100383 Map<PathFragment, Artifact> result = new HashMap<>();
384 for (Map.Entry<PathFragment, Artifact> entry : manifest.entrySet()) {
385 result.put(path.getRelative(entry.getKey()), entry.getValue());
386 }
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000387
388 // Finally add symlinks outside the source tree on top of everything else.
389 for (Map.Entry<PathFragment, Artifact> entry : getRootSymlinksAsMap().entrySet()) {
390 PathFragment mappedPath = entry.getKey();
391 Artifact mappedArtifact = entry.getValue();
392 if (result.put(mappedPath, mappedArtifact) != null) {
393 // Emit warning if we overwrote something and we're capable of emitting warnings.
394 if (eventHandler != null) {
395 eventHandler.handle(Event.warn(location, "overwrote " + mappedPath + " symlink mapping "
396 + "with root symlink to " + mappedArtifact));
397 }
398 }
399 }
400
401 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100402 }
403
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100404 /**
405 * Returns the root symlinks.
406 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000407 public NestedSet<SymlinkEntry> getRootSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100408 return rootSymlinks;
409 }
410
411 /**
412 * Returns the root symlinks.
413 */
414 public Map<PathFragment, Artifact> getRootSymlinksAsMap() {
415 return entriesToMap(rootSymlinks);
416 }
417
418 /**
419 * Returns the unified map of path fragments to artifacts, taking both artifacts and symlinks into
420 * account.
421 */
422 public Map<PathFragment, Artifact> asMapWithoutRootSymlinks() {
423 Map<PathFragment, Artifact> result = entriesToMap(symlinks);
424 // If multiple artifacts have the same root-relative path, the last one in the list will win.
425 // That is because the runfiles tree cannot contain the same artifact for different
426 // configurations, because it only uses root-relative paths.
427 for (Artifact artifact : Iterables.filter(unconditionalArtifacts, Artifact.MIDDLEMAN_FILTER)) {
428 result.put(artifact.getRootRelativePath(), artifact);
429 }
430 return result;
431 }
432
433 /**
434 * Returns the pruning manifests specified for this runfiles tree.
435 */
436 public NestedSet<PruningManifest> getPruningManifests() {
437 return pruningManifests;
438 }
439
440 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000441 * Returns the manifest expander specified for this runfiles tree.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100442 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000443 private EmptyFilesSupplier getEmptyFilesProvider() {
444 return emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100445 }
446
447 /**
448 * Returns the unified map of path fragments to artifacts, taking into account artifacts,
449 * symlinks, and pruning manifest candidates. The returned set is guaranteed to be a (not
450 * necessarily strict) superset of the actual runfiles tree created at execution time.
451 */
452 public NestedSet<Artifact> getAllArtifacts() {
453 if (isEmpty()) {
454 return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
455 }
456 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
457 allArtifacts
458 .addTransitive(unconditionalArtifacts)
459 .addAll(Iterables.transform(symlinks, TO_ARTIFACT))
460 .addAll(Iterables.transform(rootSymlinks, TO_ARTIFACT));
461 for (PruningManifest manifest : getPruningManifests()) {
462 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
463 }
464 return allArtifacts.build();
465 }
466
467 /**
468 * Returns if there are no runfiles.
469 */
470 public boolean isEmpty() {
471 return unconditionalArtifacts.isEmpty() && symlinks.isEmpty() && rootSymlinks.isEmpty() &&
472 pruningManifests.isEmpty();
473 }
474
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000475 private static Map<PathFragment, Artifact> entriesToMap(Iterable<SymlinkEntry> entrySet) {
476 Map<PathFragment, Artifact> map = new LinkedHashMap<>();
477 for (SymlinkEntry entry : entrySet) {
478 map.put(entry.getPath(), entry.getArtifact());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100479 }
480 return map;
481 }
482
483 /**
484 * Builder for Runfiles objects.
485 */
486 public static final class Builder {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000487
488 private String suffix;
489
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100490 /**
491 * This must be COMPILE_ORDER because {@link #asMapWithoutRootSymlinks} overwrites earlier
492 * entries with later ones, so we want a post-order iteration.
493 */
494 private NestedSetBuilder<Artifact> artifactsBuilder =
495 NestedSetBuilder.compileOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000496 private NestedSetBuilder<SymlinkEntry> symlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100497 NestedSetBuilder.stableOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000498 private NestedSetBuilder<SymlinkEntry> rootSymlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100499 NestedSetBuilder.stableOrder();
500 private NestedSetBuilder<PruningManifest> pruningManifestsBuilder =
501 NestedSetBuilder.stableOrder();
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000502 private EmptyFilesSupplier emptyFilesSupplier = DUMMY_EMPTY_FILES_SUPPLIER;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100503
504 /**
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000505 * Only used for Runfiles.EMPTY.
506 */
Ulf Adams4226be22015-09-03 17:00:54 +0000507 private Builder() {
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000508 this.suffix = "";
509 }
510
511 public Builder(String suffix) {
512 this.suffix = suffix;
513 }
514
515 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100516 * Builds a new Runfiles object.
517 */
518 public Runfiles build() {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000519 return new Runfiles(suffix, artifactsBuilder.build(), symlinksBuilder.build(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100520 rootSymlinksBuilder.build(), pruningManifestsBuilder.build(),
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000521 emptyFilesSupplier);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100522 }
523
524 /**
525 * Adds an artifact to the internal collection of artifacts.
526 */
527 public Builder addArtifact(Artifact artifact) {
528 Preconditions.checkNotNull(artifact);
529 artifactsBuilder.add(artifact);
530 return this;
531 }
532
533 /**
534 * Adds several artifacts to the internal collection.
535 */
536 public Builder addArtifacts(Iterable<Artifact> artifacts) {
537 for (Artifact artifact : artifacts) {
538 addArtifact(artifact);
539 }
540 return this;
541 }
542
543
544 /**
Carmi Grushko75665632015-08-27 23:19:26 +0000545 * @deprecated Use {@link #addTransitiveArtifacts} instead, to prevent increased memory use.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100546 */
547 @Deprecated
548 public Builder addArtifacts(NestedSet<Artifact> artifacts) {
549 // Do not delete this method, or else addArtifacts(Iterable) calls with a NestedSet argument
550 // will not be flagged.
551 Iterable<Artifact> it = artifacts;
552 addArtifacts(it);
553 return this;
554 }
Carmi Grushko75665632015-08-27 23:19:26 +0000555
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100556 /**
557 * Adds a nested set to the internal collection.
558 */
559 public Builder addTransitiveArtifacts(NestedSet<Artifact> artifacts) {
560 artifactsBuilder.addTransitive(artifacts);
561 return this;
562 }
563
564 /**
565 * Adds a symlink.
566 */
567 public Builder addSymlink(PathFragment link, Artifact target) {
568 Preconditions.checkNotNull(link);
569 Preconditions.checkNotNull(target);
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000570 symlinksBuilder.add(new SymlinkEntry(link, target));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100571 return this;
572 }
573
574 /**
575 * Adds several symlinks.
576 */
577 public Builder addSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000578 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
579 symlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
580 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100581 return this;
582 }
583
584 /**
585 * Adds several symlinks as a NestedSet.
586 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000587 public Builder addSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100588 symlinksBuilder.addTransitive(symlinks);
589 return this;
590 }
591
592 /**
593 * Adds several root symlinks.
594 */
595 public Builder addRootSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000596 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
597 rootSymlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
598 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 return this;
600 }
601
602 /**
603 * Adds several root symlinks as a NestedSet.
604 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000605 public Builder addRootSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100606 rootSymlinksBuilder.addTransitive(symlinks);
607 return this;
608 }
609
610 /**
611 * Adds a pruning manifest. See {@link PruningManifest} for an explanation.
612 */
613 public Builder addPruningManifest(PruningManifest manifest) {
614 pruningManifestsBuilder.add(manifest);
615 return this;
616 }
617
618 /**
619 * Adds several pruning manifests as a NestedSet. See {@link PruningManifest} for an
620 * explanation.
621 */
622 public Builder addPruningManifests(NestedSet<PruningManifest> manifests) {
623 pruningManifestsBuilder.addTransitive(manifests);
624 return this;
625 }
626
627 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000628 * Specify a function that can create additional manifest entries based on the input entries,
629 * see {@link EmptyFilesSupplier} for more details.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100630 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000631 public Builder setEmptyFilesSupplier(EmptyFilesSupplier supplier) {
632 emptyFilesSupplier = Preconditions.checkNotNull(supplier);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100633 return this;
634 }
635
636 /**
637 * Merges runfiles from a given runfiles support.
638 *
639 * @param runfilesSupport the runfiles support to be merged in
640 */
641 public Builder merge(@Nullable RunfilesSupport runfilesSupport) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100642 if (runfilesSupport == null) {
643 return this;
644 }
645 // TODO(bazel-team): We may be able to remove this now.
646 addArtifact(runfilesSupport.getRunfilesMiddleman());
Lukacs Berki1f820792015-03-06 18:16:10 +0000647 merge(runfilesSupport.getRunfiles());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100648 return this;
649 }
650
651 /**
652 * Adds the runfiles for a particular target and visits the transitive closure of "srcs",
653 * "deps" and "data", collecting all of their respective runfiles.
654 */
655 public Builder addRunfiles(RuleContext ruleContext,
656 Function<TransitiveInfoCollection, Runfiles> mapping) {
657 Preconditions.checkNotNull(mapping);
658 Preconditions.checkNotNull(ruleContext);
659 addDataDeps(ruleContext);
660 addNonDataDeps(ruleContext, mapping);
661 return this;
662 }
663
664 /**
665 * Adds the files specified by a mapping from the transitive info collection to the runfiles.
666 *
667 * <p>Dependencies in {@code srcs} and {@code deps} are considered.
668 */
669 public Builder add(RuleContext ruleContext,
670 Function<TransitiveInfoCollection, Runfiles> mapping) {
671 Preconditions.checkNotNull(ruleContext);
672 Preconditions.checkNotNull(mapping);
673 for (TransitiveInfoCollection dep : getNonDataDeps(ruleContext)) {
674 Runfiles runfiles = mapping.apply(dep);
675 if (runfiles != null) {
676 merge(runfiles);
677 }
678 }
679
680 return this;
681 }
682
683 /**
684 * Collects runfiles from data dependencies of a target.
685 */
686 public Builder addDataDeps(RuleContext ruleContext) {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000687 addTargets(getPrerequisites(ruleContext, "data", Mode.DATA), RunfilesProvider.DATA_RUNFILES);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100688 return this;
689 }
690
691 /**
692 * Collects runfiles from "srcs" and "deps" of a target.
693 */
694 public Builder addNonDataDeps(RuleContext ruleContext,
695 Function<TransitiveInfoCollection, Runfiles> mapping) {
696 for (TransitiveInfoCollection target : getNonDataDeps(ruleContext)) {
697 addTargetExceptFileTargets(target, mapping);
698 }
699 return this;
700 }
701
702 public Builder addTargets(Iterable<? extends TransitiveInfoCollection> targets,
703 Function<TransitiveInfoCollection, Runfiles> mapping) {
704 for (TransitiveInfoCollection target : targets) {
705 addTarget(target, mapping);
706 }
707 return this;
708 }
709
710 public Builder addTarget(TransitiveInfoCollection target,
711 Function<TransitiveInfoCollection, Runfiles> mapping) {
712 return addTargetIncludingFileTargets(target, mapping);
713 }
714
715 private Builder addTargetExceptFileTargets(TransitiveInfoCollection target,
716 Function<TransitiveInfoCollection, Runfiles> mapping) {
717 Runfiles runfiles = mapping.apply(target);
718 if (runfiles != null) {
719 merge(runfiles);
720 }
721
722 return this;
723 }
724
725 private Builder addTargetIncludingFileTargets(TransitiveInfoCollection target,
726 Function<TransitiveInfoCollection, Runfiles> mapping) {
727 if (target.getProvider(RunfilesProvider.class) == null
728 && mapping == RunfilesProvider.DATA_RUNFILES) {
729 // RuleConfiguredTarget implements RunfilesProvider, so this will only be called on
730 // FileConfiguredTarget instances.
731 // TODO(bazel-team): This is a terrible hack. We should be able to make this go away
732 // by implementing RunfilesProvider on FileConfiguredTarget. We'd need to be mindful
733 // of the memory use, though, since we have a whole lot of FileConfiguredTarget instances.
734 addTransitiveArtifacts(target.getProvider(FileProvider.class).getFilesToBuild());
735 return this;
736 }
737
738 return addTargetExceptFileTargets(target, mapping);
739 }
740
741 /**
742 * Adds symlinks to given artifacts at their exec paths.
743 */
744 public Builder addSymlinksToArtifacts(Iterable<Artifact> artifacts) {
745 for (Artifact artifact : artifacts) {
746 addSymlink(artifact.getExecPath(), artifact);
747 }
748 return this;
749 }
750
751 /**
752 * Add the other {@link Runfiles} object transitively.
753 */
754 public Builder merge(Runfiles runfiles) {
755 return merge(runfiles, true);
756 }
757
758 /**
759 * Add the other {@link Runfiles} object transitively, but don't merge
760 * pruning manifests.
761 */
762 public Builder mergeExceptPruningManifests(Runfiles runfiles) {
763 return merge(runfiles, false);
764 }
765
766 /**
767 * Add the other {@link Runfiles} object transitively, with the option to include or exclude
768 * pruning manifests in the merge.
769 */
770 private Builder merge(Runfiles runfiles, boolean includePruningManifests) {
Ulf Adams4226be22015-09-03 17:00:54 +0000771 if (runfiles.isEmpty()) {
772 return this;
773 }
774 // The suffix should be the same within any blaze build, except for the EMPTY runfiles, which
775 // may have an empty suffix, but that is covered above.
776 Preconditions.checkArgument(suffix.equals(runfiles.suffix));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100777 artifactsBuilder.addTransitive(runfiles.getUnconditionalArtifacts());
778 symlinksBuilder.addTransitive(runfiles.getSymlinks());
779 rootSymlinksBuilder.addTransitive(runfiles.getRootSymlinks());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100780 if (includePruningManifests) {
781 pruningManifestsBuilder.addTransitive(runfiles.getPruningManifests());
782 }
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000783 if (emptyFilesSupplier == DUMMY_EMPTY_FILES_SUPPLIER) {
784 emptyFilesSupplier = runfiles.getEmptyFilesProvider();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100785 } else {
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000786 EmptyFilesSupplier otherSupplier = runfiles.getEmptyFilesProvider();
787 Preconditions.checkState((otherSupplier == DUMMY_EMPTY_FILES_SUPPLIER)
788 || emptyFilesSupplier.equals(otherSupplier));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100789 }
790 return this;
791 }
792
793 private static Iterable<TransitiveInfoCollection> getNonDataDeps(RuleContext ruleContext) {
794 return Iterables.concat(
795 // TODO(bazel-team): This line shouldn't be here. Removing it requires that no rules have
796 // dependent rules in srcs (except for filegroups and such), but always in deps.
797 // TODO(bazel-team): DONT_CHECK is not optimal here. Rules that use split configs need to
798 // be changed not to call into here.
799 getPrerequisites(ruleContext, "srcs", Mode.DONT_CHECK),
800 getPrerequisites(ruleContext, "deps", Mode.DONT_CHECK));
801 }
802
803 /**
804 * For the specified attribute "attributeName" (which must be of type list(label)), resolves all
805 * the labels into ConfiguredTargets (for the same configuration as this one) and returns them
806 * as a list.
807 *
808 * <p>If the rule does not have the specified attribute, returns the empty list.
809 */
810 private static Iterable<? extends TransitiveInfoCollection> getPrerequisites(
811 RuleContext ruleContext, String attributeName, Mode mode) {
812 if (ruleContext.getRule().isAttrDefined(attributeName, Type.LABEL_LIST)) {
813 return ruleContext.getPrerequisites(attributeName, mode);
814 } else {
815 return Collections.emptyList();
816 }
817 }
818 }
819}