blob: 96c476934904fe89bb2e07631d6dd2734d3762a2 [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;
dannarkbe3cefc2018-12-13 11:52:45 -080027import com.google.devtools.build.lib.cmdline.LabelConstants;
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;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000035import com.google.devtools.build.lib.packages.BuildType;
cpeyser27458be2018-02-14 12:49:17 -080036import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
cpeyser27458be2018-02-14 12:49:17 -080037import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
cparsons90f70b62018-05-01 12:45:02 -070038import com.google.devtools.build.lib.skylarkbuildapi.RunfilesApi;
Benjamin Petersoneadc65a2018-11-07 09:22:40 -080039import com.google.devtools.build.lib.skylarkbuildapi.SymlinkEntryApi;
Googlerd21a0d12019-11-21 13:52:30 -080040import com.google.devtools.build.lib.syntax.Depset;
adonovanf5262c52020-04-02 09:25:14 -070041import com.google.devtools.build.lib.syntax.Location;
Googler34f70582019-11-25 12:27:34 -080042import com.google.devtools.build.lib.syntax.Printer;
Googlerfb631cf2019-11-21 11:19:32 -080043import com.google.devtools.build.lib.syntax.SkylarkType;
fellyc31c8c92018-08-29 15:07:59 -070044import com.google.devtools.build.lib.util.Fingerprint;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010046import java.io.BufferedReader;
47import java.io.IOException;
48import java.io.InputStreamReader;
49import java.util.Collections;
50import java.util.HashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051import java.util.LinkedHashMap;
52import java.util.Map;
Googler3ed13c82016-03-07 21:53:29 +000053import java.util.Objects;
Michajlo Matijkiwac879b92015-04-15 21:28:33 +000054import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import javax.annotation.Nullable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056
57/**
58 * An object that encapsulates runfiles. Conceptually, the runfiles are a map of paths to files,
59 * forming a symlink tree.
60 *
61 * <p>In order to reduce memory consumption, this map is not explicitly stored here, but instead as
62 * a combination of four parts: artifacts placed at their root-relative paths, source tree symlinks,
63 * root symlinks (outside of the source tree), and artifacts included as parts of "pruning
64 * manifests" (see {@link PruningManifest}).
65 */
66@Immutable
cpeyser27458be2018-02-14 12:49:17 -080067@AutoCodec
cparsons90f70b62018-05-01 12:45:02 -070068public final class Runfiles implements RunfilesApi {
Lukacs Berki5ab30d62015-03-11 22:06:04 +000069 private static final Function<SymlinkEntry, Artifact> TO_ARTIFACT =
70 new Function<SymlinkEntry, Artifact>() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071 @Override
Lukacs Berki5ab30d62015-03-11 22:06:04 +000072 public Artifact apply(SymlinkEntry input) {
73 return input.getArtifact();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074 }
75 };
76
shahan20f35b42018-02-28 15:57:33 -080077 private static class DummyEmptyFilesSupplier implements EmptyFilesSupplier {
78 private DummyEmptyFilesSupplier() {}
cpeyser27458be2018-02-14 12:49:17 -080079
80 @Override
81 public Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths) {
82 return ImmutableList.of();
83 }
84 }
85
shahan20f35b42018-02-28 15:57:33 -080086 @AutoCodec @AutoCodec.VisibleForSerialization
87 static final EmptyFilesSupplier DUMMY_EMPTY_FILES_SUPPLIER = new DummyEmptyFilesSupplier();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010088
Lukacs Berki5ab30d62015-03-11 22:06:04 +000089 /**
90 * An entry in the runfiles map.
Googlerc85af312016-03-10 17:55:17 +000091 *
92 * <p>build-runfiles.cc enforces the following constraints: The PathFragment must not be an
Eric Dobson20bd2742016-11-25 21:59:09 +000093 * absolute path, nor contain "..". Overlapping runfiles links are also refused. This is the case
Googlerc85af312016-03-10 17:55:17 +000094 * where you ask to create a link to "foo" and also "foo/bar.txt". I.e. you're asking it to make
95 * "foo" both a file (symlink) and a directory.
96 *
97 * <p>Links to directories are heavily discouraged.
Lukacs Berki5ab30d62015-03-11 22:06:04 +000098 */
99 //
100 // O intrepid fixer or bugs and implementor of features, dare not to add a .equals() method
101 // to this class, lest you condemn yourself, or a fellow other developer to spending two
102 // delightful hours in a fancy hotel on a Chromebook that is utterly unsuitable for Java
103 // development to figure out what went wrong, just like I just did.
104 //
105 // The semantics of the symlinks nested set dictates that later entries overwrite earlier
106 // ones. However, the semantics of nested sets dictate that if there are duplicate entries, they
107 // are only returned once in the iterator.
108 //
109 // These two things, innocent when taken alone, result in the effect that when there are three
110 // entries for the same path, the first one and the last one the same, and the middle one
111 // different, the *middle* one will take effect: the middle one overrides the first one, and the
112 // first one prevents the last one from appearing on the iterator.
113 //
114 // The lack of a .equals() method prevents this by making the first entry in the above case not
115 // equals to the third one if they are not the same instance (which they almost never are)
116 //
117 // Goodnight, prince(ss)?, and sweet dreams.
cpeyser27458be2018-02-14 12:49:17 -0800118 @AutoCodec
119 @VisibleForSerialization
Benjamin Petersoneadc65a2018-11-07 09:22:40 -0800120 static final class SymlinkEntry implements SymlinkEntryApi {
Googler40c11232019-11-26 09:35:55 -0800121
122 static final SkylarkType TYPE = SkylarkType.of(SymlinkEntry.class);
123
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000124 private final PathFragment path;
125 private final Artifact artifact;
126
cpeyser27458be2018-02-14 12:49:17 -0800127 @VisibleForSerialization
128 SymlinkEntry(PathFragment path, Artifact artifact) {
Ulf Adams8594de82017-03-20 16:30:11 +0000129 this.path = Preconditions.checkNotNull(path);
130 this.artifact = Preconditions.checkNotNull(artifact);
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000131 }
132
Benjamin Petersoneadc65a2018-11-07 09:22:40 -0800133 @Override
134 public String getPathString() {
135 return getPath().getPathString();
136 }
137
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000138 public PathFragment getPath() {
139 return path;
140 }
141
Benjamin Petersoneadc65a2018-11-07 09:22:40 -0800142 @Override
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000143 public Artifact getArtifact() {
144 return artifact;
145 }
Eric Dobson20bd2742016-11-25 21:59:09 +0000146
Ulf Adams8594de82017-03-20 16:30:11 +0000147 @Override
Eric Dobson20bd2742016-11-25 21:59:09 +0000148 public boolean isImmutable() {
149 return true;
150 }
151
Ulf Adams8594de82017-03-20 16:30:11 +0000152 @Override
Googler34f70582019-11-25 12:27:34 -0800153 public void repr(Printer printer) {
vladmos46907932017-06-30 14:01:45 +0200154 printer.append("SymlinkEntry(path = ");
Benjamin Petersoneadc65a2018-11-07 09:22:40 -0800155 printer.repr(getPathString());
156 printer.append(", target_file = ");
vladmos46907932017-06-30 14:01:45 +0200157 getArtifact().repr(printer);
158 printer.append(")");
Eric Dobson20bd2742016-11-25 21:59:09 +0000159 }
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000160 }
161
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100162 // It is important to declare this *after* the DUMMY_SYMLINK_EXPANDER to avoid NPEs
163 public static final Runfiles EMPTY = new Builder().build();
164
Kristina Chodorowb579b942015-03-02 15:51:58 +0000165 /**
166 * The directory to put all runfiles under.
167 *
168 * <p>Using "foo" will put runfiles under &lt;target&gt;.runfiles/foo.</p>
Googler3ed13c82016-03-07 21:53:29 +0000169 *
170 * <p>This is either set to the workspace name, or is empty.
Kristina Chodorowb579b942015-03-02 15:51:58 +0000171 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000172 private final PathFragment suffix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100173
174 /**
175 * The artifacts that should *always* be present in the runfiles directory. These are
176 * differentiated from the artifacts that may or may not be included by a pruning manifest
177 * (see {@link PruningManifest} below).
178 *
179 * <p>This collection may not include any middlemen. These artifacts will be placed at a location
180 * that corresponds to the root-relative path of each artifact. It's possible for several
181 * artifacts to have the same root-relative path, in which case the last one will win.
182 */
183 private final NestedSet<Artifact> unconditionalArtifacts;
184
185 /**
186 * A map of symlinks that should be present in the runfiles directory. In general, the symlink can
187 * be determined from the artifact by using the root-relative path, so this should only be used
188 * for cases where that isn't possible.
189 *
190 * <p>This may include runfiles symlinks from the root of the runfiles tree.
191 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000192 private final NestedSet<SymlinkEntry> symlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100193
194 /**
195 * A map of symlinks that should be present above the runfiles directory. These are useful for
196 * certain rule types like AppEngine apps which have root level config files outside of the
197 * regular source tree.
198 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000199 private final NestedSet<SymlinkEntry> rootSymlinks;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200
201 /**
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200202 * A set of middlemen artifacts. {@link RuleConfiguredTargetBuilder} adds these to the {@link
203 * FilesToRunProvider} of binaries that include this runfiles tree in their runfiles.
204 */
205 private final NestedSet<Artifact> extraMiddlemen;
206
207 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000208 * Interface used for adding empty files to the runfiles at the last minute. Mainly to support
209 * python-related rules adding __init__.py files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100210 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000211 public interface EmptyFilesSupplier {
212 /** Calculate additional empty files to add based on the existing manifest paths. */
213 Iterable<PathFragment> getExtraPaths(Set<PathFragment> manifestPaths);
214 }
215
216 /** Generates extra (empty file) inputs. */
217 private final EmptyFilesSupplier emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100218
219 /**
Googler3ed13c82016-03-07 21:53:29 +0000220 * Behavior upon finding a conflict between two runfile entries. A conflict means that two
221 * different artifacts have the same runfiles path specified. For example, adding artifact
Googlerc85af312016-03-10 17:55:17 +0000222 * "a.foo" at path "bar" when there is already an artifact "b.foo" at path "bar". The policies
223 * are ordered from least strict to most strict.
Googler3ed13c82016-03-07 21:53:29 +0000224 *
225 * <p>Note that conflicts are found relatively late, when the manifest file is created, not when
226 * the symlinks are added to runfiles.
227 *
228 * <p>If no EventHandler is available, all values are treated as IGNORE.
229 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000230 public enum ConflictPolicy {
Googler3ed13c82016-03-07 21:53:29 +0000231 IGNORE,
232 WARN,
233 ERROR,
234 }
235
236 /** Policy for this Runfiles tree */
237 private ConflictPolicy conflictPolicy = ConflictPolicy.IGNORE;
238
239 /**
cpeyser27458be2018-02-14 12:49:17 -0800240 * Defines a set of artifacts that may or may not be included in the runfiles directory and a
241 * manifest file that makes that determination. These are applied on top of any artifacts
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242 * specified in {@link #unconditionalArtifacts}.
243 *
cpeyser27458be2018-02-14 12:49:17 -0800244 * <p>The incentive behind this is to enable execution-phase "pruning" of runfiles. Anything set
245 * in unconditionalArtifacts is hard-set in Blaze's analysis phase, and thus unchangeable in
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100246 * response to execution phase results. This isn't always convenient. For example, say we have an
247 * action that consumes a set of "possible" runtime dependencies for a source file, parses that
248 * file for "import a.b.c" statements, and outputs a manifest of the actual dependencies that are
249 * referenced and thus really needed. This can reduce the size of the runfiles set, but we can't
250 * use this information until the manifest output is available.
251 *
252 * <p>Only artifacts present in the candidate set AND the manifest output make it into the
253 * runfiles tree. The candidate set requirement guarantees that analysis-time dependencies are a
254 * superset of the pruned dependencies, so undeclared inclusions (which can break build
255 * correctness) aren't possible.
256 */
cpeyser27458be2018-02-14 12:49:17 -0800257 @AutoCodec
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100258 public static class PruningManifest {
259 private final NestedSet<Artifact> candidateRunfiles;
260 private final Artifact manifestFile;
261
262 /**
263 * Creates a new pruning manifest.
264 *
265 * @param candidateRunfiles set of possible artifacts that the manifest file may reference
266 * @param manifestFile the manifest file, expected to be a newline-separated list of
267 * source tree root-relative paths (i.e. "my/package/myfile.txt"). Anything that can't be
268 * resolved back to an entry in candidateRunfiles is ignored and will *not* make it into
269 * the runfiles tree.
270 */
271 public PruningManifest(NestedSet<Artifact> candidateRunfiles, Artifact manifestFile) {
272 this.candidateRunfiles = candidateRunfiles;
273 this.manifestFile = manifestFile;
274 }
275
276 public NestedSet<Artifact> getCandidateRunfiles() {
277 return candidateRunfiles;
278 }
279
280 public Artifact getManifestFile() {
281 return manifestFile;
282 }
283 }
284
285 /**
286 * The pruning manifests that should be applied to these runfiles.
287 */
288 private final NestedSet<PruningManifest> pruningManifests;
289
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000290 /**
291 * If external runfiles should be created under .runfiles/wsname/external/repo as well as
292 * .runfiles/repo.
293 */
294 private final boolean legacyExternalRunfiles;
295
cpeyser27458be2018-02-14 12:49:17 -0800296 @AutoCodec.Instantiator
297 @VisibleForSerialization
298 Runfiles(
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000299 PathFragment suffix,
cpeyser27458be2018-02-14 12:49:17 -0800300 NestedSet<Artifact> unconditionalArtifacts,
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000301 NestedSet<SymlinkEntry> symlinks,
302 NestedSet<SymlinkEntry> rootSymlinks,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100303 NestedSet<PruningManifest> pruningManifests,
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200304 NestedSet<Artifact> extraMiddlemen,
Googlerc85af312016-03-10 17:55:17 +0000305 EmptyFilesSupplier emptyFilesSupplier,
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000306 ConflictPolicy conflictPolicy,
307 boolean legacyExternalRunfiles) {
Ulf Adams4226be22015-09-03 17:00:54 +0000308 this.suffix = suffix;
cpeyser27458be2018-02-14 12:49:17 -0800309 this.unconditionalArtifacts = Preconditions.checkNotNull(unconditionalArtifacts);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100310 this.symlinks = Preconditions.checkNotNull(symlinks);
311 this.rootSymlinks = Preconditions.checkNotNull(rootSymlinks);
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200312 this.extraMiddlemen = Preconditions.checkNotNull(extraMiddlemen);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100313 this.pruningManifests = Preconditions.checkNotNull(pruningManifests);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000314 this.emptyFilesSupplier = Preconditions.checkNotNull(emptyFilesSupplier);
Googlerc85af312016-03-10 17:55:17 +0000315 this.conflictPolicy = conflictPolicy;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000316 this.legacyExternalRunfiles = legacyExternalRunfiles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100317 }
318
319 /**
Kristina Chodorowb579b942015-03-02 15:51:58 +0000320 * Returns the runfiles' suffix.
321 */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000322 public PathFragment getSuffix() {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000323 return suffix;
324 }
325
326 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100327 * Returns the artifacts that are unconditionally included in the runfiles (as opposed to
328 * pruning manifest candidates, which may or may not be included).
329 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100330 public NestedSet<Artifact> getUnconditionalArtifacts() {
331 return unconditionalArtifacts;
332 }
333
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200334 public NestedSet<Artifact> getExtraMiddlemen() {
335 return extraMiddlemen;
336 }
337
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100338 /**
John Caterd7928422017-01-03 20:06:59 +0000339 * Returns the collection of runfiles as artifacts, including both unconditional artifacts and
340 * pruning manifest candidates.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100341 */
cparsons90f70b62018-05-01 12:45:02 -0700342 @Override
Googlerd21a0d12019-11-21 13:52:30 -0800343 public Depset /*<Artifact>*/ getArtifactsForStarlark() {
344 return Depset.of(Artifact.TYPE, getArtifacts());
Googlerfb631cf2019-11-21 11:19:32 -0800345 }
346
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100347 public NestedSet<Artifact> getArtifacts() {
348 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
Benjamin Peterson899a3e72018-06-06 14:18:29 -0700349 allArtifacts.addTransitive(unconditionalArtifacts);
ulfjack7a052892019-12-17 01:21:54 -0800350 for (PruningManifest manifest : getPruningManifests().toList()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100351 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
352 }
353 return allArtifacts.build();
354 }
355
John Caterd7928422017-01-03 20:06:59 +0000356 /** Returns the symlinks. */
cparsons90f70b62018-05-01 12:45:02 -0700357 @Override
Googlerd21a0d12019-11-21 13:52:30 -0800358 public Depset /*<SymlinkEntry>*/ getSymlinksForStarlark() {
Googler40c11232019-11-26 09:35:55 -0800359 return Depset.of(SymlinkEntry.TYPE, symlinks);
Googlerfb631cf2019-11-21 11:19:32 -0800360 }
361
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000362 public NestedSet<SymlinkEntry> getSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100363 return symlinks;
364 }
365
cparsons90f70b62018-05-01 12:45:02 -0700366 @Override
Googlerd21a0d12019-11-21 13:52:30 -0800367 public Depset /*<String>*/ getEmptyFilenamesForStarlark() {
368 return Depset.of(SkylarkType.STRING, getEmptyFilenames());
Googlerfb631cf2019-11-21 11:19:32 -0800369 }
370
Brian Silverman4321b672016-04-15 12:16:04 +0000371 public NestedSet<String> getEmptyFilenames() {
Benjamin Petersonba2f3912018-06-18 08:54:36 -0700372 Set<PathFragment> manifestKeys =
373 Streams.concat(
ulfjack7a052892019-12-17 01:21:54 -0800374 symlinks.toList().stream().map(SymlinkEntry::getPath),
375 getArtifacts().toList().stream().map(Artifact::getRootRelativePath))
Benjamin Petersonba2f3912018-06-18 08:54:36 -0700376 .collect(ImmutableSet.toImmutableSet());
377 Iterable<PathFragment> emptyKeys = emptyFilesSupplier.getExtraPaths(manifestKeys);
378 return NestedSetBuilder.<String>stableOrder()
379 .addAll(
380 Streams.stream(emptyKeys)
381 .map(PathFragment::toString)
382 .collect(ImmutableList.toImmutableList()))
383 .build();
Brian Silverman4321b672016-04-15 12:16:04 +0000384 }
385
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100386 /**
387 * Returns the symlinks as a map from path fragment to artifact.
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000388 *
389 * @param checker If not null, check for conflicts using this checker.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100390 */
mstaib0e81f9a2018-09-17 14:30:28 -0700391 public Map<PathFragment, Artifact> getSymlinksAsMap(@Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000392 return entriesToMap(symlinks, checker);
393 }
394
395 /**
396 * @param eventHandler Used for throwing an error if we have an obscuring runlink.
397 * May be null, in which case obscuring symlinks are silently discarded.
398 * @param location Location for reporter. Ignored if reporter is null.
399 * @param workingManifest Manifest to be checked for obscuring symlinks.
400 * @return map of source file names mapped to their location on disk.
401 */
402 @VisibleForTesting
403 static Map<PathFragment, Artifact> filterListForObscuringSymlinks(
404 EventHandler eventHandler, Location location, Map<PathFragment, Artifact> workingManifest) {
405 Map<PathFragment, Artifact> newManifest = new HashMap<>();
406
407 outer:
cushon2b732532020-01-16 15:40:13 -0800408 for (Map.Entry<PathFragment, Artifact> entry : workingManifest.entrySet()) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000409 PathFragment source = entry.getKey();
410 Artifact symlink = entry.getValue();
411 // drop nested entries; warn if this changes anything
412 int n = source.segmentCount();
413 for (int j = 1; j < n; ++j) {
414 PathFragment prefix = source.subFragment(0, n - j);
415 Artifact ancestor = workingManifest.get(prefix);
416 if (ancestor != null) {
417 // This is an obscuring symlink, so just drop it and move on if there's no reporter.
418 if (eventHandler == null) {
419 continue outer;
420 }
421 PathFragment suffix = source.subFragment(n - j, n);
shahanb3ba5142018-04-06 19:17:12 -0700422 PathFragment viaAncestor = ancestor.getExecPath().getRelative(suffix);
423 PathFragment expected = symlink.getExecPath();
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000424 if (!viaAncestor.equals(expected)) {
shahanb3ba5142018-04-06 19:17:12 -0700425 eventHandler.handle(
426 Event.warn(
427 location,
428 "runfiles symlink "
429 + source
430 + " -> "
431 + expected
432 + " obscured by "
433 + prefix
434 + " -> "
435 + ancestor.getExecPath()));
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000436 }
437 continue outer;
438 }
439 }
440 newManifest.put(entry.getKey(), entry.getValue());
441 }
442 return newManifest;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100443 }
444
445 /**
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000446 * Returns the symlinks as a map from PathFragment to Artifact.
447 *
448 * @param eventHandler Used for throwing an error if we have an obscuring runlink within the
Yue Gan5e60e382017-03-20 10:58:11 +0000449 * normal source tree entries, or runfile conflicts. May be null, in which case obscuring
450 * symlinks are silently discarded, and conflicts are overwritten.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100451 * @param location Location for eventHandler warnings. Ignored if eventHandler is null.
felly56fd4fe2018-09-05 10:54:54 -0700452 * @param resolver The {@link ArtifactPathResolver} to use for the pruning manifest, if present.
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000453 * @return Map<PathFragment, Artifact> path fragment to artifact, of normal source tree entries
Yue Gan5e60e382017-03-20 10:58:11 +0000454 * and elements that live outside the source tree. Null values represent empty input files.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100455 */
felly56fd4fe2018-09-05 10:54:54 -0700456 public Map<PathFragment, Artifact> getRunfilesInputs(EventHandler eventHandler, Location location,
457 ArtifactPathResolver resolver) throws IOException {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000458 ConflictChecker checker = new ConflictChecker(conflictPolicy, eventHandler, location);
459 Map<PathFragment, Artifact> manifest = getSymlinksAsMap(checker);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100460 // Add unconditional artifacts (committed to inclusion on construction of runfiles).
ulfjack7a052892019-12-17 01:21:54 -0800461 for (Artifact artifact : getUnconditionalArtifacts().toList()) {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000462 checker.put(manifest, artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100463 }
464
465 // Add conditional artifacts (only included if they appear in a pruning manifest).
ulfjack7a052892019-12-17 01:21:54 -0800466 for (Runfiles.PruningManifest pruningManifest : getPruningManifests().toList()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100467 // This map helps us convert from source tree root-relative paths back to artifacts.
468 Map<String, Artifact> allowedRunfiles = new HashMap<>();
ulfjack7a052892019-12-17 01:21:54 -0800469 for (Artifact artifact : pruningManifest.getCandidateRunfiles().toList()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100470 allowedRunfiles.put(artifact.getRootRelativePath().getPathString(), artifact);
471 }
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000472 try (BufferedReader reader = new BufferedReader(
felly56fd4fe2018-09-05 10:54:54 -0700473 new InputStreamReader(resolver.toPath(pruningManifest.getManifestFile()).getInputStream()))) {
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000474 String line;
475 while ((line = reader.readLine()) != null) {
476 Artifact artifact = allowedRunfiles.get(line);
477 if (artifact != null) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000478 checker.put(manifest, artifact.getRootRelativePath(), artifact);
Miguel Alcon Pinto53fb4d02015-11-04 17:36:02 +0000479 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100480 }
481 }
482 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000483 manifest = filterListForObscuringSymlinks(eventHandler, location, manifest);
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000484
485 // TODO(bazel-team): Create /dev/null-like Artifact to avoid nulls?
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000486 for (PathFragment extraPath : emptyFilesSupplier.getExtraPaths(manifest.keySet())) {
487 checker.put(manifest, extraPath, null);
488 }
489
490 // Copy manifest map to another manifest map, prepending the workspace name to every path.
491 // E.g. for workspace "myworkspace", the runfile entry "mylib.so"->"/path/to/mylib.so" becomes
492 // "myworkspace/mylib.so"->"/path/to/mylib.so".
Kristina Chodorowd9121972016-04-25 17:45:55 +0000493 ManifestBuilder builder = new ManifestBuilder(suffix, legacyExternalRunfiles);
494 builder.addUnderWorkspace(manifest, checker);
Michajlo Matijkiw9c569572015-05-07 20:09:14 +0000495
Googler3ed13c82016-03-07 21:53:29 +0000496 // Finally add symlinks relative to the root of the runfiles tree, on top of everything else.
497 // This operation is always checked for conflicts, to match historical behavior.
498 if (conflictPolicy == ConflictPolicy.IGNORE) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000499 checker = new ConflictChecker(ConflictPolicy.WARN, eventHandler, location);
Googler3ed13c82016-03-07 21:53:29 +0000500 }
Kristina Chodorowd9121972016-04-25 17:45:55 +0000501 builder.add(getRootSymlinksAsMap(checker), checker);
502 return builder.build();
503 }
504
505 /**
506 * Helper class to handle munging the paths of external artifacts.
507 */
508 @VisibleForTesting
509 static final class ManifestBuilder {
510 // Manifest of paths to artifacts. Path fragments are relative to the .runfiles directory.
511 private final Map<PathFragment, Artifact> manifest;
512 private final PathFragment workspaceName;
513 private final boolean legacyExternalRunfiles;
514 // Whether we saw the local workspace name in the runfiles. If legacyExternalRunfiles is true,
515 // then this is true, as anything under external/ will also have a runfile under the local
516 // workspace.
517 private boolean sawWorkspaceName;
518
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000519 public ManifestBuilder(
Kristina Chodorowd9121972016-04-25 17:45:55 +0000520 PathFragment workspaceName, boolean legacyExternalRunfiles) {
521 this.manifest = new HashMap<>();
522 this.workspaceName = workspaceName;
523 this.legacyExternalRunfiles = legacyExternalRunfiles;
524 this.sawWorkspaceName = legacyExternalRunfiles;
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000525 }
526
mstaib0e81f9a2018-09-17 14:30:28 -0700527 /**
528 * Adds a map under the workspaceName.
529 */
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000530 public void addUnderWorkspace(
mstaib0e81f9a2018-09-17 14:30:28 -0700531 Map<PathFragment, Artifact> inputManifest, ConflictChecker checker) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000532 for (Map.Entry<PathFragment, Artifact> entry : inputManifest.entrySet()) {
533 PathFragment path = entry.getKey();
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000534 if (isUnderWorkspace(path)) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000535 sawWorkspaceName = true;
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000536 checker.put(manifest, workspaceName.getRelative(path), entry.getValue());
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000537 } else {
538 if (legacyExternalRunfiles) {
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000539 checker.put(manifest, workspaceName.getRelative(path), entry.getValue());
Laurent Le Brunf3cf98f2016-06-17 13:36:24 +0000540 }
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000541 // Always add the non-legacy .runfiles/repo/whatever path.
542 checker.put(manifest, getExternalPath(path), entry.getValue());
Kristina Chodorowd9121972016-04-25 17:45:55 +0000543 }
544 }
545 }
546
mstaib0e81f9a2018-09-17 14:30:28 -0700547 /**
548 * Adds a map to the root directory.
549 */
550 public void add(Map<PathFragment, Artifact> inputManifest, ConflictChecker checker) {
Kristina Chodorowd9121972016-04-25 17:45:55 +0000551 for (Map.Entry<PathFragment, Artifact> entry : inputManifest.entrySet()) {
552 checker.put(manifest, checkForWorkspace(entry.getKey()), entry.getValue());
553 }
554 }
555
556 /**
557 * Returns the manifest, adding the workspaceName directory if it is not already present.
558 */
559 public Map<PathFragment, Artifact> build() {
560 if (!sawWorkspaceName) {
561 // If we haven't seen it and we have seen other files, add the workspace name directory.
562 // It might not be there if all of the runfiles are from other repos (and then running from
563 // x.runfiles/ws will fail, because ws won't exist). We can't tell Runfiles to create a
564 // directory, so instead this creates a hidden file inside the desired directory.
565 manifest.put(workspaceName.getRelative(".runfile"), null);
566 }
567 return manifest;
568 }
569
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000570 private PathFragment getExternalPath(PathFragment path) {
dannarkbe3cefc2018-12-13 11:52:45 -0800571 return checkForWorkspace(path.relativeTo(LabelConstants.EXTERNAL_PACKAGE_NAME));
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000572 }
573
Kristina Chodorowd9121972016-04-25 17:45:55 +0000574 private PathFragment checkForWorkspace(PathFragment path) {
Lukacs Berki4475bf12017-03-20 14:35:46 +0000575 sawWorkspaceName = sawWorkspaceName
576 || path.getSegment(0).equals(workspaceName.getPathString());
Kristina Chodorowd9121972016-04-25 17:45:55 +0000577 return path;
578 }
579
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000580 private static boolean isUnderWorkspace(PathFragment path) {
dannarkbe3cefc2018-12-13 11:52:45 -0800581 return !path.startsWith(LabelConstants.EXTERNAL_PACKAGE_NAME);
Kristina Chodorowd9121972016-04-25 17:45:55 +0000582 }
583 }
584
585 boolean getLegacyExternalRunfiles() {
586 return legacyExternalRunfiles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100587 }
588
Benjamin Petersoneadc65a2018-11-07 09:22:40 -0800589 /** Returns the root symlinks. */
590 @Override
Googlerd21a0d12019-11-21 13:52:30 -0800591 public Depset /*<SymlinkEntry>*/ getRootSymlinksForStarlark() {
Googler40c11232019-11-26 09:35:55 -0800592 return Depset.of(SymlinkEntry.TYPE, rootSymlinks);
Googlerfb631cf2019-11-21 11:19:32 -0800593 }
594
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000595 public NestedSet<SymlinkEntry> getRootSymlinks() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100596 return rootSymlinks;
597 }
598
599 /**
Googler3ed13c82016-03-07 21:53:29 +0000600 * Returns the root symlinks as a map from path fragment to artifact.
601 *
602 * @param checker If not null, check for conflicts using this checker.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100603 */
mstaib0e81f9a2018-09-17 14:30:28 -0700604 public Map<PathFragment, Artifact> getRootSymlinksAsMap(@Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000605 return entriesToMap(rootSymlinks, checker);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100606 }
607
608 /**
609 * Returns the unified map of path fragments to artifacts, taking both artifacts and symlinks into
610 * account.
611 */
612 public Map<PathFragment, Artifact> asMapWithoutRootSymlinks() {
mstaib0e81f9a2018-09-17 14:30:28 -0700613 Map<PathFragment, Artifact> result = entriesToMap(symlinks, null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100614 // If multiple artifacts have the same root-relative path, the last one in the list will win.
615 // That is because the runfiles tree cannot contain the same artifact for different
616 // configurations, because it only uses root-relative paths.
ulfjack7a052892019-12-17 01:21:54 -0800617 for (Artifact artifact : unconditionalArtifacts.toList()) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000618 result.put(artifact.getRootRelativePath(), artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100619 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000620 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100621 }
622
623 /**
624 * Returns the pruning manifests specified for this runfiles tree.
625 */
626 public NestedSet<PruningManifest> getPruningManifests() {
627 return pruningManifests;
628 }
629
630 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000631 * Returns the manifest expander specified for this runfiles tree.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100632 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000633 private EmptyFilesSupplier getEmptyFilesProvider() {
634 return emptyFilesSupplier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100635 }
636
637 /**
638 * Returns the unified map of path fragments to artifacts, taking into account artifacts,
639 * symlinks, and pruning manifest candidates. The returned set is guaranteed to be a (not
640 * necessarily strict) superset of the actual runfiles tree created at execution time.
641 */
642 public NestedSet<Artifact> getAllArtifacts() {
643 if (isEmpty()) {
644 return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
645 }
646 NestedSetBuilder<Artifact> allArtifacts = NestedSetBuilder.stableOrder();
647 allArtifacts
648 .addTransitive(unconditionalArtifacts)
Ulf Adamscd31d3b2019-12-16 00:50:03 -0800649 .addAll(Iterables.transform(symlinks.toList(), TO_ARTIFACT))
650 .addAll(Iterables.transform(rootSymlinks.toList(), TO_ARTIFACT));
651 for (PruningManifest manifest : getPruningManifests().toList()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100652 allArtifacts.addTransitive(manifest.getCandidateRunfiles());
653 }
654 return allArtifacts.build();
655 }
656
657 /**
658 * Returns if there are no runfiles.
659 */
660 public boolean isEmpty() {
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200661 return unconditionalArtifacts.isEmpty()
662 && symlinks.isEmpty()
663 && rootSymlinks.isEmpty()
664 && pruningManifests.isEmpty()
665 && extraMiddlemen.isEmpty();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100666 }
667
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000668 /**
669 * Flatten a sequence of entries into a single map.
670 *
671 * @param entrySet Sequence of entries to add.
672 * @param checker If not null, check for conflicts with this checker, otherwise silently allow
ulfjack7a052892019-12-17 01:21:54 -0800673 * entries to overwrite previous entries.
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000674 * @return Map<PathFragment, Artifact> Map of runfile entries.
675 */
676 private static Map<PathFragment, Artifact> entriesToMap(
ulfjack7a052892019-12-17 01:21:54 -0800677 NestedSet<SymlinkEntry> entrySet, @Nullable ConflictChecker checker) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000678 checker = (checker != null) ? checker : ConflictChecker.IGNORE_CHECKER;
679 Map<PathFragment, Artifact> map = new LinkedHashMap<>();
ulfjack7a052892019-12-17 01:21:54 -0800680 for (SymlinkEntry entry : entrySet.toList()) {
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000681 checker.put(map, entry.getPath(), entry.getArtifact());
682 }
683 return map;
684 }
685
Googlerc85af312016-03-10 17:55:17 +0000686 /** Returns currently policy for conflicting symlink entries. */
687 public ConflictPolicy getConflictPolicy() {
688 return this.conflictPolicy;
689 }
690
Googler3ed13c82016-03-07 21:53:29 +0000691 /** Set whether we should warn about conflicting symlink entries. */
Googlerc85af312016-03-10 17:55:17 +0000692 public Runfiles setConflictPolicy(ConflictPolicy conflictPolicy) {
Googler3ed13c82016-03-07 21:53:29 +0000693 this.conflictPolicy = conflictPolicy;
Googlerc85af312016-03-10 17:55:17 +0000694 return this;
Googler3ed13c82016-03-07 21:53:29 +0000695 }
696
697 /**
698 * Checks for conflicts between entries in a runfiles tree while putting them in a map.
699 */
700 public static final class ConflictChecker {
701 /** Prebuilt ConflictChecker with policy set to IGNORE */
702 public static final ConflictChecker IGNORE_CHECKER =
703 new ConflictChecker(ConflictPolicy.IGNORE, null, null);
704
705 /** Behavior when a conflict is found. */
706 private final ConflictPolicy policy;
707
708 /** Used for warning on conflicts. May be null, in which case conflicts are ignored. */
709 private final EventHandler eventHandler;
710
711 /** Location for eventHandler warnings. Ignored if eventHandler is null. */
712 private final Location location;
713
mstaib0e81f9a2018-09-17 14:30:28 -0700714 /** Type of event to emit */
715 private final EventKind eventKind;
716
Googler3ed13c82016-03-07 21:53:29 +0000717 /** Construct a ConflictChecker for the given reporter with the given behavior */
718 public ConflictChecker(ConflictPolicy policy, EventHandler eventHandler, Location location) {
719 if (eventHandler == null) {
720 this.policy = ConflictPolicy.IGNORE; // Can't warn even if we wanted to
721 } else {
722 this.policy = policy;
723 }
724 this.eventHandler = eventHandler;
725 this.location = location;
mstaib0e81f9a2018-09-17 14:30:28 -0700726 this.eventKind = (policy == ConflictPolicy.ERROR) ? EventKind.ERROR : EventKind.WARNING;
Googler3ed13c82016-03-07 21:53:29 +0000727 }
728
729 /**
730 * Add an entry to a Map of symlinks, optionally reporting conflicts.
731 *
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000732 * @param map Manifest of runfile entries.
733 * @param path Path fragment to use as key in map.
Googler3ed13c82016-03-07 21:53:29 +0000734 * @param artifact Artifact to store in map. This may be null to indicate an empty file.
735 */
mstaib0e81f9a2018-09-17 14:30:28 -0700736 public void put(Map<PathFragment, Artifact> map, PathFragment path, Artifact artifact) {
ulfjackff179a32018-02-06 04:16:34 -0800737 Preconditions.checkArgument(
738 artifact == null || !artifact.isMiddlemanArtifact(), "%s", artifact);
Googler3ed13c82016-03-07 21:53:29 +0000739 if (policy != ConflictPolicy.IGNORE && map.containsKey(path)) {
740 // Previous and new entry might have value of null
741 Artifact previous = map.get(path);
742 if (!Objects.equals(previous, artifact)) {
shahanb3ba5142018-04-06 19:17:12 -0700743 String previousStr =
744 (previous == null) ? "empty file" : previous.getExecPath().toString();
745 String artifactStr =
746 (artifact == null) ? "empty file" : artifact.getExecPath().toString();
mstaib0e81f9a2018-09-17 14:30:28 -0700747 String message =
748 String.format(
749 "overwrote runfile %s, was symlink to %s, now symlink to %s",
750 path.getSafePathString(),
751 previousStr,
752 artifactStr);
753 eventHandler.handle(Event.of(eventKind, location, message));
Googler3ed13c82016-03-07 21:53:29 +0000754 }
755 }
Damien Martin-Guillerez9e4c78f2016-04-22 11:42:34 +0000756 map.put(path, artifact);
Googler3ed13c82016-03-07 21:53:29 +0000757 }
758 }
759
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100760 /**
761 * Builder for Runfiles objects.
762 */
763 public static final class Builder {
Kristina Chodorowb579b942015-03-02 15:51:58 +0000764
Googler3ed13c82016-03-07 21:53:29 +0000765 /** This is set to the workspace name */
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000766 private PathFragment suffix;
Kristina Chodorowb579b942015-03-02 15:51:58 +0000767
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100768 /**
769 * This must be COMPILE_ORDER because {@link #asMapWithoutRootSymlinks} overwrites earlier
770 * entries with later ones, so we want a post-order iteration.
771 */
772 private NestedSetBuilder<Artifact> artifactsBuilder =
773 NestedSetBuilder.compileOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000774 private NestedSetBuilder<SymlinkEntry> symlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100775 NestedSetBuilder.stableOrder();
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000776 private NestedSetBuilder<SymlinkEntry> rootSymlinksBuilder =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100777 NestedSetBuilder.stableOrder();
778 private NestedSetBuilder<PruningManifest> pruningManifestsBuilder =
779 NestedSetBuilder.stableOrder();
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200780 private NestedSetBuilder<Artifact> extraMiddlemenBuilder = NestedSetBuilder.stableOrder();
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000781 private EmptyFilesSupplier emptyFilesSupplier = DUMMY_EMPTY_FILES_SUPPLIER;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100782
Googlerc85af312016-03-10 17:55:17 +0000783 /** Build the Runfiles object with this policy */
784 private ConflictPolicy conflictPolicy = ConflictPolicy.IGNORE;
785
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000786 private final boolean legacyExternalRunfiles;
787
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100788 /**
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000789 * Only used for Runfiles.EMPTY.
790 */
Ulf Adams4226be22015-09-03 17:00:54 +0000791 private Builder() {
Kristina Chodorow7ef02512016-04-22 16:14:12 +0000792 this.suffix = PathFragment.EMPTY_FRAGMENT;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000793 this.legacyExternalRunfiles = false;
794 }
795
796 /**
797 * Creates a builder with the given suffix. Transitional constructor so that new rules don't
798 * accidentally depend on the legacy repository structure, until that option is removed.
799 *
800 * @param workspace is the string specified in workspace() in the WORKSPACE file.
801 */
802 public Builder(String workspace) {
803 this(workspace, false);
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000804 }
805
Kristina Chodorowcc8954a2015-10-27 17:23:32 +0000806 /**
807 * Creates a builder with the given suffix.
808 * @param workspace is the string specified in workspace() in the WORKSPACE file.
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000809 * @param legacyExternalRunfiles if the wsname/external/repo symlinks should also be
810 * created.
Kristina Chodorowcc8954a2015-10-27 17:23:32 +0000811 */
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000812 public Builder(String workspace, boolean legacyExternalRunfiles) {
nharmatab4060b62017-04-04 17:11:39 +0000813 this(PathFragment.create(workspace), legacyExternalRunfiles);
Jon Brandvein8d100342016-12-28 17:04:37 +0000814 }
815
816 /**
817 * Creates a builder with the given suffix.
818 * @param suffix is the PathFragment wrapping the string specified in workspace() in the
819 * WORKSPACE file.
820 * @param legacyExternalRunfiles if the wsname/external/repo symlinks should also be
821 * created.
822 */
823 private Builder(PathFragment suffix, boolean legacyExternalRunfiles) {
824 this.suffix = suffix;
Kristina Chodorow8dba2b22016-04-22 21:24:31 +0000825 this.legacyExternalRunfiles = legacyExternalRunfiles;
Kristina Chodorow9d513ca2015-08-12 15:48:30 +0000826 }
827
828 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100829 * Builds a new Runfiles object.
830 */
831 public Runfiles build() {
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +0200832 return new Runfiles(
833 suffix,
834 artifactsBuilder.build(),
835 symlinksBuilder.build(),
836 rootSymlinksBuilder.build(),
837 pruningManifestsBuilder.build(),
838 extraMiddlemenBuilder.build(),
839 emptyFilesSupplier,
840 conflictPolicy,
841 legacyExternalRunfiles);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100842 }
843
844 /**
845 * Adds an artifact to the internal collection of artifacts.
846 */
847 public Builder addArtifact(Artifact artifact) {
848 Preconditions.checkNotNull(artifact);
cushon110cba02019-02-19 12:43:11 -0800849 Preconditions.checkArgument(
850 !artifact.isMiddlemanArtifact(), "unexpected middleman artifact: %s", artifact);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100851 artifactsBuilder.add(artifact);
852 return this;
853 }
854
855 /**
856 * Adds several artifacts to the internal collection.
857 */
858 public Builder addArtifacts(Iterable<Artifact> artifacts) {
859 for (Artifact artifact : artifacts) {
860 addArtifact(artifact);
861 }
862 return this;
863 }
864
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100865 /**
Carmi Grushko75665632015-08-27 23:19:26 +0000866 * @deprecated Use {@link #addTransitiveArtifacts} instead, to prevent increased memory use.
jingwen68c57f02018-11-21 16:17:17 -0800867 * <p>See also {@link Builder#addTransitiveArtifactsWrappedInStableOrder}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100868 */
869 @Deprecated
870 public Builder addArtifacts(NestedSet<Artifact> artifacts) {
871 // Do not delete this method, or else addArtifacts(Iterable) calls with a NestedSet argument
872 // will not be flagged.
ulfjack7a052892019-12-17 01:21:54 -0800873 addArtifacts(artifacts.toList());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100874 return this;
875 }
Carmi Grushko75665632015-08-27 23:19:26 +0000876
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100877 /**
878 * Adds a nested set to the internal collection.
879 */
880 public Builder addTransitiveArtifacts(NestedSet<Artifact> artifacts) {
881 artifactsBuilder.addTransitive(artifacts);
882 return this;
883 }
884
885 /**
tomlucd032d02017-09-19 15:50:28 +0200886 * Adds a nested set to the internal collection.
887 *
888 * <p>The nested set will become wrapped in stable order. Only use this when the set of
889 * artifacts will not have conflicting root relative paths, or the wrong artifact will end up in
890 * the runfiles tree.
891 */
892 public Builder addTransitiveArtifactsWrappedInStableOrder(NestedSet<Artifact> artifacts) {
893 NestedSet<Artifact> wrappedArtifacts =
894 NestedSetBuilder.<Artifact>stableOrder().addTransitive(artifacts).build();
895 artifactsBuilder.addTransitive(wrappedArtifacts);
896 return this;
897 }
898
899 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100900 * Adds a symlink.
901 */
902 public Builder addSymlink(PathFragment link, Artifact target) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000903 symlinksBuilder.add(new SymlinkEntry(link, target));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100904 return this;
905 }
906
Ulf Adams8594de82017-03-20 16:30:11 +0000907 /** Adds several symlinks. Neither keys nor values may be null. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100908 public Builder addSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000909 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
910 symlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
911 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100912 return this;
913 }
914
915 /**
916 * Adds several symlinks as a NestedSet.
917 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000918 public Builder addSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100919 symlinksBuilder.addTransitive(symlinks);
920 return this;
921 }
922
923 /**
Googlerc85af312016-03-10 17:55:17 +0000924 * Adds a root symlink.
925 */
926 public Builder addRootSymlink(PathFragment link, Artifact target) {
Googlerc85af312016-03-10 17:55:17 +0000927 rootSymlinksBuilder.add(new SymlinkEntry(link, target));
928 return this;
929 }
930
Ulf Adams8594de82017-03-20 16:30:11 +0000931 /** Adds several root symlinks. Neither keys nor values may be null. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100932 public Builder addRootSymlinks(Map<PathFragment, Artifact> symlinks) {
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000933 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
934 rootSymlinksBuilder.add(new SymlinkEntry(symlink.getKey(), symlink.getValue()));
935 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100936 return this;
937 }
938
939 /**
940 * Adds several root symlinks as a NestedSet.
941 */
Lukacs Berki5ab30d62015-03-11 22:06:04 +0000942 public Builder addRootSymlinks(NestedSet<SymlinkEntry> symlinks) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100943 rootSymlinksBuilder.addTransitive(symlinks);
944 return this;
945 }
946
947 /**
948 * Adds a pruning manifest. See {@link PruningManifest} for an explanation.
949 */
950 public Builder addPruningManifest(PruningManifest manifest) {
951 pruningManifestsBuilder.add(manifest);
952 return this;
953 }
954
955 /**
956 * Adds several pruning manifests as a NestedSet. See {@link PruningManifest} for an
957 * explanation.
958 */
959 public Builder addPruningManifests(NestedSet<PruningManifest> manifests) {
960 pruningManifestsBuilder.addTransitive(manifests);
961 return this;
962 }
963
964 /**
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000965 * Specify a function that can create additional manifest entries based on the input entries,
966 * see {@link EmptyFilesSupplier} for more details.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100967 */
Michajlo Matijkiwac879b92015-04-15 21:28:33 +0000968 public Builder setEmptyFilesSupplier(EmptyFilesSupplier supplier) {
969 emptyFilesSupplier = Preconditions.checkNotNull(supplier);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100970 return this;
971 }
972
973 /**
974 * Merges runfiles from a given runfiles support.
975 *
976 * @param runfilesSupport the runfiles support to be merged in
977 */
978 public Builder merge(@Nullable RunfilesSupport runfilesSupport) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100979 if (runfilesSupport == null) {
980 return this;
981 }
Lukacs Berki1f820792015-03-06 18:16:10 +0000982 merge(runfilesSupport.getRunfiles());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100983 return this;
984 }
985
986 /**
987 * Adds the runfiles for a particular target and visits the transitive closure of "srcs",
988 * "deps" and "data", collecting all of their respective runfiles.
989 */
990 public Builder addRunfiles(RuleContext ruleContext,
991 Function<TransitiveInfoCollection, Runfiles> mapping) {
992 Preconditions.checkNotNull(mapping);
993 Preconditions.checkNotNull(ruleContext);
994 addDataDeps(ruleContext);
995 addNonDataDeps(ruleContext, mapping);
996 return this;
997 }
998
999 /**
1000 * Adds the files specified by a mapping from the transitive info collection to the runfiles.
1001 *
1002 * <p>Dependencies in {@code srcs} and {@code deps} are considered.
1003 */
1004 public Builder add(RuleContext ruleContext,
1005 Function<TransitiveInfoCollection, Runfiles> mapping) {
1006 Preconditions.checkNotNull(ruleContext);
1007 Preconditions.checkNotNull(mapping);
1008 for (TransitiveInfoCollection dep : getNonDataDeps(ruleContext)) {
1009 Runfiles runfiles = mapping.apply(dep);
1010 if (runfiles != null) {
1011 merge(runfiles);
1012 }
1013 }
1014
1015 return this;
1016 }
1017
1018 /**
1019 * Collects runfiles from data dependencies of a target.
1020 */
1021 public Builder addDataDeps(RuleContext ruleContext) {
gregce475d91a2018-05-25 12:18:27 -07001022 addTargets(getPrerequisites(ruleContext, "data", Mode.DONT_CHECK),
1023 RunfilesProvider.DATA_RUNFILES);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001024 return this;
1025 }
1026
1027 /**
1028 * Collects runfiles from "srcs" and "deps" of a target.
1029 */
1030 public Builder addNonDataDeps(RuleContext ruleContext,
1031 Function<TransitiveInfoCollection, Runfiles> mapping) {
1032 for (TransitiveInfoCollection target : getNonDataDeps(ruleContext)) {
1033 addTargetExceptFileTargets(target, mapping);
1034 }
1035 return this;
1036 }
1037
1038 public Builder addTargets(Iterable<? extends TransitiveInfoCollection> targets,
1039 Function<TransitiveInfoCollection, Runfiles> mapping) {
1040 for (TransitiveInfoCollection target : targets) {
1041 addTarget(target, mapping);
1042 }
1043 return this;
1044 }
1045
1046 public Builder addTarget(TransitiveInfoCollection target,
1047 Function<TransitiveInfoCollection, Runfiles> mapping) {
1048 return addTargetIncludingFileTargets(target, mapping);
1049 }
1050
1051 private Builder addTargetExceptFileTargets(TransitiveInfoCollection target,
1052 Function<TransitiveInfoCollection, Runfiles> mapping) {
1053 Runfiles runfiles = mapping.apply(target);
1054 if (runfiles != null) {
1055 merge(runfiles);
1056 }
1057
1058 return this;
1059 }
1060
1061 private Builder addTargetIncludingFileTargets(TransitiveInfoCollection target,
1062 Function<TransitiveInfoCollection, Runfiles> mapping) {
1063 if (target.getProvider(RunfilesProvider.class) == null
1064 && mapping == RunfilesProvider.DATA_RUNFILES) {
1065 // RuleConfiguredTarget implements RunfilesProvider, so this will only be called on
1066 // FileConfiguredTarget instances.
1067 // TODO(bazel-team): This is a terrible hack. We should be able to make this go away
1068 // by implementing RunfilesProvider on FileConfiguredTarget. We'd need to be mindful
1069 // of the memory use, though, since we have a whole lot of FileConfiguredTarget instances.
1070 addTransitiveArtifacts(target.getProvider(FileProvider.class).getFilesToBuild());
1071 return this;
1072 }
1073
1074 return addTargetExceptFileTargets(target, mapping);
1075 }
1076
ulfjack459ce872020-01-09 05:06:07 -08001077 /** Adds symlinks to given artifacts at their exec paths. */
1078 public Builder addSymlinksToArtifacts(NestedSet<Artifact> artifacts) {
1079 // These are symlinks using the exec path, not the root-relative path, which currently
1080 // requires flattening.
1081 return addSymlinksToArtifacts(artifacts.toList());
1082 }
1083
1084 /** Adds symlinks to given artifacts at their exec paths. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001085 public Builder addSymlinksToArtifacts(Iterable<Artifact> artifacts) {
1086 for (Artifact artifact : artifacts) {
1087 addSymlink(artifact.getExecPath(), artifact);
1088 }
1089 return this;
1090 }
1091
1092 /**
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +02001093 * Add extra middlemen artifacts that should be built by reverse dependency binaries. This
1094 * method exists solely to support the unfortunate legacy behavior of some rules; new uses
1095 * should not be added.
1096 */
1097 public Builder addLegacyExtraMiddleman(Artifact middleman) {
1098 Preconditions.checkArgument(middleman.isMiddlemanArtifact(), middleman);
1099 extraMiddlemenBuilder.add(middleman);
1100 return this;
1101 }
1102
ulfjack459ce872020-01-09 05:06:07 -08001103 /** Add the other {@link Runfiles} object transitively. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001104 public Builder merge(Runfiles runfiles) {
Googlerd7a780e2017-03-14 18:10:02 +00001105 return merge(runfiles, true, true);
1106 }
1107
1108 /**
ulfjack459ce872020-01-09 05:06:07 -08001109 * Add the other {@link Runfiles} object transitively, but don't merge unconditional artifacts.
Googlerd7a780e2017-03-14 18:10:02 +00001110 */
1111 public Builder mergeExceptUnconditionalArtifacts(Runfiles runfiles) {
1112 return merge(runfiles, false, true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001113 }
1114
ulfjack459ce872020-01-09 05:06:07 -08001115 /** Add the other {@link Runfiles} object transitively, but don't merge pruning manifests. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001116 public Builder mergeExceptPruningManifests(Runfiles runfiles) {
Googlerd7a780e2017-03-14 18:10:02 +00001117 return merge(runfiles, true, false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001118 }
1119
1120 /**
1121 * Add the other {@link Runfiles} object transitively, with the option to include or exclude
1122 * pruning manifests in the merge.
1123 */
Googlerd7a780e2017-03-14 18:10:02 +00001124 private Builder merge(Runfiles runfiles, boolean includeUnconditionalArtifacts,
1125 boolean includePruningManifests) {
Googlerc85af312016-03-10 17:55:17 +00001126 // Propagate the most strict conflict checking from merged-in runfiles
1127 if (runfiles.conflictPolicy.compareTo(conflictPolicy) > 0) {
1128 conflictPolicy = runfiles.conflictPolicy;
1129 }
Ulf Adams4226be22015-09-03 17:00:54 +00001130 if (runfiles.isEmpty()) {
1131 return this;
1132 }
1133 // The suffix should be the same within any blaze build, except for the EMPTY runfiles, which
1134 // may have an empty suffix, but that is covered above.
cushon44a4fd82019-02-06 09:34:50 -08001135 Preconditions.checkArgument(
1136 suffix.equals(runfiles.suffix), "%s != %s", suffix, runfiles.suffix);
Googlerd7a780e2017-03-14 18:10:02 +00001137 if (includeUnconditionalArtifacts) {
1138 artifactsBuilder.addTransitive(runfiles.getUnconditionalArtifacts());
1139 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001140 symlinksBuilder.addTransitive(runfiles.getSymlinks());
1141 rootSymlinksBuilder.addTransitive(runfiles.getRootSymlinks());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001142 if (includePruningManifests) {
1143 pruningManifestsBuilder.addTransitive(runfiles.getPruningManifests());
1144 }
Benjamin Petersondbbcbcb2017-10-26 11:38:13 +02001145 extraMiddlemenBuilder.addTransitive(runfiles.getExtraMiddlemen());
Michajlo Matijkiwac879b92015-04-15 21:28:33 +00001146 if (emptyFilesSupplier == DUMMY_EMPTY_FILES_SUPPLIER) {
1147 emptyFilesSupplier = runfiles.getEmptyFilesProvider();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001148 } else {
Michajlo Matijkiwac879b92015-04-15 21:28:33 +00001149 EmptyFilesSupplier otherSupplier = runfiles.getEmptyFilesProvider();
1150 Preconditions.checkState((otherSupplier == DUMMY_EMPTY_FILES_SUPPLIER)
1151 || emptyFilesSupplier.equals(otherSupplier));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001152 }
1153 return this;
1154 }
1155
1156 private static Iterable<TransitiveInfoCollection> getNonDataDeps(RuleContext ruleContext) {
1157 return Iterables.concat(
1158 // TODO(bazel-team): This line shouldn't be here. Removing it requires that no rules have
1159 // dependent rules in srcs (except for filegroups and such), but always in deps.
1160 // TODO(bazel-team): DONT_CHECK is not optimal here. Rules that use split configs need to
1161 // be changed not to call into here.
1162 getPrerequisites(ruleContext, "srcs", Mode.DONT_CHECK),
1163 getPrerequisites(ruleContext, "deps", Mode.DONT_CHECK));
1164 }
1165
1166 /**
1167 * For the specified attribute "attributeName" (which must be of type list(label)), resolves all
1168 * the labels into ConfiguredTargets (for the same configuration as this one) and returns them
1169 * as a list.
1170 *
1171 * <p>If the rule does not have the specified attribute, returns the empty list.
1172 */
1173 private static Iterable<? extends TransitiveInfoCollection> getPrerequisites(
1174 RuleContext ruleContext, String attributeName, Mode mode) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001175 if (ruleContext.getRule().isAttrDefined(attributeName, BuildType.LABEL_LIST)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001176 return ruleContext.getPrerequisites(attributeName, mode);
1177 } else {
1178 return Collections.emptyList();
1179 }
1180 }
1181 }
Jon Brandvein8d100342016-12-28 17:04:37 +00001182
cparsons90f70b62018-05-01 12:45:02 -07001183 @Override
1184 public Runfiles merge(RunfilesApi other) {
ulfjack8c007af2019-02-12 11:10:24 -08001185 Runfiles o = (Runfiles) other;
1186 if (isEmpty()) {
1187 // This is not just a memory / performance optimization. The Builder requires a valid suffix,
1188 // but the {@code Runfiles.EMPTY} singleton has an invalid one, which must not be used to
1189 // construct a Runfiles.Builder.
1190 return o;
1191 } else if (o.isEmpty()) {
1192 return this;
1193 }
1194 return new Runfiles.Builder(suffix, false).merge(this).merge(o).build();
Jon Brandvein8d100342016-12-28 17:04:37 +00001195 }
fellyc31c8c92018-08-29 15:07:59 -07001196
1197 /**
1198 * Fingerprint this {@link Runfiles} tree.
1199 */
1200 public void fingerprint(Fingerprint fp) {
1201 fp.addBoolean(getLegacyExternalRunfiles());
1202 fp.addPath(getSuffix());
mstaib0e81f9a2018-09-17 14:30:28 -07001203 Map<PathFragment, Artifact> symlinks = getSymlinksAsMap(null);
fellyc31c8c92018-08-29 15:07:59 -07001204 fp.addInt(symlinks.size());
1205 for (Map.Entry<PathFragment, Artifact> symlink : symlinks.entrySet()) {
1206 fp.addPath(symlink.getKey());
1207 fp.addPath(symlink.getValue().getExecPath());
1208 }
mstaib0e81f9a2018-09-17 14:30:28 -07001209 Map<PathFragment, Artifact> rootSymlinks = getRootSymlinksAsMap(null);
fellyc31c8c92018-08-29 15:07:59 -07001210 fp.addInt(rootSymlinks.size());
1211 for (Map.Entry<PathFragment, Artifact> rootSymlink : rootSymlinks.entrySet()) {
1212 fp.addPath(rootSymlink.getKey());
1213 fp.addPath(rootSymlink.getValue().getExecPath());
1214 }
1215
ulfjack7a052892019-12-17 01:21:54 -08001216 for (Artifact artifact : getArtifacts().toList()) {
fellyc31c8c92018-08-29 15:07:59 -07001217 fp.addPath(artifact.getRootRelativePath());
1218 fp.addPath(artifact.getExecPath());
1219 }
brandjon08561e32018-09-25 06:59:00 -07001220
ulfjack7a052892019-12-17 01:21:54 -08001221 for (String name : getEmptyFilenames().toList()) {
brandjon08561e32018-09-25 06:59:00 -07001222 fp.addString(name);
1223 }
fellyc31c8c92018-08-29 15:07:59 -07001224 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001225}