blob: e03edd50c43182befd40a10c0003995c2f60f674 [file] [log] [blame]
cparsons90f70b62018-05-01 12:45:02 -07001// Copyright 2018 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.analysis;
16
17import com.google.common.annotations.VisibleForTesting;
18import com.google.common.base.Function;
tomlua155b532017-11-08 20:12:47 +010019import com.google.common.base.Preconditions;
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000020import com.google.common.collect.ImmutableList;
Benjamin Petersonba2f3912018-06-18 08:54:36 -070021import com.google.common.collect.ImmutableSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010022import com.google.common.collect.Iterables;
Benjamin Petersonba2f3912018-06-18 08:54:36 -070023import com.google.common.collect.Streams;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.devtools.build.lib.actions.Artifact;
felly56fd4fe2018-09-05 10:54:54 -070025import com.google.devtools.build.lib.actions.ArtifactPathResolver;
gregce593f7f92017-09-19 02:02:21 +020026import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
Kristina Chodorowd9121972016-04-25 17:45:55 +000027import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.collect.nestedset.NestedSet;
29import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
30import com.google.devtools.build.lib.collect.nestedset.Order;
Brian Silverman13891f62015-06-22 16:46:40 +000031import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import com.google.devtools.build.lib.events.Event;
33import com.google.devtools.build.lib.events.EventHandler;
Googler3ed13c82016-03-07 21:53:29 +000034import com.google.devtools.build.lib.events.EventKind;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035import com.google.devtools.build.lib.events.Location;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000036import com.google.devtools.build.lib.packages.BuildType;
cpeyser27458be2018-02-14 12:49:17 -080037import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
cpeyser27458be2018-02-14 12:49:17 -080038import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
cparsons90f70b62018-05-01 12:45:02 -070039import com.google.devtools.build.lib.skylarkbuildapi.RunfilesApi;
vladmos46907932017-06-30 14:01:45 +020040import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
Eric Dobson20bd2742016-11-25 21:59:09 +000041import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
fellyc31c8c92018-08-29 15:07:59 -070042import com.google.devtools.build.lib.util.Fingerprint;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044import java.io.BufferedReader;
45import java.io.IOException;
46import java.io.InputStreamReader;
47import java.util.Collections;
48import java.util.HashMap;
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +000049import java.util.Iterator;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import java.util.LinkedHashMap;
51import java.util.Map;
Googler3ed13c82016-03-07 21:53:29 +000052import java.util.Objects;
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000053import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import javax.annotation.Nullable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055
56/**
57 * An object that encapsulates runfiles. Conceptually, the runfiles are a map of paths to files,
58 * forming a symlink tree.
59 *
60 * <p>In order to reduce memory consumption, this map is not explicitly stored here, but instead as
61 * a combination of four parts: artifacts placed at their root-relative paths, source tree symlinks,
62 * root symlinks (outside of the source tree), and artifacts included as parts of "pruning
63 * manifests" (see {@link PruningManifest}).
64 */
65@Immutable
cpeyser27458be2018-02-14 12:49:17 -080066@AutoCodec
cparsons90f70b62018-05-01 12:45:02 -070067public final class Runfiles implements RunfilesApi {
Lukacs Berki5ab30d62015-03-11 22:06:04 +000068 private static final Function<SymlinkEntry, Artifact> TO_ARTIFACT =
69 new Function<SymlinkEntry, Artifact>() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070 @Override
Lukacs Berki5ab30d62015-03-11 22:06:04 +000071 public Artifact apply(SymlinkEntry input) {
72 return input.getArtifact();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073 }
74 };
75
shahan20f35b42018-02-28 15:57:33 -080076 private static class DummyEmptyFilesSupplier implements EmptyFilesSupplier {
77 private DummyEmptyFilesSupplier() {}
cpeyser27458be2018-02-14 12:49:17 -080078
79 @Override
80 public Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths) {
81 return ImmutableList.of();
82 }
83 }
84
shahan20f35b42018-02-28 15:57:33 -080085 @AutoCodec @AutoCodec.VisibleForSerialization
86 static final EmptyFilesSupplier DUMMY_EMPTY_FILES_SUPPLIER = new DummyEmptyFilesSupplier();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087
Lukacs Berki5ab30d62015-03-11 22:06:04 +000088 /**
89 * An entry in the runfiles map.
Googlerc85af312016-03-10 17:55:17 +000090 *
91 * <p>build-runfiles.cc enforces the following constraints: The PathFragment must not be an
Eric Dobson20bd2742016-11-25 21:59:09 +000092 * absolute path, nor contain "..". Overlapping runfiles links are also refused. This is the case
Googlerc85af312016-03-10 17:55:17 +000093 * where you ask to create a link to "foo" and also "foo/bar.txt". I.e. you're asking it to make
94 * "foo" both a file (symlink) and a directory.
95 *
96 * <p>Links to directories are heavily discouraged.
Lukacs Berki5ab30d62015-03-11 22:06:04 +000097 */
98 //
99 // O intrepid fixer or bugs and implementor of features, dare not to add a .equals() method
100 // to this class, lest you condemn yourself, or a fellow other developer to spending two
101 // delightful hours in a fancy hotel on a Chromebook that is utterly unsuitable for Java
102 // development to figure out what went wrong, just like I just did.
103 //
104 // The semantics of the symlinks nested set dictates that later entries overwrite earlier
105 // ones. However, the semantics of nested sets dictate that if there are duplicate entries, they
106 // are only returned once in the iterator.
107 //
108 // These two things, innocent when taken alone, result in the effect that when there are three
109 // entries for the same path, the first one and the last one the same, and the middle one
110 // different, the *middle* one will take effect: the middle one overrides the first one, and the
111 // first one prevents the last one from appearing on the iterator.
112 //
113 // The lack of a .equals() method prevents this by making the first entry in the above case not
114 // equals to the third one if they are not the same instance (which they almost never are)
115 //
116 // Goodnight, prince(ss)?, and sweet dreams.
cpeyser27458be2018-02-14 12:49:17 -0800117 @AutoCodec
118 @VisibleForSerialization
119 static final class SymlinkEntry implements SkylarkValue {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000120 private final PathFragment path;
121 private final Artifact artifact;
122
cpeyser27458be2018-02-14 12:49:17 -0800123 @VisibleForSerialization
124 SymlinkEntry(PathFragment path, Artifact artifact) {
Ulf Adams8594de82017-03-20 16:30:11 +0000125 this.path = Preconditions.checkNotNull(path);
126 this.artifact = Preconditions.checkNotNull(artifact);
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000127 }
128
129 public PathFragment getPath() {
130 return path;
131 }
132
133 public Artifact getArtifact() {
134 return artifact;
135 }
Eric Dobson20bd2742016-11-25 21:59:09 +0000136
Ulf Adams8594de82017-03-20 16:30:11 +0000137 @Override
Eric Dobson20bd2742016-11-25 21:59:09 +0000138 public boolean isImmutable() {
139 return true;
140 }
141
Ulf Adams8594de82017-03-20 16:30:11 +0000142 @Override
vladmos46907932017-06-30 14:01:45 +0200143 public void repr(SkylarkPrinter printer) {
144 printer.append("SymlinkEntry(path = ");
145 printer.repr(getPath().toString());
146 printer.append(", artifact = ");
147 getArtifact().repr(printer);
148 printer.append(")");
Eric Dobson20bd2742016-11-25 21:59:09 +0000149 }
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000150 }
151
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152 // It is important to declare this *after* the DUMMY_SYMLINK_EXPANDER to avoid NPEs
153 public static final Runfiles EMPTY = new Builder().build();
154
Kristina Chodorowb579b942015-03-02 15:51:58 +0000155 /**
156 * The directory to put all runfiles under.
157 *
158 * <p>Using "foo" will put runfiles under &lt;target&gt;.runfiles/foo.</p>
Googler3ed13c82016-03-07 21:53:29 +0000159 *
160 * <p>This is either set to the workspace name, or is empty.
Kristina Chodorowb579b942015-03-02 15:51:58 +0000161 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000162 private final PathFragment suffix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163
164 /**
165 * The artifacts that should *always* be present in the runfiles directory. These are
166 * differentiated from the artifacts that may or may not be included by a pruning manifest
167 * (see {@link PruningManifest} below).
168 *
169 * <p>This collection may not include any middlemen. These artifacts will be placed at a location
170 * that corresponds to the root-relative path of each artifact. It's possible for several
171 * artifacts to have the same root-relative path, in which case the last one will win.
172 */
173 private final NestedSet<Artifact> unconditionalArtifacts;
174
175 /**
176 * A map of symlinks that should be present in the runfiles directory. In general, the symlink can
177 * be determined from the artifact by using the root-relative path, so this should only be used
178 * for cases where that isn't possible.
179 *
180 * <p>This may include runfiles symlinks from the root of the runfiles tree.
181 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000182 private final NestedSet<SymlinkEntry> symlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183
184 /**
185 * A map of symlinks that should be present above the runfiles directory. These are useful for
186 * certain rule types like AppEngine apps which have root level config files outside of the
187 * regular source tree.
188 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000189 private final NestedSet<SymlinkEntry> rootSymlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100190
191 /**
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200192 * A set of middlemen artifacts. {@link RuleConfiguredTargetBuilder} adds these to the {@link
193 * FilesToRunProvider} of binaries that include this runfiles tree in their runfiles.
194 */
195 private final NestedSet<Artifact> extraMiddlemen;
196
197 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000198 * Interface used for adding empty files to the runfiles at the last minute. Mainly to support
199 * python-related rules adding __init__.py files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000201 public interface EmptyFilesSupplier {
202 /** Calculate additional empty files to add based on the existing manifest paths. */
203 Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths);
204 }
205
206 /** Generates extra (empty file) inputs. */
207 private final EmptyFilesSupplier emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100208
209 /**
Googler3ed13c82016-03-07 21:53:29 +0000210 * Behavior upon finding a conflict between two runfile entries. A conflict means that two
211 * different artifacts have the same runfiles path specified. For example, adding artifact
Googlerc85af312016-03-10 17:55:17 +0000212 * "a.foo" at path "bar" when there is already an artifact "b.foo" at path "bar". The policies
213 * are ordered from least strict to most strict.
Googler3ed13c82016-03-07 21:53:29 +0000214 *
215 * <p>Note that conflicts are found relatively late, when the manifest file is created, not when
216 * the symlinks are added to runfiles.
217 *
218 * <p>If no EventHandler is available, all values are treated as IGNORE.
219 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000220 public enum ConflictPolicy {
Googler3ed13c82016-03-07 21:53:29 +0000221 IGNORE,
222 WARN,
223 ERROR,
224 }
225
226 /** Policy for this Runfiles tree */
227 private ConflictPolicy conflictPolicy = ConflictPolicy.IGNORE;
228
229 /**
cpeyser27458be2018-02-14 12:49:17 -0800230 * Defines a set of artifacts that may or may not be included in the runfiles directory and a
231 * manifest file that makes that determination. These are applied on top of any artifacts
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100232 * specified in {@link #unconditionalArtifacts}.
233 *
cpeyser27458be2018-02-14 12:49:17 -0800234 * <p>The incentive behind this is to enable execution-phase "pruning" of runfiles. Anything set
235 * in unconditionalArtifacts is hard-set in Blaze's analysis phase, and thus unchangeable in
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100236 * response to execution phase results. This isn't always convenient. For example, say we have an
237 * action that consumes a set of "possible" runtime dependencies for a source file, parses that
238 * file for "import a.b.c" statements, and outputs a manifest of the actual dependencies that are
239 * referenced and thus really needed. This can reduce the size of the runfiles set, but we can't
240 * use this information until the manifest output is available.
241 *
242 * <p>Only artifacts present in the candidate set AND the manifest output make it into the
243 * runfiles tree. The candidate set requirement guarantees that analysis-time dependencies are a
244 * superset of the pruned dependencies, so undeclared inclusions (which can break build
245 * correctness) aren't possible.
246 */
cpeyser27458be2018-02-14 12:49:17 -0800247 @AutoCodec
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100248 public static class PruningManifest {
249 private final NestedSet<Artifact> candidateRunfiles;
250 private final Artifact manifestFile;
251
252 /**
253 * Creates a new pruning manifest.
254 *
255 * @param candidateRunfiles set of possible artifacts that the manifest file may reference
256 * @param manifestFile the manifest file, expected to be a newline-separated list of
257 * source tree root-relative paths (i.e. "my/package/myfile.txt"). Anything that can't be
258 * resolved back to an entry in candidateRunfiles is ignored and will *not* make it into
259 * the runfiles tree.
260 */
261 public PruningManifest(NestedSet<Artifact> candidateRunfiles, Artifact manifestFile) {
262 this.candidateRunfiles = candidateRunfiles;
263 this.manifestFile = manifestFile;
264 }
265
266 public NestedSet<Artifact> getCandidateRunfiles() {
267 return candidateRunfiles;
268 }
269
270 public Artifact getManifestFile() {
271 return manifestFile;
272 }
273 }
274
275 /**
276 * The pruning manifests that should be applied to these runfiles.
277 */
278 private final NestedSet<PruningManifest> pruningManifests;
279
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000280 /**
281 * If external runfiles should be created under .runfiles/wsname/external/repo as well as
282 * .runfiles/repo.
283 */
284 private final boolean legacyExternalRunfiles;
285
cpeyser27458be2018-02-14 12:49:17 -0800286 @AutoCodec.Instantiator
287 @VisibleForSerialization
288 Runfiles(
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000289 PathFragment suffix,
cpeyser27458be2018-02-14 12:49:17 -0800290 NestedSet<Artifact> unconditionalArtifacts,
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000291 NestedSet<SymlinkEntry> symlinks,
292 NestedSet<SymlinkEntry> rootSymlinks,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100293 NestedSet<PruningManifest> pruningManifests,
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200294 NestedSet<Artifact> extraMiddlemen,
Googlerc85af312016-03-10 17:55:17 +0000295 EmptyFilesSupplier emptyFilesSupplier,
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000296 ConflictPolicy conflictPolicy,
297 boolean legacyExternalRunfiles) {
Ulf Adams4226be22015-09-03 17:00:54 +0000298 this.suffix = suffix;
cpeyser27458be2018-02-14 12:49:17 -0800299 this.unconditionalArtifacts = Preconditions.checkNotNull(unconditionalArtifacts);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300 this.symlinks = Preconditions.checkNotNull(symlinks);
301 this.rootSymlinks = Preconditions.checkNotNull(rootSymlinks);
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200302 this.extraMiddlemen = Preconditions.checkNotNull(extraMiddlemen);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100303 this.pruningManifests = Preconditions.checkNotNull(pruningManifests);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000304 this.emptyFilesSupplier = Preconditions.checkNotNull(emptyFilesSupplier);
Googlerc85af312016-03-10 17:55:17 +0000305 this.conflictPolicy = conflictPolicy;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000306 this.legacyExternalRunfiles = legacyExternalRunfiles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100307 }
308
309 /**
Kristina Chodorowb579b942015-03-02 15:51:58 +0000310 * Returns the runfiles' suffix.
311 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000312 public PathFragment getSuffix() {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000313 return suffix;
314 }
315
316 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100317 * Returns the artifacts that are unconditionally included in the runfiles (as opposed to
318 * pruning manifest candidates, which may or may not be included).
319 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100320 public NestedSet<Artifact> getUnconditionalArtifacts() {
321 return unconditionalArtifacts;
322 }
323
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200324 public NestedSet<Artifact> getExtraMiddlemen() {
325 return extraMiddlemen;
326 }
327
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100328 /**
John Caterd7928422017-01-03 20:06:59 +0000329 * Returns the collection of runfiles as artifacts, including both unconditional artifacts and
330 * pruning manifest candidates.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100331 */
cparsons90f70b62018-05-01 12:45:02 -0700332 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100333 public NestedSet<Artifact> getArtifacts() {
334 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
Benjamin Peterson899a3e72018-06-06 14:18:29 -0700335 allArtifacts.addTransitive(unconditionalArtifacts);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100336 for (PruningManifest manifest : getPruningManifests()) {
337 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
338 }
339 return allArtifacts.build();
340 }
341
John Caterd7928422017-01-03 20:06:59 +0000342 /** Returns the symlinks. */
cparsons90f70b62018-05-01 12:45:02 -0700343 @Override
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000344 public NestedSet<SymlinkEntry> getSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 return symlinks;
346 }
347
cparsons90f70b62018-05-01 12:45:02 -0700348 @Override
Brian Silverman4321b672016-04-15 12:16:04 +0000349 public NestedSet<String> getEmptyFilenames() {
Benjamin Petersonba2f3912018-06-18 08:54:36 -0700350 Set<PathFragment> manifestKeys =
351 Streams.concat(
352 Streams.stream(symlinks).map(SymlinkEntry::getPath),
353 Streams.stream(getArtifacts()).map(Artifact::getRootRelativePath))
354 .collect(ImmutableSet.toImmutableSet());
355 Iterable<PathFragment> emptyKeys = emptyFilesSupplier.getExtraPaths(manifestKeys);
356 return NestedSetBuilder.<String>stableOrder()
357 .addAll(
358 Streams.stream(emptyKeys)
359 .map(PathFragment::toString)
360 .collect(ImmutableList.toImmutableList()))
361 .build();
Brian Silverman4321b672016-04-15 12:16:04 +0000362 }
363
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100364 /**
365 * Returns the symlinks as a map from path fragment to artifact.
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000366 *
367 * @param checker If not null, check for conflicts using this checker.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100368 */
mstaib0e81f9a2018-09-17 14:30:28 -0700369 public Map<PathFragment, Artifact> getSymlinksAsMap(@Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000370 return entriesToMap(symlinks, checker);
371 }
372
373 /**
374 * @param eventHandler Used for throwing an error if we have an obscuring runlink.
375 * May be null, in which case obscuring symlinks are silently discarded.
376 * @param location Location for reporter. Ignored if reporter is null.
377 * @param workingManifest Manifest to be checked for obscuring symlinks.
378 * @return map of source file names mapped to their location on disk.
379 */
380 @VisibleForTesting
381 static Map<PathFragment, Artifact> filterListForObscuringSymlinks(
382 EventHandler eventHandler, Location location, Map<PathFragment, Artifact> workingManifest) {
383 Map<PathFragment, Artifact> newManifest = new HashMap<>();
384
385 outer:
jcater36745912018-05-01 13:20:00 -0700386 for (Iterator<Map.Entry<PathFragment, Artifact>> i = workingManifest.entrySet().iterator();
387 i.hasNext(); ) {
388 Map.Entry<PathFragment, Artifact> entry = i.next();
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000389 PathFragment source = entry.getKey();
390 Artifact symlink = entry.getValue();
391 // drop nested entries; warn if this changes anything
392 int n = source.segmentCount();
393 for (int j = 1; j < n; ++j) {
394 PathFragment prefix = source.subFragment(0, n - j);
395 Artifact ancestor = workingManifest.get(prefix);
396 if (ancestor != null) {
397 // This is an obscuring symlink, so just drop it and move on if there's no reporter.
398 if (eventHandler == null) {
399 continue outer;
400 }
401 PathFragment suffix = source.subFragment(n - j, n);
shahanb3ba5142018-04-06 19:17:12 -0700402 PathFragment viaAncestor = ancestor.getExecPath().getRelative(suffix);
403 PathFragment expected = symlink.getExecPath();
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000404 if (!viaAncestor.equals(expected)) {
shahanb3ba5142018-04-06 19:17:12 -0700405 eventHandler.handle(
406 Event.warn(
407 location,
408 "runfiles symlink "
409 + source
410 + " -> "
411 + expected
412 + " obscured by "
413 + prefix
414 + " -> "
415 + ancestor.getExecPath()));
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000416 }
417 continue outer;
418 }
419 }
420 newManifest.put(entry.getKey(), entry.getValue());
421 }
422 return newManifest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100423 }
424
425 /**
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000426 * Returns the symlinks as a map from PathFragment to Artifact.
427 *
428 * @param eventHandler Used for throwing an error if we have an obscuring runlink within the
Yue Gan5e60e382017-03-20 10:58:11 +0000429 * normal source tree entries, or runfile conflicts. May be null, in which case obscuring
430 * symlinks are silently discarded, and conflicts are overwritten.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100431 * @param location Location for eventHandler warnings. Ignored if eventHandler is null.
felly56fd4fe2018-09-05 10:54:54 -0700432 * @param resolver The {@link ArtifactPathResolver} to use for the pruning manifest, if present.
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000433 * @return Map<PathFragment, Artifact> path fragment to artifact, of normal source tree entries
Yue Gan5e60e382017-03-20 10:58:11 +0000434 * and elements that live outside the source tree. Null values represent empty input files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435 */
felly56fd4fe2018-09-05 10:54:54 -0700436 public Map<PathFragment, Artifact> getRunfilesInputs(EventHandler eventHandler, Location location,
437 ArtifactPathResolver resolver) throws IOException {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000438 ConflictChecker checker = new ConflictChecker(conflictPolicy, eventHandler, location);
439 Map<PathFragment, Artifact> manifest = getSymlinksAsMap(checker);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100440 // Add unconditional artifacts (committed to inclusion on construction of runfiles).
ulfjackff179a32018-02-06 04:16:34 -0800441 for (Artifact artifact : getUnconditionalArtifacts()) {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000442 checker.put(manifest, artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100443 }
444
445 // Add conditional artifacts (only included if they appear in a pruning manifest).
446 for (Runfiles.PruningManifest pruningManifest : getPruningManifests()) {
447 // This map helps us convert from source tree root-relative paths back to artifacts.
448 Map<String, Artifact> allowedRunfiles = new HashMap<>();
449 for (Artifact artifact : pruningManifest.getCandidateRunfiles()) {
450 allowedRunfiles.put(artifact.getRootRelativePath().getPathString(), artifact);
451 }
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000452 try (BufferedReader reader = new BufferedReader(
felly56fd4fe2018-09-05 10:54:54 -0700453 new InputStreamReader(resolver.toPath(pruningManifest.getManifestFile()).getInputStream()))) {
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000454 String line;
455 while ((line = reader.readLine()) != null) {
456 Artifact artifact = allowedRunfiles.get(line);
457 if (artifact != null) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000458 checker.put(manifest, artifact.getRootRelativePath(), artifact);
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000459 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100460 }
461 }
462 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000463 manifest = filterListForObscuringSymlinks(eventHandler, location, manifest);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000464
465 // TODO(bazel-team): Create /dev/null-like Artifact to avoid nulls?
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000466 for (PathFragment extraPath : emptyFilesSupplier.getExtraPaths(manifest.keySet())) {
467 checker.put(manifest, extraPath, null);
468 }
469
470 // Copy manifest map to another manifest map, prepending the workspace name to every path.
471 // E.g. for workspace "myworkspace", the runfile entry "mylib.so"->"/path/to/mylib.so" becomes
472 // "myworkspace/mylib.so"->"/path/to/mylib.so".
Kristina Chodorowd9121972016-04-25 17:45:55 +0000473 ManifestBuilder builder = new ManifestBuilder(suffix, legacyExternalRunfiles);
474 builder.addUnderWorkspace(manifest, checker);
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000475
Googler3ed13c82016-03-07 21:53:29 +0000476 // Finally add symlinks relative to the root of the runfiles tree, on top of everything else.
477 // This operation is always checked for conflicts, to match historical behavior.
478 if (conflictPolicy == ConflictPolicy.IGNORE) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000479 checker = new ConflictChecker(ConflictPolicy.WARN, eventHandler, location);
Googler3ed13c82016-03-07 21:53:29 +0000480 }
Kristina Chodorowd9121972016-04-25 17:45:55 +0000481 builder.add(getRootSymlinksAsMap(checker), checker);
482 return builder.build();
483 }
484
485 /**
486 * Helper class to handle munging the paths of external artifacts.
487 */
488 @VisibleForTesting
489 static final class ManifestBuilder {
490 // Manifest of paths to artifacts. Path fragments are relative to the .runfiles directory.
491 private final Map<PathFragment, Artifact> manifest;
492 private final PathFragment workspaceName;
493 private final boolean legacyExternalRunfiles;
494 // Whether we saw the local workspace name in the runfiles. If legacyExternalRunfiles is true,
495 // then this is true, as anything under external/ will also have a runfile under the local
496 // workspace.
497 private boolean sawWorkspaceName;
498
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000499 public ManifestBuilder(
Kristina Chodorowd9121972016-04-25 17:45:55 +0000500 PathFragment workspaceName, boolean legacyExternalRunfiles) {
501 this.manifest = new HashMap<>();
502 this.workspaceName = workspaceName;
503 this.legacyExternalRunfiles = legacyExternalRunfiles;
504 this.sawWorkspaceName = legacyExternalRunfiles;
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000505 }
506
mstaib0e81f9a2018-09-17 14:30:28 -0700507 /**
508 * Adds a map under the workspaceName.
509 */
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000510 public void addUnderWorkspace(
mstaib0e81f9a2018-09-17 14:30:28 -0700511 Map<PathFragment, Artifact> inputManifest, ConflictChecker checker) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000512 for (Map.Entry<PathFragment, Artifact> entry : inputManifest.entrySet()) {
513 PathFragment path = entry.getKey();
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000514 if (isUnderWorkspace(path)) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000515 sawWorkspaceName = true;
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000516 checker.put(manifest, workspaceName.getRelative(path), entry.getValue());
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000517 } else {
518 if (legacyExternalRunfiles) {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000519 checker.put(manifest, workspaceName.getRelative(path), entry.getValue());
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000520 }
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000521 // Always add the non-legacy .runfiles/repo/whatever path.
522 checker.put(manifest, getExternalPath(path), entry.getValue());
Kristina Chodorowd9121972016-04-25 17:45:55 +0000523 }
524 }
525 }
526
mstaib0e81f9a2018-09-17 14:30:28 -0700527 /**
528 * Adds a map to the root directory.
529 */
530 public void add(Map<PathFragment, Artifact> inputManifest, ConflictChecker checker) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000531 for (Map.Entry<PathFragment, Artifact> entry : inputManifest.entrySet()) {
532 checker.put(manifest, checkForWorkspace(entry.getKey()), entry.getValue());
533 }
534 }
535
536 /**
537 * Returns the manifest, adding the workspaceName directory if it is not already present.
538 */
539 public Map<PathFragment, Artifact> build() {
540 if (!sawWorkspaceName) {
541 // If we haven't seen it and we have seen other files, add the workspace name directory.
542 // It might not be there if all of the runfiles are from other repos (and then running from
543 // x.runfiles/ws will fail, because ws won't exist). We can't tell Runfiles to create a
544 // directory, so instead this creates a hidden file inside the desired directory.
545 manifest.put(workspaceName.getRelative(".runfile"), null);
546 }
547 return manifest;
548 }
549
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000550 private PathFragment getExternalPath(PathFragment path) {
551 return checkForWorkspace(path.relativeTo(Label.EXTERNAL_PACKAGE_NAME));
552 }
553
Kristina Chodorowd9121972016-04-25 17:45:55 +0000554 private PathFragment checkForWorkspace(PathFragment path) {
Lukacs Berki4475bf12017-03-20 14:35:46 +0000555 sawWorkspaceName = sawWorkspaceName
556 || path.getSegment(0).equals(workspaceName.getPathString());
Kristina Chodorowd9121972016-04-25 17:45:55 +0000557 return path;
558 }
559
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000560 private static boolean isUnderWorkspace(PathFragment path) {
561 return !path.startsWith(Label.EXTERNAL_PACKAGE_NAME);
Kristina Chodorowd9121972016-04-25 17:45:55 +0000562 }
563 }
564
565 boolean getLegacyExternalRunfiles() {
566 return legacyExternalRunfiles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100567 }
568
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100569 /**
570 * Returns the root symlinks.
571 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000572 public NestedSet<SymlinkEntry> getRootSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100573 return rootSymlinks;
574 }
575
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100576 /**
Googler3ed13c82016-03-07 21:53:29 +0000577 * Returns the root symlinks as a map from path fragment to artifact.
578 *
579 * @param checker If not null, check for conflicts using this checker.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100580 */
mstaib0e81f9a2018-09-17 14:30:28 -0700581 public Map<PathFragment, Artifact> getRootSymlinksAsMap(@Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000582 return entriesToMap(rootSymlinks, checker);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100583 }
584
585 /**
586 * Returns the unified map of path fragments to artifacts, taking both artifacts and symlinks into
587 * account.
588 */
589 public Map<PathFragment, Artifact> asMapWithoutRootSymlinks() {
mstaib0e81f9a2018-09-17 14:30:28 -0700590 Map<PathFragment, Artifact> result = entriesToMap(symlinks, null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100591 // If multiple artifacts have the same root-relative path, the last one in the list will win.
592 // That is because the runfiles tree cannot contain the same artifact for different
593 // configurations, because it only uses root-relative paths.
ulfjackff179a32018-02-06 04:16:34 -0800594 for (Artifact artifact : unconditionalArtifacts) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000595 result.put(artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000597 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100598 }
599
600 /**
601 * Returns the pruning manifests specified for this runfiles tree.
602 */
603 public NestedSet<PruningManifest> getPruningManifests() {
604 return pruningManifests;
605 }
606
607 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000608 * Returns the manifest expander specified for this runfiles tree.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100609 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000610 private EmptyFilesSupplier getEmptyFilesProvider() {
611 return emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100612 }
613
614 /**
615 * Returns the unified map of path fragments to artifacts, taking into account artifacts,
616 * symlinks, and pruning manifest candidates. The returned set is guaranteed to be a (not
617 * necessarily strict) superset of the actual runfiles tree created at execution time.
618 */
619 public NestedSet<Artifact> getAllArtifacts() {
620 if (isEmpty()) {
621 return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
622 }
623 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
624 allArtifacts
625 .addTransitive(unconditionalArtifacts)
626 .addAll(Iterables.transform(symlinks, TO_ARTIFACT))
627 .addAll(Iterables.transform(rootSymlinks, TO_ARTIFACT));
628 for (PruningManifest manifest : getPruningManifests()) {
629 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
630 }
631 return allArtifacts.build();
632 }
633
634 /**
635 * Returns if there are no runfiles.
636 */
637 public boolean isEmpty() {
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200638 return unconditionalArtifacts.isEmpty()
639 && symlinks.isEmpty()
640 && rootSymlinks.isEmpty()
641 && pruningManifests.isEmpty()
642 && extraMiddlemen.isEmpty();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100643 }
644
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000645 /**
646 * Flatten a sequence of entries into a single map.
647 *
648 * @param entrySet Sequence of entries to add.
649 * @param checker If not null, check for conflicts with this checker, otherwise silently allow
mstaib0e81f9a2018-09-17 14:30:28 -0700650 * entries to overwrite previous entries.
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000651 * @return Map<PathFragment, Artifact> Map of runfile entries.
652 */
653 private static Map<PathFragment, Artifact> entriesToMap(
mstaib0e81f9a2018-09-17 14:30:28 -0700654 Iterable<SymlinkEntry> entrySet, @Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000655 checker = (checker != null) ? checker : ConflictChecker.IGNORE_CHECKER;
656 Map<PathFragment, Artifact> map = new LinkedHashMap<>();
657 for (SymlinkEntry entry : entrySet) {
658 checker.put(map, entry.getPath(), entry.getArtifact());
659 }
660 return map;
661 }
662
Googlerc85af312016-03-10 17:55:17 +0000663 /** Returns currently policy for conflicting symlink entries. */
664 public ConflictPolicy getConflictPolicy() {
665 return this.conflictPolicy;
666 }
667
Googler3ed13c82016-03-07 21:53:29 +0000668 /** Set whether we should warn about conflicting symlink entries. */
Googlerc85af312016-03-10 17:55:17 +0000669 public Runfiles setConflictPolicy(ConflictPolicy conflictPolicy) {
Googler3ed13c82016-03-07 21:53:29 +0000670 this.conflictPolicy = conflictPolicy;
Googlerc85af312016-03-10 17:55:17 +0000671 return this;
Googler3ed13c82016-03-07 21:53:29 +0000672 }
673
674 /**
675 * Checks for conflicts between entries in a runfiles tree while putting them in a map.
676 */
677 public static final class ConflictChecker {
678 /** Prebuilt ConflictChecker with policy set to IGNORE */
679 public static final ConflictChecker IGNORE_CHECKER =
680 new ConflictChecker(ConflictPolicy.IGNORE, null, null);
681
682 /** Behavior when a conflict is found. */
683 private final ConflictPolicy policy;
684
685 /** Used for warning on conflicts. May be null, in which case conflicts are ignored. */
686 private final EventHandler eventHandler;
687
688 /** Location for eventHandler warnings. Ignored if eventHandler is null. */
689 private final Location location;
690
mstaib0e81f9a2018-09-17 14:30:28 -0700691 /** Type of event to emit */
692 private final EventKind eventKind;
693
Googler3ed13c82016-03-07 21:53:29 +0000694 /** Construct a ConflictChecker for the given reporter with the given behavior */
695 public ConflictChecker(ConflictPolicy policy, EventHandler eventHandler, Location location) {
696 if (eventHandler == null) {
697 this.policy = ConflictPolicy.IGNORE; // Can't warn even if we wanted to
698 } else {
699 this.policy = policy;
700 }
701 this.eventHandler = eventHandler;
702 this.location = location;
mstaib0e81f9a2018-09-17 14:30:28 -0700703 this.eventKind = (policy == ConflictPolicy.ERROR) ? EventKind.ERROR : EventKind.WARNING;
Googler3ed13c82016-03-07 21:53:29 +0000704 }
705
706 /**
707 * Add an entry to a Map of symlinks, optionally reporting conflicts.
708 *
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000709 * @param map Manifest of runfile entries.
710 * @param path Path fragment to use as key in map.
Googler3ed13c82016-03-07 21:53:29 +0000711 * @param artifact Artifact to store in map. This may be null to indicate an empty file.
712 */
mstaib0e81f9a2018-09-17 14:30:28 -0700713 public void put(Map<PathFragment, Artifact> map, PathFragment path, Artifact artifact) {
ulfjackff179a32018-02-06 04:16:34 -0800714 Preconditions.checkArgument(
715 artifact == null || !artifact.isMiddlemanArtifact(), "%s", artifact);
Googler3ed13c82016-03-07 21:53:29 +0000716 if (policy != ConflictPolicy.IGNORE && map.containsKey(path)) {
717 // Previous and new entry might have value of null
718 Artifact previous = map.get(path);
719 if (!Objects.equals(previous, artifact)) {
shahanb3ba5142018-04-06 19:17:12 -0700720 String previousStr =
721 (previous == null) ? "empty file" : previous.getExecPath().toString();
722 String artifactStr =
723 (artifact == null) ? "empty file" : artifact.getExecPath().toString();
mstaib0e81f9a2018-09-17 14:30:28 -0700724 String message =
725 String.format(
726 "overwrote runfile %s, was symlink to %s, now symlink to %s",
727 path.getSafePathString(),
728 previousStr,
729 artifactStr);
730 eventHandler.handle(Event.of(eventKind, location, message));
Googler3ed13c82016-03-07 21:53:29 +0000731 }
732 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000733 map.put(path, artifact);
Googler3ed13c82016-03-07 21:53:29 +0000734 }
735 }
736
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100737 /**
738 * Builder for Runfiles objects.
739 */
740 public static final class Builder {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000741
Googler3ed13c82016-03-07 21:53:29 +0000742 /** This is set to the workspace name */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000743 private PathFragment suffix;
Kristina Chodorowb579b942015-03-02 15:51:58 +0000744
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100745 /**
746 * This must be COMPILE_ORDER because {@link #asMapWithoutRootSymlinks} overwrites earlier
747 * entries with later ones, so we want a post-order iteration.
748 */
749 private NestedSetBuilder<Artifact> artifactsBuilder =
750 NestedSetBuilder.compileOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000751 private NestedSetBuilder<SymlinkEntry> symlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100752 NestedSetBuilder.stableOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000753 private NestedSetBuilder<SymlinkEntry> rootSymlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100754 NestedSetBuilder.stableOrder();
755 private NestedSetBuilder<PruningManifest> pruningManifestsBuilder =
756 NestedSetBuilder.stableOrder();
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200757 private NestedSetBuilder<Artifact> extraMiddlemenBuilder = NestedSetBuilder.stableOrder();
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000758 private EmptyFilesSupplier emptyFilesSupplier = DUMMY_EMPTY_FILES_SUPPLIER;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100759
Googlerc85af312016-03-10 17:55:17 +0000760 /** Build the Runfiles object with this policy */
761 private ConflictPolicy conflictPolicy = ConflictPolicy.IGNORE;
762
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000763 private final boolean legacyExternalRunfiles;
764
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100765 /**
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000766 * Only used for Runfiles.EMPTY.
767 */
Ulf Adams4226be22015-09-03 17:00:54 +0000768 private Builder() {
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000769 this.suffix = PathFragment.EMPTY_FRAGMENT;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000770 this.legacyExternalRunfiles = false;
771 }
772
773 /**
774 * Creates a builder with the given suffix. Transitional constructor so that new rules don't
775 * accidentally depend on the legacy repository structure, until that option is removed.
776 *
777 * @param workspace is the string specified in workspace() in the WORKSPACE file.
778 */
779 public Builder(String workspace) {
780 this(workspace, false);
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000781 }
782
Kristina Chodorowcc8954a2015-10-27 17:23:32 +0000783 /**
784 * Creates a builder with the given suffix.
785 * @param workspace is the string specified in workspace() in the WORKSPACE file.
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000786 * @param legacyExternalRunfiles if the wsname/external/repo symlinks should also be
787 * created.
Kristina Chodorowcc8954a2015-10-27 17:23:32 +0000788 */
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000789 public Builder(String workspace, boolean legacyExternalRunfiles) {
nharmatab4060b62017-04-04 17:11:39 +0000790 this(PathFragment.create(workspace), legacyExternalRunfiles);
Jon Brandvein8d100342016-12-28 17:04:37 +0000791 }
792
793 /**
794 * Creates a builder with the given suffix.
795 * @param suffix is the PathFragment wrapping the string specified in workspace() in the
796 * WORKSPACE file.
797 * @param legacyExternalRunfiles if the wsname/external/repo symlinks should also be
798 * created.
799 */
800 private Builder(PathFragment suffix, boolean legacyExternalRunfiles) {
801 this.suffix = suffix;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000802 this.legacyExternalRunfiles = legacyExternalRunfiles;
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000803 }
804
805 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100806 * Builds a new Runfiles object.
807 */
808 public Runfiles build() {
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200809 return new Runfiles(
810 suffix,
811 artifactsBuilder.build(),
812 symlinksBuilder.build(),
813 rootSymlinksBuilder.build(),
814 pruningManifestsBuilder.build(),
815 extraMiddlemenBuilder.build(),
816 emptyFilesSupplier,
817 conflictPolicy,
818 legacyExternalRunfiles);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100819 }
820
821 /**
822 * Adds an artifact to the internal collection of artifacts.
823 */
824 public Builder addArtifact(Artifact artifact) {
825 Preconditions.checkNotNull(artifact);
ulfjackff179a32018-02-06 04:16:34 -0800826 Preconditions.checkArgument(!artifact.isMiddlemanArtifact());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100827 artifactsBuilder.add(artifact);
828 return this;
829 }
830
831 /**
832 * Adds several artifacts to the internal collection.
833 */
834 public Builder addArtifacts(Iterable<Artifact> artifacts) {
835 for (Artifact artifact : artifacts) {
836 addArtifact(artifact);
837 }
838 return this;
839 }
840
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100841 /**
Carmi Grushko75665632015-08-27 23:19:26 +0000842 * @deprecated Use {@link #addTransitiveArtifacts} instead, to prevent increased memory use.
tomlucd032d02017-09-19 15:50:28 +0200843 * <p>See alse {@link Builder#addTransitiveArtifactsWrappedInStableOrder}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100844 */
845 @Deprecated
846 public Builder addArtifacts(NestedSet<Artifact> artifacts) {
847 // Do not delete this method, or else addArtifacts(Iterable) calls with a NestedSet argument
848 // will not be flagged.
849 Iterable<Artifact> it = artifacts;
850 addArtifacts(it);
851 return this;
852 }
Carmi Grushko75665632015-08-27 23:19:26 +0000853
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100854 /**
855 * Adds a nested set to the internal collection.
856 */
857 public Builder addTransitiveArtifacts(NestedSet<Artifact> artifacts) {
858 artifactsBuilder.addTransitive(artifacts);
859 return this;
860 }
861
862 /**
tomlucd032d02017-09-19 15:50:28 +0200863 * Adds a nested set to the internal collection.
864 *
865 * <p>The nested set will become wrapped in stable order. Only use this when the set of
866 * artifacts will not have conflicting root relative paths, or the wrong artifact will end up in
867 * the runfiles tree.
868 */
869 public Builder addTransitiveArtifactsWrappedInStableOrder(NestedSet<Artifact> artifacts) {
870 NestedSet<Artifact> wrappedArtifacts =
871 NestedSetBuilder.<Artifact>stableOrder().addTransitive(artifacts).build();
872 artifactsBuilder.addTransitive(wrappedArtifacts);
873 return this;
874 }
875
876 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100877 * Adds a symlink.
878 */
879 public Builder addSymlink(PathFragment link, Artifact target) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000880 symlinksBuilder.add(new SymlinkEntry(link, target));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100881 return this;
882 }
883
Ulf Adams8594de82017-03-20 16:30:11 +0000884 /** Adds several symlinks. Neither keys nor values may be null. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100885 public Builder addSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000886 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
887 symlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
888 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100889 return this;
890 }
891
892 /**
893 * Adds several symlinks as a NestedSet.
894 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000895 public Builder addSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100896 symlinksBuilder.addTransitive(symlinks);
897 return this;
898 }
899
900 /**
Googlerc85af312016-03-10 17:55:17 +0000901 * Adds a root symlink.
902 */
903 public Builder addRootSymlink(PathFragment link, Artifact target) {
Googlerc85af312016-03-10 17:55:17 +0000904 rootSymlinksBuilder.add(new SymlinkEntry(link, target));
905 return this;
906 }
907
Ulf Adams8594de82017-03-20 16:30:11 +0000908 /** Adds several root symlinks. Neither keys nor values may be null. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100909 public Builder addRootSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000910 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
911 rootSymlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
912 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100913 return this;
914 }
915
916 /**
917 * Adds several root symlinks as a NestedSet.
918 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000919 public Builder addRootSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100920 rootSymlinksBuilder.addTransitive(symlinks);
921 return this;
922 }
923
924 /**
925 * Adds a pruning manifest. See {@link PruningManifest} for an explanation.
926 */
927 public Builder addPruningManifest(PruningManifest manifest) {
928 pruningManifestsBuilder.add(manifest);
929 return this;
930 }
931
932 /**
933 * Adds several pruning manifests as a NestedSet. See {@link PruningManifest} for an
934 * explanation.
935 */
936 public Builder addPruningManifests(NestedSet<PruningManifest> manifests) {
937 pruningManifestsBuilder.addTransitive(manifests);
938 return this;
939 }
940
941 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000942 * Specify a function that can create additional manifest entries based on the input entries,
943 * see {@link EmptyFilesSupplier} for more details.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100944 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000945 public Builder setEmptyFilesSupplier(EmptyFilesSupplier supplier) {
946 emptyFilesSupplier = Preconditions.checkNotNull(supplier);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100947 return this;
948 }
949
950 /**
951 * Merges runfiles from a given runfiles support.
952 *
953 * @param runfilesSupport the runfiles support to be merged in
954 */
955 public Builder merge(@Nullable RunfilesSupport runfilesSupport) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100956 if (runfilesSupport == null) {
957 return this;
958 }
Lukacs Berki1f820792015-03-06 18:16:10 +0000959 merge(runfilesSupport.getRunfiles());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100960 return this;
961 }
962
963 /**
964 * Adds the runfiles for a particular target and visits the transitive closure of "srcs",
965 * "deps" and "data", collecting all of their respective runfiles.
966 */
967 public Builder addRunfiles(RuleContext ruleContext,
968 Function<TransitiveInfoCollection, Runfiles> mapping) {
969 Preconditions.checkNotNull(mapping);
970 Preconditions.checkNotNull(ruleContext);
971 addDataDeps(ruleContext);
972 addNonDataDeps(ruleContext, mapping);
973 return this;
974 }
975
976 /**
977 * Adds the files specified by a mapping from the transitive info collection to the runfiles.
978 *
979 * <p>Dependencies in {@code srcs} and {@code deps} are considered.
980 */
981 public Builder add(RuleContext ruleContext,
982 Function<TransitiveInfoCollection, Runfiles> mapping) {
983 Preconditions.checkNotNull(ruleContext);
984 Preconditions.checkNotNull(mapping);
985 for (TransitiveInfoCollection dep : getNonDataDeps(ruleContext)) {
986 Runfiles runfiles = mapping.apply(dep);
987 if (runfiles != null) {
988 merge(runfiles);
989 }
990 }
991
992 return this;
993 }
994
995 /**
996 * Collects runfiles from data dependencies of a target.
997 */
998 public Builder addDataDeps(RuleContext ruleContext) {
gregce475d91a2018-05-25 12:18:27 -0700999 addTargets(getPrerequisites(ruleContext, "data", Mode.DONT_CHECK),
1000 RunfilesProvider.DATA_RUNFILES);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001001 return this;
1002 }
1003
1004 /**
1005 * Collects runfiles from "srcs" and "deps" of a target.
1006 */
1007 public Builder addNonDataDeps(RuleContext ruleContext,
1008 Function<TransitiveInfoCollection, Runfiles> mapping) {
1009 for (TransitiveInfoCollection target : getNonDataDeps(ruleContext)) {
1010 addTargetExceptFileTargets(target, mapping);
1011 }
1012 return this;
1013 }
1014
1015 public Builder addTargets(Iterable<? extends TransitiveInfoCollection> targets,
1016 Function<TransitiveInfoCollection, Runfiles> mapping) {
1017 for (TransitiveInfoCollection target : targets) {
1018 addTarget(target, mapping);
1019 }
1020 return this;
1021 }
1022
1023 public Builder addTarget(TransitiveInfoCollection target,
1024 Function<TransitiveInfoCollection, Runfiles> mapping) {
1025 return addTargetIncludingFileTargets(target, mapping);
1026 }
1027
1028 private Builder addTargetExceptFileTargets(TransitiveInfoCollection target,
1029 Function<TransitiveInfoCollection, Runfiles> mapping) {
1030 Runfiles runfiles = mapping.apply(target);
1031 if (runfiles != null) {
1032 merge(runfiles);
1033 }
1034
1035 return this;
1036 }
1037
1038 private Builder addTargetIncludingFileTargets(TransitiveInfoCollection target,
1039 Function<TransitiveInfoCollection, Runfiles> mapping) {
1040 if (target.getProvider(RunfilesProvider.class) == null
1041 && mapping == RunfilesProvider.DATA_RUNFILES) {
1042 // RuleConfiguredTarget implements RunfilesProvider, so this will only be called on
1043 // FileConfiguredTarget instances.
1044 // TODO(bazel-team): This is a terrible hack. We should be able to make this go away
1045 // by implementing RunfilesProvider on FileConfiguredTarget. We'd need to be mindful
1046 // of the memory use, though, since we have a whole lot of FileConfiguredTarget instances.
1047 addTransitiveArtifacts(target.getProvider(FileProvider.class).getFilesToBuild());
1048 return this;
1049 }
1050
1051 return addTargetExceptFileTargets(target, mapping);
1052 }
1053
1054 /**
1055 * Adds symlinks to given artifacts at their exec paths.
1056 */
1057 public Builder addSymlinksToArtifacts(Iterable<Artifact> artifacts) {
1058 for (Artifact artifact : artifacts) {
1059 addSymlink(artifact.getExecPath(), artifact);
1060 }
1061 return this;
1062 }
1063
1064 /**
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +02001065 * Add extra middlemen artifacts that should be built by reverse dependency binaries. This
1066 * method exists solely to support the unfortunate legacy behavior of some rules; new uses
1067 * should not be added.
1068 */
1069 public Builder addLegacyExtraMiddleman(Artifact middleman) {
1070 Preconditions.checkArgument(middleman.isMiddlemanArtifact(), middleman);
1071 extraMiddlemenBuilder.add(middleman);
1072 return this;
1073 }
1074
1075 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001076 * Add the other {@link Runfiles} object transitively.
1077 */
1078 public Builder merge(Runfiles runfiles) {
Googlerd7a780e2017-03-14 18:10:02 +00001079 return merge(runfiles, true, true);
1080 }
1081
1082 /**
1083 * Add the other {@link Runfiles} object transitively, but don't merge
1084 * unconditional artifacts.
1085 */
1086 public Builder mergeExceptUnconditionalArtifacts(Runfiles runfiles) {
1087 return merge(runfiles, false, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001088 }
1089
1090 /**
1091 * Add the other {@link Runfiles} object transitively, but don't merge
1092 * pruning manifests.
1093 */
1094 public Builder mergeExceptPruningManifests(Runfiles runfiles) {
Googlerd7a780e2017-03-14 18:10:02 +00001095 return merge(runfiles, true, false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001096 }
1097
1098 /**
1099 * Add the other {@link Runfiles} object transitively, with the option to include or exclude
1100 * pruning manifests in the merge.
1101 */
Googlerd7a780e2017-03-14 18:10:02 +00001102 private Builder merge(Runfiles runfiles, boolean includeUnconditionalArtifacts,
1103 boolean includePruningManifests) {
Googlerc85af312016-03-10 17:55:17 +00001104 // Propagate the most strict conflict checking from merged-in runfiles
1105 if (runfiles.conflictPolicy.compareTo(conflictPolicy) > 0) {
1106 conflictPolicy = runfiles.conflictPolicy;
1107 }
Ulf Adams4226be22015-09-03 17:00:54 +00001108 if (runfiles.isEmpty()) {
1109 return this;
1110 }
1111 // The suffix should be the same within any blaze build, except for the EMPTY runfiles, which
1112 // may have an empty suffix, but that is covered above.
1113 Preconditions.checkArgument(suffix.equals(runfiles.suffix));
Googlerd7a780e2017-03-14 18:10:02 +00001114 if (includeUnconditionalArtifacts) {
1115 artifactsBuilder.addTransitive(runfiles.getUnconditionalArtifacts());
1116 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001117 symlinksBuilder.addTransitive(runfiles.getSymlinks());
1118 rootSymlinksBuilder.addTransitive(runfiles.getRootSymlinks());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001119 if (includePruningManifests) {
1120 pruningManifestsBuilder.addTransitive(runfiles.getPruningManifests());
1121 }
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +02001122 extraMiddlemenBuilder.addTransitive(runfiles.getExtraMiddlemen());
Michajlo Matijkiwac879b92015-04-15 21:28:33 +00001123 if (emptyFilesSupplier == DUMMY_EMPTY_FILES_SUPPLIER) {
1124 emptyFilesSupplier = runfiles.getEmptyFilesProvider();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001125 } else {
Michajlo Matijkiwac879b92015-04-15 21:28:33 +00001126 EmptyFilesSupplier otherSupplier = runfiles.getEmptyFilesProvider();
1127 Preconditions.checkState((otherSupplier == DUMMY_EMPTY_FILES_SUPPLIER)
1128 || emptyFilesSupplier.equals(otherSupplier));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001129 }
1130 return this;
1131 }
1132
1133 private static Iterable<TransitiveInfoCollection> getNonDataDeps(RuleContext ruleContext) {
1134 return Iterables.concat(
1135 // TODO(bazel-team): This line shouldn't be here. Removing it requires that no rules have
1136 // dependent rules in srcs (except for filegroups and such), but always in deps.
1137 // TODO(bazel-team): DONT_CHECK is not optimal here. Rules that use split configs need to
1138 // be changed not to call into here.
1139 getPrerequisites(ruleContext, "srcs", Mode.DONT_CHECK),
1140 getPrerequisites(ruleContext, "deps", Mode.DONT_CHECK));
1141 }
1142
1143 /**
1144 * For the specified attribute "attributeName" (which must be of type list(label)), resolves all
1145 * the labels into ConfiguredTargets (for the same configuration as this one) and returns them
1146 * as a list.
1147 *
1148 * <p>If the rule does not have the specified attribute, returns the empty list.
1149 */
1150 private static Iterable<? extends TransitiveInfoCollection> getPrerequisites(
1151 RuleContext ruleContext, String attributeName, Mode mode) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001152 if (ruleContext.getRule().isAttrDefined(attributeName, BuildType.LABEL_LIST)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001153 return ruleContext.getPrerequisites(attributeName, mode);
1154 } else {
1155 return Collections.emptyList();
1156 }
1157 }
1158 }
Jon Brandvein8d100342016-12-28 17:04:37 +00001159
cparsons90f70b62018-05-01 12:45:02 -07001160 @Override
1161 public Runfiles merge(RunfilesApi other) {
Jon Brandvein8d100342016-12-28 17:04:37 +00001162 Runfiles.Builder builder = new Runfiles.Builder(suffix, false);
1163 builder.merge(this);
cparsons90f70b62018-05-01 12:45:02 -07001164 builder.merge((Runfiles) other);
Jon Brandvein8d100342016-12-28 17:04:37 +00001165 return builder.build();
1166 }
fellyc31c8c92018-08-29 15:07:59 -07001167
1168 /**
1169 * Fingerprint this {@link Runfiles} tree.
1170 */
1171 public void fingerprint(Fingerprint fp) {
1172 fp.addBoolean(getLegacyExternalRunfiles());
1173 fp.addPath(getSuffix());
mstaib0e81f9a2018-09-17 14:30:28 -07001174 Map<PathFragment, Artifact> symlinks = getSymlinksAsMap(null);
fellyc31c8c92018-08-29 15:07:59 -07001175 fp.addInt(symlinks.size());
1176 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
1177 fp.addPath(symlink.getKey());
1178 fp.addPath(symlink.getValue().getExecPath());
1179 }
mstaib0e81f9a2018-09-17 14:30:28 -07001180 Map<PathFragment, Artifact> rootSymlinks = getRootSymlinksAsMap(null);
fellyc31c8c92018-08-29 15:07:59 -07001181 fp.addInt(rootSymlinks.size());
1182 for (Map.Entry<PathFragment, Artifact> rootSymlink : rootSymlinks.entrySet()) {
1183 fp.addPath(rootSymlink.getKey());
1184 fp.addPath(rootSymlink.getValue().getExecPath());
1185 }
1186
1187 for (Artifact artifact : getArtifacts()) {
1188 fp.addPath(artifact.getRootRelativePath());
1189 fp.addPath(artifact.getExecPath());
1190 }
brandjon08561e32018-09-25 06:59:00 -07001191
1192 for (String name : getEmptyFilenames()) {
1193 fp.addString(name);
1194 }
fellyc31c8c92018-08-29 15:07:59 -07001195 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001196}