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