Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 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.packages; |
| 16 | |
| 17 | import com.google.common.annotations.VisibleForTesting; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableList; |
| 19 | import com.google.common.collect.ImmutableMap; |
| 20 | import com.google.common.collect.ImmutableSet; |
| 21 | import com.google.common.collect.ImmutableSortedSet; |
| 22 | import com.google.common.collect.Iterables; |
| 23 | import com.google.common.collect.Lists; |
| 24 | import com.google.common.collect.Maps; |
| 25 | import com.google.common.collect.Sets; |
Lukacs Berki | 6e91eb9 | 2015-09-21 09:12:37 +0000 | [diff] [blame] | 26 | import com.google.devtools.build.lib.cmdline.Label; |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 27 | import com.google.devtools.build.lib.cmdline.LabelSyntaxException; |
Kristina Chodorow | 73fa203 | 2015-08-28 17:57:46 +0000 | [diff] [blame] | 28 | import com.google.devtools.build.lib.cmdline.PackageIdentifier; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 29 | import com.google.devtools.build.lib.collect.CollectionUtils; |
| 30 | import com.google.devtools.build.lib.collect.ImmutableSortedKeyMap; |
| 31 | import com.google.devtools.build.lib.events.Event; |
| 32 | import com.google.devtools.build.lib.events.EventHandler; |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 33 | import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 34 | import com.google.devtools.build.lib.events.Location; |
| 35 | import com.google.devtools.build.lib.packages.AttributeMap.AcceptsLabelAttribute; |
| 36 | import com.google.devtools.build.lib.packages.License.DistributionType; |
Mark Schaller | 6df8179 | 2015-12-10 18:47:47 +0000 | [diff] [blame] | 37 | import com.google.devtools.build.lib.util.Preconditions; |
Laurent Le Brun | d8b80c3 | 2016-06-03 14:38:19 +0000 | [diff] [blame] | 38 | import com.google.devtools.build.lib.util.SpellChecker; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 39 | import com.google.devtools.build.lib.vfs.Canonicalizer; |
| 40 | import com.google.devtools.build.lib.vfs.Path; |
| 41 | import com.google.devtools.build.lib.vfs.PathFragment; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 42 | import java.io.PrintStream; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 43 | import java.util.ArrayList; |
| 44 | import java.util.Collection; |
| 45 | import java.util.Collections; |
| 46 | import java.util.HashMap; |
| 47 | import java.util.List; |
| 48 | import java.util.Map; |
| 49 | import java.util.Set; |
Han-Wen Nienhuys | 67e6f98 | 2016-01-15 15:54:12 +0000 | [diff] [blame] | 50 | import javax.annotation.Nullable; |
| 51 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 52 | /** |
| 53 | * A package, which is a container of {@link Rule}s, each of |
| 54 | * which contains a dictionary of named attributes. |
| 55 | * |
| 56 | * <p>Package instances are intended to be immutable and for all practical |
| 57 | * purposes can be treated as such. Note, however, that some member variables |
| 58 | * exposed via the public interface are not strictly immutable, so until their |
| 59 | * types are guaranteed immutable we're not applying the {@code @Immutable} |
| 60 | * annotation here. |
| 61 | */ |
Michajlo Matijkiw | 17f11eb | 2015-10-14 04:19:28 +0000 | [diff] [blame] | 62 | public class Package { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 63 | |
| 64 | /** |
| 65 | * Common superclass for all name-conflict exceptions. |
| 66 | */ |
| 67 | public static class NameConflictException extends Exception { |
| 68 | protected NameConflictException(String message) { |
| 69 | super(message); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * The repository identifier for this package. |
| 75 | */ |
| 76 | private final PackageIdentifier packageIdentifier; |
| 77 | |
| 78 | /** |
| 79 | * The name of the package, e.g. "foo/bar". |
| 80 | */ |
| 81 | protected final String name; |
| 82 | |
| 83 | /** |
| 84 | * Like name, but in the form of a PathFragment. |
| 85 | */ |
| 86 | private final PathFragment nameFragment; |
| 87 | |
| 88 | /** |
| 89 | * The filename of this package's BUILD file. |
| 90 | */ |
| 91 | protected Path filename; |
| 92 | |
| 93 | /** |
| 94 | * The directory in which this package's BUILD file resides. All InputFile |
| 95 | * members of the packages are located relative to this directory. |
| 96 | */ |
| 97 | private Path packageDirectory; |
| 98 | |
| 99 | /** |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 100 | * The name of the workspace this package is in. Used as a prefix for the runfiles directory. |
| 101 | * This can be set in the WORKSPACE file. This must be a valid target name. |
| 102 | */ |
Ulf Adams | d13207c | 2015-09-04 14:53:43 +0000 | [diff] [blame] | 103 | protected String workspaceName; |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 104 | |
| 105 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 106 | * The root of the source tree in which this package was found. It is an invariant that |
Kristina Chodorow | a1a31ff | 2016-07-27 16:34:27 +0000 | [diff] [blame] | 107 | * {@code sourceRoot.getRelative(packageId.getSourceRoot()).equals(packageDirectory)}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 108 | */ |
| 109 | private Path sourceRoot; |
| 110 | |
| 111 | /** |
| 112 | * The "Make" environment of this package, containing package-local |
| 113 | * definitions of "Make" variables. |
| 114 | */ |
| 115 | private MakeEnvironment makeEnv; |
| 116 | |
carmi | 31c6a61 | 2017-05-17 22:56:19 +0200 | [diff] [blame] | 117 | /** The collection of all targets defined in this package, indexed by name. */ |
| 118 | protected ImmutableSortedKeyMap<String, Target> targets; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 119 | |
| 120 | /** |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 121 | * Default visibility for rules that do not specify it. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 122 | */ |
| 123 | private RuleVisibility defaultVisibility; |
| 124 | private boolean defaultVisibilitySet; |
| 125 | |
| 126 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 127 | * Default package-level 'testonly' value for rules that do not specify it. |
| 128 | */ |
| 129 | private boolean defaultTestOnly = false; |
| 130 | |
| 131 | /** |
| 132 | * Default package-level 'deprecation' value for rules that do not specify it. |
| 133 | */ |
| 134 | private String defaultDeprecation; |
| 135 | |
| 136 | /** |
| 137 | * Default header strictness checking for rules that do not specify it. |
| 138 | */ |
| 139 | private String defaultHdrsCheck; |
| 140 | |
Carmi Grushko | 81dca61 | 2016-07-27 02:12:15 +0000 | [diff] [blame] | 141 | /** Default copts for cc_* rules. The rules' individual copts will append to this value. */ |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 142 | private ImmutableList<String> defaultCopts; |
| 143 | |
| 144 | /** |
| 145 | * The InputFile target corresponding to this package's BUILD file. |
| 146 | */ |
| 147 | private InputFile buildFile; |
| 148 | |
| 149 | /** |
| 150 | * True iff this package's BUILD files contained lexical or grammatical |
| 151 | * errors, or experienced errors during evaluation, or semantic errors during |
| 152 | * the construction of any rule. |
| 153 | * |
| 154 | * <p>Note: A package containing errors does not necessarily prevent a build; |
| 155 | * if all the rules needed for a given build were constructed prior to the |
| 156 | * first error, the build may proceed. |
| 157 | */ |
| 158 | private boolean containsErrors; |
| 159 | |
| 160 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 161 | * The set of labels subincluded by this package. |
| 162 | */ |
| 163 | private Set<Label> subincludes; |
| 164 | |
| 165 | /** |
| 166 | * The list of transitive closure of the Skylark file dependencies. |
| 167 | */ |
| 168 | private ImmutableList<Label> skylarkFileDependencies; |
| 169 | |
| 170 | /** |
| 171 | * The package's default "licenses" and "distribs" attributes, as specified |
| 172 | * in calls to licenses() and distribs() in the BUILD file. |
| 173 | */ |
| 174 | // These sets contain the values specified by the most recent licenses() or |
| 175 | // distribs() declarations encountered during package parsing: |
| 176 | private License defaultLicense; |
| 177 | private Set<License.DistributionType> defaultDistributionSet; |
| 178 | |
| 179 | |
| 180 | /** |
| 181 | * The names of the package() attributes that declare default values for rule |
| 182 | * {@link RuleClass#COMPATIBLE_ENVIRONMENT_ATTR} and {@link RuleClass#RESTRICTED_ENVIRONMENT_ATTR} |
| 183 | * values when not explicitly specified. |
| 184 | */ |
| 185 | public static final String DEFAULT_COMPATIBLE_WITH_ATTRIBUTE = "default_compatible_with"; |
| 186 | public static final String DEFAULT_RESTRICTED_TO_ATTRIBUTE = "default_restricted_to"; |
| 187 | |
| 188 | private Set<Label> defaultCompatibleWith = ImmutableSet.of(); |
| 189 | private Set<Label> defaultRestrictedTo = ImmutableSet.of(); |
| 190 | |
| 191 | private ImmutableSet<String> features; |
| 192 | |
| 193 | private ImmutableList<Event> events; |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 194 | private ImmutableList<Postable> posts; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 195 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 196 | /** |
| 197 | * Package initialization, part 1 of 3: instantiates a new package with the |
| 198 | * given name. |
| 199 | * |
| 200 | * <p>As part of initialization, {@link Builder} constructs {@link InputFile} |
| 201 | * and {@link PackageGroup} instances that require a valid Package instance where |
| 202 | * {@link Package#getNameFragment()} is accessible. That's why these settings are |
| 203 | * applied here at the start. |
| 204 | * |
| 205 | * @precondition {@code name} must be a suffix of |
| 206 | * {@code filename.getParentDirectory())}. |
| 207 | */ |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 208 | protected Package(PackageIdentifier packageId, String runfilesPrefix) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 209 | this.packageIdentifier = packageId; |
Ulf Adams | d13207c | 2015-09-04 14:53:43 +0000 | [diff] [blame] | 210 | this.workspaceName = runfilesPrefix; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 211 | this.nameFragment = Canonicalizer.fragments().intern(packageId.getPackageFragment()); |
| 212 | this.name = nameFragment.getPathString(); |
| 213 | } |
| 214 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 215 | /** Returns this packages' identifier. */ |
| 216 | public PackageIdentifier getPackageIdentifier() { |
| 217 | return packageIdentifier; |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Package initialization: part 2 of 3: sets this package's default header |
| 222 | * strictness checking. |
| 223 | * |
| 224 | * <p>This is needed to support C++-related rule classes |
| 225 | * which accesses {@link #getDefaultHdrsCheck} from the still-under-construction |
| 226 | * package. |
| 227 | */ |
| 228 | protected void setDefaultHdrsCheck(String defaultHdrsCheck) { |
| 229 | this.defaultHdrsCheck = defaultHdrsCheck; |
| 230 | } |
| 231 | |
| 232 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 233 | * Set the default 'testonly' value for this package. |
| 234 | */ |
| 235 | protected void setDefaultTestOnly(boolean testOnly) { |
| 236 | defaultTestOnly = testOnly; |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Set the default 'deprecation' value for this package. |
| 241 | */ |
| 242 | protected void setDefaultDeprecation(String deprecation) { |
| 243 | defaultDeprecation = deprecation; |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Sets the default value to use for a rule's {@link RuleClass#COMPATIBLE_ENVIRONMENT_ATTR} |
| 248 | * attribute when not explicitly specified by the rule. |
| 249 | */ |
| 250 | protected void setDefaultCompatibleWith(Set<Label> environments) { |
| 251 | defaultCompatibleWith = environments; |
| 252 | } |
| 253 | |
| 254 | /** |
| 255 | * Sets the default value to use for a rule's {@link RuleClass#RESTRICTED_ENVIRONMENT_ATTR} |
| 256 | * attribute when not explicitly specified by the rule. |
| 257 | */ |
| 258 | protected void setDefaultRestrictedTo(Set<Label> environments) { |
| 259 | defaultRestrictedTo = environments; |
| 260 | } |
| 261 | |
Ulf Adams | a28b540 | 2017-02-24 09:28:44 +0000 | [diff] [blame] | 262 | // This must always be consistent with Root.computeSourceRoot; otherwise computing source roots |
| 263 | // from exec paths does not work, which can break the action cache for input-discovering actions. |
Kristina Chodorow | dc30c07 | 2015-04-22 13:48:47 +0000 | [diff] [blame] | 264 | private static Path getSourceRoot(Path buildFile, PathFragment packageFragment) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 265 | Path current = buildFile.getParentDirectory(); |
Kristina Chodorow | b6fbab7 | 2016-01-28 14:38:31 +0000 | [diff] [blame] | 266 | for (int i = 0, len = packageFragment.segmentCount(); |
| 267 | i < len && !packageFragment.equals(PathFragment.EMPTY_FRAGMENT); i++) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 268 | current = current.getParentDirectory(); |
| 269 | } |
| 270 | return current; |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Package initialization: part 3 of 3: applies all other settings and completes |
| 275 | * initialization of the package. |
| 276 | * |
| 277 | * <p>Only after this method is called can this package be considered "complete" |
| 278 | * and be shared publicly. |
| 279 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 280 | protected void finishInit(Builder builder) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 281 | // If any error occurred during evaluation of this package, consider all |
| 282 | // rules in the package to be "in error" also (even if they were evaluated |
| 283 | // prior to the error). This behaviour is arguably stricter than need be, |
| 284 | // but stopping a build only for some errors but not others creates user |
| 285 | // confusion. |
| 286 | if (builder.containsErrors) { |
| 287 | for (Rule rule : builder.getTargets(Rule.class)) { |
| 288 | rule.setContainsErrors(); |
| 289 | } |
| 290 | } |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 291 | this.filename = builder.getFilename(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 292 | this.packageDirectory = filename.getParentDirectory(); |
| 293 | |
Dmitry Lomov | e36a66c | 2017-02-17 14:48:48 +0000 | [diff] [blame] | 294 | this.sourceRoot = getSourceRoot(filename, packageIdentifier.getSourceRoot()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 295 | if ((sourceRoot == null |
Dmitry Lomov | e36a66c | 2017-02-17 14:48:48 +0000 | [diff] [blame] | 296 | || !sourceRoot.getRelative(packageIdentifier.getSourceRoot()).equals(packageDirectory)) |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 297 | && !filename.getBaseName().equals("WORKSPACE")) { |
| 298 | throw new IllegalArgumentException( |
Kristina Chodorow | dc30c07 | 2015-04-22 13:48:47 +0000 | [diff] [blame] | 299 | "Invalid BUILD file name for package '" + packageIdentifier + "': " + filename); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 300 | } |
| 301 | |
| 302 | this.makeEnv = builder.makeEnv.build(); |
| 303 | this.targets = ImmutableSortedKeyMap.copyOf(builder.targets); |
| 304 | this.defaultVisibility = builder.defaultVisibility; |
| 305 | this.defaultVisibilitySet = builder.defaultVisibilitySet; |
| 306 | if (builder.defaultCopts == null) { |
| 307 | this.defaultCopts = ImmutableList.of(); |
| 308 | } else { |
| 309 | this.defaultCopts = ImmutableList.copyOf(builder.defaultCopts); |
| 310 | } |
| 311 | this.buildFile = builder.buildFile; |
| 312 | this.containsErrors = builder.containsErrors; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 313 | this.subincludes = builder.subincludes.keySet(); |
| 314 | this.skylarkFileDependencies = builder.skylarkFileDependencies; |
| 315 | this.defaultLicense = builder.defaultLicense; |
| 316 | this.defaultDistributionSet = builder.defaultDistributionSet; |
| 317 | this.features = ImmutableSortedSet.copyOf(builder.features); |
| 318 | this.events = ImmutableList.copyOf(builder.events); |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 319 | this.posts = ImmutableList.copyOf(builder.posts); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 320 | } |
| 321 | |
| 322 | /** |
| 323 | * Returns the list of subincluded labels on which the validity of this package depends. |
| 324 | */ |
| 325 | public Set<Label> getSubincludeLabels() { |
| 326 | return subincludes; |
| 327 | } |
| 328 | |
| 329 | /** |
| 330 | * Returns the list of transitive closure of the Skylark file dependencies of this package. |
| 331 | */ |
| 332 | public ImmutableList<Label> getSkylarkFileDependencies() { |
| 333 | return skylarkFileDependencies; |
| 334 | } |
| 335 | |
| 336 | /** |
| 337 | * Returns the filename of the BUILD file which defines this package. The |
| 338 | * parent directory of the BUILD file is the package directory. |
| 339 | */ |
| 340 | public Path getFilename() { |
| 341 | return filename; |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Returns the source root (a directory) beneath which this package's BUILD file was found. |
| 346 | * |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 347 | * <p> Assumes invariant: |
Kristina Chodorow | a1a31ff | 2016-07-27 16:34:27 +0000 | [diff] [blame] | 348 | * {@code getSourceRoot().getRelative(packageId.getSourceRoot()).equals(getPackageDirectory())} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 349 | */ |
| 350 | public Path getSourceRoot() { |
| 351 | return sourceRoot; |
| 352 | } |
| 353 | |
| 354 | /** |
| 355 | * Returns the directory containing the package's BUILD file. |
| 356 | */ |
| 357 | public Path getPackageDirectory() { |
| 358 | return packageDirectory; |
| 359 | } |
| 360 | |
| 361 | /** |
| 362 | * Returns the name of this package. If this build is using external repositories then this name |
| 363 | * may not be unique! |
| 364 | */ |
| 365 | public String getName() { |
| 366 | return name; |
| 367 | } |
| 368 | |
| 369 | /** |
| 370 | * Like {@link #getName}, but has type {@code PathFragment}. |
| 371 | */ |
| 372 | public PathFragment getNameFragment() { |
| 373 | return nameFragment; |
| 374 | } |
| 375 | |
| 376 | /** |
| 377 | * Returns the "Make" value from the package's make environment whose name |
| 378 | * is "varname", or null iff the variable is not defined in the environment. |
| 379 | */ |
| 380 | public String lookupMakeVariable(String varname, String platform) { |
| 381 | return makeEnv.lookup(varname, platform); |
| 382 | } |
| 383 | |
| 384 | /** |
| 385 | * Returns the make environment. This should only ever be used for serialization -- how the |
| 386 | * make variables are implemented is an implementation detail. |
| 387 | */ |
| 388 | MakeEnvironment getMakeEnvironment() { |
| 389 | return makeEnv; |
| 390 | } |
| 391 | |
| 392 | /** |
Laurent Le Brun | e8ce847 | 2015-03-12 14:20:01 +0000 | [diff] [blame] | 393 | * Returns all make variables for a given platform. |
| 394 | */ |
hlopko | a477805 | 2017-05-26 11:37:28 +0200 | [diff] [blame] | 395 | public ImmutableMap<String, String> getAllMakeVariables(String platform) { |
Laurent Le Brun | e8ce847 | 2015-03-12 14:20:01 +0000 | [diff] [blame] | 396 | ImmutableMap.Builder<String, String> map = ImmutableMap.builder(); |
| 397 | for (String var : makeEnv.getBindings().keySet()) { |
Dmitry Lomov | 7d9c650 | 2016-06-30 19:53:51 +0000 | [diff] [blame] | 398 | String value = makeEnv.lookup(var, platform); |
| 399 | if (value != null) { |
| 400 | map.put(var, value); |
| 401 | } |
Laurent Le Brun | e8ce847 | 2015-03-12 14:20:01 +0000 | [diff] [blame] | 402 | } |
| 403 | return map.build(); |
| 404 | } |
| 405 | |
| 406 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 407 | * Returns the label of this package's BUILD file. |
| 408 | * |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 409 | * <p> Typically <code>getBuildFileLabel().getName().equals("BUILD")</code> -- |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 410 | * though not necessarily: data in a subdirectory of a test package may use a |
| 411 | * different filename to avoid inadvertently creating a new package. |
| 412 | */ |
Mark Schaller | 4fa83ac | 2015-07-10 16:59:37 +0000 | [diff] [blame] | 413 | public Label getBuildFileLabel() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 414 | return buildFile.getLabel(); |
| 415 | } |
| 416 | |
| 417 | /** |
| 418 | * Returns the InputFile target for this package's BUILD file. |
| 419 | */ |
| 420 | public InputFile getBuildFile() { |
| 421 | return buildFile; |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * Returns true if errors were encountered during evaluation of this package. |
| 426 | * (The package may be incomplete and its contents should not be relied upon |
| 427 | * for critical operations. However, any Rules belonging to the package are |
| 428 | * guaranteed to be intact, unless their <code>containsErrors()</code> flag |
| 429 | * is set.) |
| 430 | */ |
| 431 | public boolean containsErrors() { |
| 432 | return containsErrors; |
| 433 | } |
| 434 | |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 435 | public List<Postable> getPosts() { |
| 436 | return posts; |
| 437 | } |
| 438 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 439 | public List<Event> getEvents() { |
| 440 | return events; |
| 441 | } |
| 442 | |
carmi | 31c6a61 | 2017-05-17 22:56:19 +0200 | [diff] [blame] | 443 | /** Returns an (immutable, unordered) view of all the targets belonging to this package. */ |
| 444 | public ImmutableSortedKeyMap<String, Target> getTargets() { |
| 445 | return targets; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 446 | } |
| 447 | |
| 448 | /** |
carmi | 31c6a61 | 2017-05-17 22:56:19 +0200 | [diff] [blame] | 449 | * Common getTargets implementation, accessible by {@link Package.Builder}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 450 | */ |
| 451 | private static Collection<Target> getTargets(Map<String, Target> targetMap) { |
| 452 | return Collections.unmodifiableCollection(targetMap.values()); |
| 453 | } |
| 454 | |
| 455 | /** |
| 456 | * Returns a (read-only, unordered) iterator of all the targets belonging |
| 457 | * to this package which are instances of the specified class. |
| 458 | */ |
| 459 | public <T extends Target> Iterable<T> getTargets(Class<T> targetClass) { |
| 460 | return getTargets(targets, targetClass); |
| 461 | } |
| 462 | |
| 463 | /** |
| 464 | * Common getTargets implementation, accessible by both {@link Package} and |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 465 | * {@link Package.Builder}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 466 | */ |
| 467 | private static <T extends Target> Iterable<T> getTargets(Map<String, Target> targetMap, |
| 468 | Class<T> targetClass) { |
| 469 | return Iterables.filter(targetMap.values(), targetClass); |
| 470 | } |
| 471 | |
| 472 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 473 | * Returns the rule that corresponds to a particular BUILD target name. Useful |
| 474 | * for walking through the dependency graph of a target. |
| 475 | * Fails if the target is not a Rule. |
| 476 | */ |
Ulf Adams | b313a97 | 2015-05-04 12:05:57 +0000 | [diff] [blame] | 477 | @VisibleForTesting // Should be package-private |
| 478 | public Rule getRule(String targetName) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 479 | return (Rule) targets.get(targetName); |
| 480 | } |
| 481 | |
John Cater | b4f461e | 2016-10-25 16:16:35 +0000 | [diff] [blame] | 482 | /** Returns all rules in the package that match the given rule class. */ |
| 483 | public Iterable<Rule> getRulesMatchingRuleClass(final String ruleClass) { |
| 484 | Iterable<Rule> targets = getTargets(Rule.class); |
laurentlb | 3d2a68c | 2017-06-30 00:32:04 +0200 | [diff] [blame] | 485 | return Iterables.filter(targets, rule -> rule.getRuleClass().equals(ruleClass)); |
John Cater | b4f461e | 2016-10-25 16:16:35 +0000 | [diff] [blame] | 486 | } |
| 487 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 488 | /** |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 489 | * Returns this package's workspace name. |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 490 | */ |
Kristina Chodorow | 734e7f7 | 2016-01-29 15:04:31 +0000 | [diff] [blame] | 491 | public String getWorkspaceName() { |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 492 | return workspaceName; |
| 493 | } |
| 494 | |
| 495 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 496 | * Returns the features specified in the <code>package()</code> declaration. |
| 497 | */ |
| 498 | public ImmutableSet<String> getFeatures() { |
| 499 | return features; |
| 500 | } |
| 501 | |
| 502 | /** |
| 503 | * Returns the target (a member of this package) whose name is "targetName". |
| 504 | * First rules are searched, then output files, then input files. The target |
| 505 | * name must be valid, as defined by {@code LabelValidator#validateTargetName}. |
| 506 | * |
| 507 | * @throws NoSuchTargetException if the specified target was not found. |
| 508 | */ |
| 509 | public Target getTarget(String targetName) throws NoSuchTargetException { |
| 510 | Target target = targets.get(targetName); |
| 511 | if (target != null) { |
| 512 | return target; |
| 513 | } |
| 514 | |
| 515 | // No such target. |
| 516 | |
| 517 | // If there's a file on the disk that's not mentioned in the BUILD file, |
| 518 | // produce a more informative error. NOTE! this code path is only executed |
| 519 | // on failure, which is (relatively) very rare. In the common case no |
| 520 | // stat(2) is executed. |
| 521 | Path filename = getPackageDirectory().getRelative(targetName); |
| 522 | String suffix; |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 523 | if (!PathFragment.create(targetName).isNormalized()) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 524 | // Don't check for file existence in this case because the error message |
| 525 | // would be confusing and wrong. If the targetName is "foo/bar/.", and |
| 526 | // there is a directory "foo/bar", it doesn't mean that "//pkg:foo/bar/." |
| 527 | // is a valid label. |
| 528 | suffix = ""; |
| 529 | } else if (filename.isDirectory()) { |
| 530 | suffix = "; however, a source directory of this name exists. (Perhaps add " |
| 531 | + "'exports_files([\"" + targetName + "\"])' to " + name + "/BUILD, or define a " |
| 532 | + "filegroup?)"; |
| 533 | } else if (filename.exists()) { |
| 534 | suffix = "; however, a source file of this name exists. (Perhaps add " |
| 535 | + "'exports_files([\"" + targetName + "\"])' to " + name + "/BUILD?)"; |
| 536 | } else { |
Laurent Le Brun | 57badf4 | 2017-01-02 15:12:24 +0000 | [diff] [blame] | 537 | suffix = SpellChecker.didYouMean(targetName, targets.keySet()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 538 | } |
| 539 | |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 540 | throw makeNoSuchTargetException(targetName, suffix); |
| 541 | } |
| 542 | |
| 543 | protected NoSuchTargetException makeNoSuchTargetException(String targetName, String suffix) { |
| 544 | Label label; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 545 | try { |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 546 | label = createLabel(targetName); |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 547 | } catch (LabelSyntaxException e) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 548 | throw new IllegalArgumentException(targetName); |
| 549 | } |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 550 | String msg = String.format( |
| 551 | "target '%s' not declared in package '%s'%s defined by %s", |
| 552 | targetName, |
| 553 | name, |
| 554 | suffix, |
| 555 | filename); |
| 556 | return new NoSuchTargetException(label, msg); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 557 | } |
| 558 | |
| 559 | /** |
| 560 | * Creates a label for a target inside this package. |
| 561 | * |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 562 | * @throws LabelSyntaxException if the {@code targetName} is invalid |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 563 | */ |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 564 | public Label createLabel(String targetName) throws LabelSyntaxException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 565 | return Label.create(packageIdentifier, targetName); |
| 566 | } |
| 567 | |
| 568 | /** |
| 569 | * Returns the default visibility for this package. |
| 570 | */ |
| 571 | public RuleVisibility getDefaultVisibility() { |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 572 | return defaultVisibility; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 573 | } |
| 574 | |
| 575 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 576 | * Returns the default testonly value. |
| 577 | */ |
| 578 | public Boolean getDefaultTestOnly() { |
| 579 | return defaultTestOnly; |
| 580 | } |
| 581 | |
| 582 | /** |
Greg Estren | c48214d | 2015-03-17 22:43:19 +0000 | [diff] [blame] | 583 | * Returns the default deprecation value. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 584 | */ |
| 585 | public String getDefaultDeprecation() { |
| 586 | return defaultDeprecation; |
| 587 | } |
| 588 | |
Carmi Grushko | 81dca61 | 2016-07-27 02:12:15 +0000 | [diff] [blame] | 589 | /** Gets the default header checking mode. */ |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 590 | public String getDefaultHdrsCheck() { |
Googler | 198a54f | 2016-01-18 11:02:10 +0000 | [diff] [blame] | 591 | return defaultHdrsCheck != null ? defaultHdrsCheck : "strict"; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 592 | } |
| 593 | |
| 594 | /** |
| 595 | * Returns the default copts value, to which rules should append their |
| 596 | * specific copts. |
| 597 | */ |
| 598 | public ImmutableList<String> getDefaultCopts() { |
| 599 | return defaultCopts; |
| 600 | } |
| 601 | |
| 602 | /** |
| 603 | * Returns whether the default header checking mode has been set or it is the |
| 604 | * default value. |
| 605 | */ |
| 606 | public boolean isDefaultHdrsCheckSet() { |
| 607 | return defaultHdrsCheck != null; |
| 608 | } |
| 609 | |
| 610 | public boolean isDefaultVisibilitySet() { |
| 611 | return defaultVisibilitySet; |
| 612 | } |
| 613 | |
| 614 | /** |
| 615 | * Gets the parsed license object for the default license |
| 616 | * declared by this package. |
| 617 | */ |
| 618 | public License getDefaultLicense() { |
| 619 | return defaultLicense; |
| 620 | } |
| 621 | |
| 622 | /** |
| 623 | * Returns the parsed set of distributions declared as the default for this |
| 624 | * package. |
| 625 | */ |
| 626 | public Set<License.DistributionType> getDefaultDistribs() { |
| 627 | return defaultDistributionSet; |
| 628 | } |
| 629 | |
| 630 | /** |
| 631 | * Returns the default value to use for a rule's {@link RuleClass#COMPATIBLE_ENVIRONMENT_ATTR} |
| 632 | * attribute when not explicitly specified by the rule. |
| 633 | */ |
| 634 | public Set<Label> getDefaultCompatibleWith() { |
| 635 | return defaultCompatibleWith; |
| 636 | } |
| 637 | |
| 638 | /** |
| 639 | * Returns the default value to use for a rule's {@link RuleClass#RESTRICTED_ENVIRONMENT_ATTR} |
| 640 | * attribute when not explicitly specified by the rule. |
| 641 | */ |
| 642 | public Set<Label> getDefaultRestrictedTo() { |
| 643 | return defaultRestrictedTo; |
| 644 | } |
| 645 | |
| 646 | @Override |
| 647 | public String toString() { |
Han-Wen Nienhuys | 58070da | 2015-04-20 11:37:58 +0000 | [diff] [blame] | 648 | return "Package(" + name + ")=" |
| 649 | + (targets != null ? getTargets(Rule.class) : "initializing..."); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 650 | } |
| 651 | |
| 652 | /** |
| 653 | * Dumps the package for debugging. Do not depend on the exact format/contents of this debugging |
| 654 | * output. |
| 655 | */ |
| 656 | public void dump(PrintStream out) { |
| 657 | out.println(" Package " + getName() + " (" + getFilename() + ")"); |
| 658 | |
| 659 | // Rules: |
| 660 | out.println(" Rules"); |
| 661 | for (Rule rule : getTargets(Rule.class)) { |
| 662 | out.println(" " + rule.getTargetKind() + " " + rule.getLabel()); |
| 663 | for (Attribute attr : rule.getAttributes()) { |
| 664 | for (Object possibleValue : AggregatingAttributeMapper.of(rule) |
| 665 | .visitAttribute(attr.getName(), attr.getType())) { |
| 666 | out.println(" " + attr.getName() + " = " + possibleValue); |
| 667 | } |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | // Files: |
| 672 | out.println(" Files"); |
| 673 | for (FileTarget file : getTargets(FileTarget.class)) { |
| 674 | out.print(" " + file.getTargetKind() + " " + file.getLabel()); |
| 675 | if (file instanceof OutputFile) { |
| 676 | out.println(" (generated by " + ((OutputFile) file).getGeneratingRule().getLabel() + ")"); |
| 677 | } else { |
| 678 | out.println(); |
| 679 | } |
| 680 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 681 | } |
| 682 | |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 683 | public static Builder newExternalPackageBuilder(Builder.Helper helper, Path workspacePath, |
| 684 | String runfilesPrefix) { |
| 685 | Builder b = new Builder(helper.createFreshPackage( |
| 686 | Label.EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix)); |
Han-Wen Nienhuys | f172d39 | 2015-09-18 17:37:51 +0000 | [diff] [blame] | 687 | b.setFilename(workspacePath); |
| 688 | b.setMakeEnv(new MakeEnvironment.Builder()); |
| 689 | return b; |
| 690 | } |
| 691 | |
Nathan Harmata | caa000a | 2016-06-07 17:46:19 +0000 | [diff] [blame] | 692 | /** |
| 693 | * A builder for {@link Package} objects. Only intended to be used by {@link PackageFactory} and |
| 694 | * {@link com.google.devtools.build.lib.skyframe.PackageFunction}. |
| 695 | */ |
Han-Wen Nienhuys | f172d39 | 2015-09-18 17:37:51 +0000 | [diff] [blame] | 696 | public static class Builder { |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 697 | public static interface Helper { |
| 698 | /** |
| 699 | * Returns a fresh {@link Package} instance that a {@link Builder} will internally mutate |
Nathan Harmata | caa000a | 2016-06-07 17:46:19 +0000 | [diff] [blame] | 700 | * during package loading. Called by {@link PackageFactory}. |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 701 | */ |
| 702 | Package createFreshPackage(PackageIdentifier packageId, String runfilesPrefix); |
Nathan Harmata | caa000a | 2016-06-07 17:46:19 +0000 | [diff] [blame] | 703 | |
| 704 | /** |
| 705 | * Called after {@link com.google.devtools.build.lib.skyframe.PackageFunction} is completely |
| 706 | * done loading the given {@link Package}. |
| 707 | */ |
| 708 | void onLoadingComplete(Package pkg); |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 709 | } |
| 710 | |
| 711 | /** {@link Helper} that simply calls the {@link Package} constructor. */ |
| 712 | public static class DefaultHelper implements Helper { |
| 713 | public static final DefaultHelper INSTANCE = new DefaultHelper(); |
| 714 | |
| 715 | private DefaultHelper() { |
| 716 | } |
| 717 | |
| 718 | @Override |
| 719 | public Package createFreshPackage(PackageIdentifier packageId, String runfilesPrefix) { |
| 720 | return new Package(packageId, runfilesPrefix); |
| 721 | } |
Nathan Harmata | caa000a | 2016-06-07 17:46:19 +0000 | [diff] [blame] | 722 | |
| 723 | @Override |
| 724 | public void onLoadingComplete(Package pkg) { |
| 725 | } |
Ulf Adams | 4226be2 | 2015-09-03 17:00:54 +0000 | [diff] [blame] | 726 | } |
| 727 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 728 | /** |
| 729 | * The output instance for this builder. Needs to be instantiated and |
| 730 | * available with name info throughout initialization. All other settings |
Michajlo Matijkiw | 17f11eb | 2015-10-14 04:19:28 +0000 | [diff] [blame] | 731 | * are applied during {@link #build}. See {@link Package#Package} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 732 | * and {@link Package#finishInit} for details. |
| 733 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 734 | protected Package pkg; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 735 | |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 736 | private Path filename = null; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 737 | private Label buildFileLabel = null; |
| 738 | private InputFile buildFile = null; |
| 739 | private MakeEnvironment.Builder makeEnv = null; |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 740 | private RuleVisibility defaultVisibility = ConstantRuleVisibility.PRIVATE; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 741 | private boolean defaultVisibilitySet; |
| 742 | private List<String> defaultCopts = null; |
| 743 | private List<String> features = new ArrayList<>(); |
| 744 | private List<Event> events = Lists.newArrayList(); |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 745 | private List<Postable> posts = Lists.newArrayList(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 746 | private boolean containsErrors = false; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 747 | |
| 748 | private License defaultLicense = License.NO_LICENSE; |
| 749 | private Set<License.DistributionType> defaultDistributionSet = License.DEFAULT_DISTRIB; |
| 750 | |
| 751 | protected Map<String, Target> targets = new HashMap<>(); |
| 752 | protected Map<Label, EnvironmentGroup> environmentGroups = new HashMap<>(); |
| 753 | |
| 754 | protected Map<Label, Path> subincludes = null; |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 755 | protected ImmutableList<Label> skylarkFileDependencies = ImmutableList.of(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 756 | |
Han-Wen Nienhuys | f172d39 | 2015-09-18 17:37:51 +0000 | [diff] [blame] | 757 | protected ExternalPackageBuilder externalPackageData = new ExternalPackageBuilder(); |
| 758 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 759 | /** |
| 760 | * True iff the "package" function has already been called in this package. |
| 761 | */ |
| 762 | private boolean packageFunctionUsed; |
| 763 | |
| 764 | /** |
| 765 | * The collection of the prefixes of every output file. Maps every prefix |
| 766 | * to an output file whose prefix it is. |
| 767 | * |
| 768 | * <p>This is needed to make the output file prefix conflict check be |
| 769 | * reasonably fast. However, since it can potentially take a lot of memory and |
| 770 | * is useless after the package has been loaded, it isn't passed to the |
| 771 | * package itself. |
| 772 | */ |
| 773 | private Map<String, OutputFile> outputFilePrefixes = new HashMap<>(); |
| 774 | |
| 775 | private boolean alreadyBuilt = false; |
| 776 | |
| 777 | private EventHandler builderEventHandler = new EventHandler() { |
| 778 | @Override |
| 779 | public void handle(Event event) { |
| 780 | addEvent(event); |
| 781 | } |
| 782 | }; |
| 783 | |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 784 | protected Builder(Package pkg) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 785 | this.pkg = pkg; |
| 786 | if (pkg.getName().startsWith("javatests/")) { |
| 787 | setDefaultTestonly(true); |
| 788 | } |
| 789 | } |
| 790 | |
Nathan Harmata | 42fb560 | 2016-05-25 20:32:08 +0000 | [diff] [blame] | 791 | public Builder(Helper helper, PackageIdentifier id, String runfilesPrefix) { |
| 792 | this(helper.createFreshPackage(id, runfilesPrefix)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 793 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 794 | |
| 795 | protected PackageIdentifier getPackageIdentifier() { |
| 796 | return pkg.getPackageIdentifier(); |
| 797 | } |
| 798 | |
Damien Martin-Guillerez | 8ca065c2 | 2015-11-27 16:22:59 +0000 | [diff] [blame] | 799 | /** Determine if we are in the WORKSPACE file or not */ |
| 800 | public boolean isWorkspace() { |
Lukacs Berki | e19ee27 | 2015-12-10 11:34:29 +0000 | [diff] [blame] | 801 | return pkg.getPackageIdentifier().equals(Label.EXTERNAL_PACKAGE_IDENTIFIER); |
Damien Martin-Guillerez | 8ca065c2 | 2015-11-27 16:22:59 +0000 | [diff] [blame] | 802 | } |
| 803 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 804 | /** |
| 805 | * Sets the name of this package's BUILD file. |
| 806 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 807 | Builder setFilename(Path filename) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 808 | this.filename = filename; |
| 809 | try { |
| 810 | buildFileLabel = createLabel(filename.getBaseName()); |
| 811 | addInputFile(buildFileLabel, Location.fromFile(filename)); |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 812 | } catch (LabelSyntaxException e) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 813 | // This can't actually happen. |
| 814 | throw new AssertionError("Package BUILD file has an illegal name: " + filename); |
| 815 | } |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 816 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 817 | } |
| 818 | |
| 819 | public Label getBuildFileLabel() { |
| 820 | return buildFileLabel; |
| 821 | } |
| 822 | |
| 823 | Path getFilename() { |
| 824 | return filename; |
| 825 | } |
| 826 | |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 827 | public List<Postable> getPosts() { |
| 828 | return posts; |
| 829 | } |
| 830 | |
Janak Ramakrishnan | 0a4c6e4 | 2015-09-17 00:37:58 +0000 | [diff] [blame] | 831 | public List<Event> getEvents() { |
| 832 | return events; |
| 833 | } |
| 834 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 835 | /** |
| 836 | * Sets this package's Make environment. |
| 837 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 838 | Builder setMakeEnv(MakeEnvironment.Builder makeEnv) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 839 | this.makeEnv = makeEnv; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 840 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 841 | } |
| 842 | |
Googler | 9429644 | 2015-03-25 09:47:41 +0000 | [diff] [blame] | 843 | MakeEnvironment.Builder getMakeEnvironment() { |
| 844 | return makeEnv; |
| 845 | } |
| 846 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 847 | /** |
| 848 | * Sets the default visibility for this package. Called at most once per |
| 849 | * package from PackageFactory. |
| 850 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 851 | Builder setDefaultVisibility(RuleVisibility visibility) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 852 | this.defaultVisibility = visibility; |
| 853 | this.defaultVisibilitySet = true; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 854 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 855 | } |
| 856 | |
| 857 | /** |
| 858 | * Sets whether the default visibility is set in the BUILD file. |
| 859 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 860 | Builder setDefaultVisibilitySet(boolean defaultVisibilitySet) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 861 | this.defaultVisibilitySet = defaultVisibilitySet; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 862 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 863 | } |
| 864 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 865 | /** Sets the default value of 'testonly'. Rule-level 'testonly' will override this. */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 866 | Builder setDefaultTestonly(boolean defaultTestonly) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 867 | pkg.setDefaultTestOnly(defaultTestonly); |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 868 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 869 | } |
| 870 | |
| 871 | /** |
| 872 | * Sets the default value of 'deprecation'. Rule-level 'deprecation' will append to this. |
| 873 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 874 | Builder setDefaultDeprecation(String defaultDeprecation) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 875 | pkg.setDefaultDeprecation(defaultDeprecation); |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 876 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 877 | } |
| 878 | |
| 879 | /** |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 880 | * Uses the workspace name from {@code //external} to set this package's workspace name. |
| 881 | */ |
Han-Wen Nienhuys | 2cffe03 | 2015-09-21 15:28:40 +0000 | [diff] [blame] | 882 | @VisibleForTesting |
| 883 | public Builder setWorkspaceName(String workspaceName) { |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 884 | pkg.workspaceName = workspaceName; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 885 | return this; |
Kristina Chodorow | 91876f0 | 2015-02-27 17:14:12 +0000 | [diff] [blame] | 886 | } |
| 887 | |
| 888 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 889 | * Returns whether the "package" function has been called yet |
| 890 | */ |
| 891 | public boolean isPackageFunctionUsed() { |
| 892 | return packageFunctionUsed; |
| 893 | } |
| 894 | |
| 895 | public void setPackageFunctionUsed() { |
| 896 | packageFunctionUsed = true; |
| 897 | } |
| 898 | |
| 899 | /** |
| 900 | * Sets the default header checking mode. |
| 901 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 902 | public Builder setDefaultHdrsCheck(String hdrsCheck) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 903 | // Note that this setting is propagated directly to the package because |
| 904 | // other code needs the ability to read this info directly from the |
| 905 | // under-construction package. See {@link Package#setDefaultHdrsCheck}. |
| 906 | pkg.setDefaultHdrsCheck(hdrsCheck); |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 907 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 908 | } |
| 909 | |
Carmi Grushko | 81dca61 | 2016-07-27 02:12:15 +0000 | [diff] [blame] | 910 | /** Sets the default value of copts. Rule-level copts will append to this. */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 911 | public Builder setDefaultCopts(List<String> defaultCopts) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 912 | this.defaultCopts = defaultCopts; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 913 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 914 | } |
| 915 | |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 916 | public Builder addFeatures(Iterable<String> features) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 917 | Iterables.addAll(this.features, features); |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 918 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 919 | } |
| 920 | |
| 921 | /** |
| 922 | * Declares that errors were encountering while loading this package. |
| 923 | */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 924 | public Builder setContainsErrors() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 925 | containsErrors = true; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 926 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 927 | } |
| 928 | |
| 929 | public boolean containsErrors() { |
| 930 | return containsErrors; |
| 931 | } |
| 932 | |
Klaus Aehlig | 5c04d2a | 2017-06-13 18:14:15 +0200 | [diff] [blame] | 933 | public Builder addPosts(Iterable<Postable> posts) { |
| 934 | for (Postable post : posts) { |
| 935 | addPost(post); |
| 936 | } |
| 937 | return this; |
| 938 | } |
| 939 | |
| 940 | public Builder addPost(Postable post) { |
| 941 | this.posts.add(post); |
| 942 | return this; |
| 943 | } |
| 944 | |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 945 | public Builder addEvents(Iterable<Event> events) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 946 | for (Event event : events) { |
| 947 | addEvent(event); |
| 948 | } |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 949 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 950 | } |
| 951 | |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 952 | public Builder addEvent(Event event) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 953 | this.events.add(event); |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 954 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 955 | } |
| 956 | |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 957 | Builder setSkylarkFileDependencies(ImmutableList<Label> skylarkFileDependencies) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 958 | this.skylarkFileDependencies = skylarkFileDependencies; |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 959 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 960 | } |
| 961 | |
| 962 | /** |
| 963 | * Sets the default license for this package. |
| 964 | */ |
| 965 | void setDefaultLicense(License license) { |
| 966 | this.defaultLicense = license; |
| 967 | } |
| 968 | |
| 969 | License getDefaultLicense() { |
| 970 | return defaultLicense; |
| 971 | } |
| 972 | |
| 973 | /** |
| 974 | * Initializes the default set of distributions for targets in this package. |
| 975 | * |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 976 | * <p> TODO(bazel-team): (2011) consider moving the license & distribs info into Metadata--maybe |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 977 | * even in the Build language. |
| 978 | */ |
| 979 | void setDefaultDistribs(Set<DistributionType> dists) { |
| 980 | this.defaultDistributionSet = dists; |
| 981 | } |
| 982 | |
| 983 | Set<DistributionType> getDefaultDistribs() { |
| 984 | return defaultDistributionSet; |
| 985 | } |
| 986 | |
| 987 | /** |
| 988 | * Sets the default value to use for a rule's {@link RuleClass#COMPATIBLE_ENVIRONMENT_ATTR} |
| 989 | * attribute when not explicitly specified by the rule. Records a package error if |
| 990 | * any labels are duplicated. |
| 991 | */ |
| 992 | void setDefaultCompatibleWith(List<Label> environments, String attrName, Location location) { |
| 993 | if (!checkForDuplicateLabels(environments, "package " + pkg.getName(), attrName, location, |
| 994 | builderEventHandler)) { |
| 995 | setContainsErrors(); |
| 996 | } |
| 997 | pkg.setDefaultCompatibleWith(ImmutableSet.copyOf(environments)); |
| 998 | } |
| 999 | |
| 1000 | /** |
| 1001 | * Sets the default value to use for a rule's {@link RuleClass#RESTRICTED_ENVIRONMENT_ATTR} |
| 1002 | * attribute when not explicitly specified by the rule. Records a package error if |
| 1003 | * any labels are duplicated. |
| 1004 | */ |
| 1005 | void setDefaultRestrictedTo(List<Label> environments, String attrName, Location location) { |
| 1006 | if (!checkForDuplicateLabels(environments, "package " + pkg.getName(), attrName, location, |
| 1007 | builderEventHandler)) { |
| 1008 | setContainsErrors(); |
| 1009 | } |
| 1010 | |
| 1011 | pkg.setDefaultRestrictedTo(ImmutableSet.copyOf(environments)); |
| 1012 | } |
| 1013 | |
| 1014 | /** |
Mark Schaller | ee62445 | 2016-01-13 18:41:24 +0000 | [diff] [blame] | 1015 | * Creates a new {@link Rule} {@code r} where {@code r.getPackage()} is the {@link Package} |
| 1016 | * associated with this {@link Builder}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1017 | * |
Mark Schaller | ee62445 | 2016-01-13 18:41:24 +0000 | [diff] [blame] | 1018 | * <p>The created {@link Rule} will have no attribute values, no output files, and therefore |
| 1019 | * will be in an invalid state. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1020 | */ |
Mark Schaller | ee62445 | 2016-01-13 18:41:24 +0000 | [diff] [blame] | 1021 | Rule createRule( |
| 1022 | Label label, |
| 1023 | RuleClass ruleClass, |
| 1024 | Location location, |
Michajlo Matijkiw | 1a75907 | 2015-10-22 13:52:00 +0000 | [diff] [blame] | 1025 | AttributeContainer attributeContainer) { |
Michajlo Matijkiw | 6d47141 | 2016-08-09 20:35:45 +0000 | [diff] [blame] | 1026 | return new Rule( |
| 1027 | pkg, |
| 1028 | label, |
| 1029 | ruleClass, |
| 1030 | location, |
| 1031 | attributeContainer); |
| 1032 | } |
| 1033 | |
| 1034 | /** |
Michajlo Matijkiw | 0433a68 | 2016-08-15 15:02:36 +0000 | [diff] [blame] | 1035 | * Same as {@link #createRule(Label, RuleClass, Location, AttributeContainer)}, except |
Michajlo Matijkiw | 6d47141 | 2016-08-09 20:35:45 +0000 | [diff] [blame] | 1036 | * allows specifying an {@link ImplicitOutputsFunction} override. Only use if you know what |
| 1037 | * you're doing. |
| 1038 | */ |
| 1039 | Rule createRule( |
| 1040 | Label label, |
| 1041 | RuleClass ruleClass, |
| 1042 | Location location, |
| 1043 | AttributeContainer attributeContainer, |
| 1044 | ImplicitOutputsFunction implicitOutputsFunction) { |
| 1045 | return new Rule( |
| 1046 | pkg, |
| 1047 | label, |
| 1048 | ruleClass, |
| 1049 | location, |
| 1050 | attributeContainer, |
| 1051 | implicitOutputsFunction); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1052 | } |
| 1053 | |
| 1054 | /** |
| 1055 | * Called by the parser when a "mocksubinclude" is encountered, to record the |
| 1056 | * mappings from labels to absolute paths upon which that the validity of |
| 1057 | * this package depends. |
| 1058 | */ |
| 1059 | void addSubinclude(Label label, Path resolvedPath) { |
| 1060 | if (subincludes == null) { |
| 1061 | // This is a TreeMap because the order needs to be deterministic. |
| 1062 | subincludes = Maps.newTreeMap(); |
| 1063 | } |
| 1064 | |
| 1065 | Path oldResolvedPath = subincludes.put(label, resolvedPath); |
| 1066 | if (oldResolvedPath != null && !oldResolvedPath.equals(resolvedPath)){ |
| 1067 | // The same label should have been resolved to the same path |
| 1068 | throw new IllegalStateException("Ambiguous subinclude path"); |
| 1069 | } |
| 1070 | } |
| 1071 | |
| 1072 | public Set<Label> getSubincludeLabels() { |
| 1073 | return subincludes == null ? Sets.<Label>newHashSet() : subincludes.keySet(); |
| 1074 | } |
| 1075 | |
| 1076 | public Map<Label, Path> getSubincludes() { |
| 1077 | return subincludes == null ? Maps.<Label, Path>newHashMap() : subincludes; |
| 1078 | } |
| 1079 | |
| 1080 | public Collection<Target> getTargets() { |
| 1081 | return Package.getTargets(targets); |
| 1082 | } |
| 1083 | |
Han-Wen Nienhuys | 67e6f98 | 2016-01-15 15:54:12 +0000 | [diff] [blame] | 1084 | @Nullable |
| 1085 | public Target getTarget(String name) { |
| 1086 | return targets.get(name); |
| 1087 | } |
| 1088 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1089 | /** |
| 1090 | * Returns an (immutable, unordered) view of all the targets belonging to |
| 1091 | * this package which are instances of the specified class. |
| 1092 | */ |
| 1093 | <T extends Target> Iterable<T> getTargets(Class<T> targetClass) { |
| 1094 | return Package.getTargets(targets, targetClass); |
| 1095 | } |
| 1096 | |
| 1097 | /** |
| 1098 | * An input file name conflicts with an existing package member. |
| 1099 | */ |
| 1100 | static class GeneratedLabelConflict extends NameConflictException { |
| 1101 | private GeneratedLabelConflict(String message) { |
| 1102 | super(message); |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | /** |
| 1107 | * Creates an input file target in this package with the specified name. |
| 1108 | * |
| 1109 | * @param targetName name of the input file. This must be a valid target |
| 1110 | * name as defined by {@link |
| 1111 | * com.google.devtools.build.lib.cmdline.LabelValidator#validateTargetName}. |
| 1112 | * @return the newly-created InputFile, or the old one if it already existed. |
| 1113 | * @throws GeneratedLabelConflict if the name was already taken by a Rule or |
| 1114 | * an OutputFile target. |
| 1115 | * @throws IllegalArgumentException if the name is not a valid label |
| 1116 | */ |
| 1117 | InputFile createInputFile(String targetName, Location location) |
| 1118 | throws GeneratedLabelConflict { |
| 1119 | Target existing = targets.get(targetName); |
| 1120 | if (existing == null) { |
| 1121 | try { |
| 1122 | return addInputFile(createLabel(targetName), location); |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 1123 | } catch (LabelSyntaxException e) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1124 | throw new IllegalArgumentException("FileTarget in package " + pkg.getName() |
| 1125 | + " has illegal name: " + targetName); |
| 1126 | } |
| 1127 | } else if (existing instanceof InputFile) { |
| 1128 | return (InputFile) existing; // idempotent |
| 1129 | } else { |
| 1130 | throw new GeneratedLabelConflict("generated label '//" + pkg.getName() + ":" |
| 1131 | + targetName + "' conflicts with existing " |
| 1132 | + existing.getTargetKind()); |
| 1133 | } |
| 1134 | } |
| 1135 | |
| 1136 | /** |
| 1137 | * Sets the visibility and license for an input file. The input file must already exist as |
| 1138 | * a member of this package. |
| 1139 | * @throws IllegalArgumentException if the input file doesn't exist in this |
| 1140 | * package's target map. |
| 1141 | */ |
| 1142 | void setVisibilityAndLicense(InputFile inputFile, RuleVisibility visibility, License license) { |
| 1143 | String filename = inputFile.getName(); |
| 1144 | Target cacheInstance = targets.get(filename); |
Ulf Adams | 07dba94 | 2015-03-05 14:47:37 +0000 | [diff] [blame] | 1145 | if (!(cacheInstance instanceof InputFile)) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1146 | throw new IllegalArgumentException("Can't set visibility for nonexistent FileTarget " |
| 1147 | + filename + " in package " + pkg.getName() + "."); |
| 1148 | } |
| 1149 | if (!((InputFile) cacheInstance).isVisibilitySpecified() |
| 1150 | || cacheInstance.getVisibility() != visibility |
| 1151 | || cacheInstance.getLicense() != license) { |
| 1152 | targets.put(filename, new InputFile( |
| 1153 | pkg, cacheInstance.getLabel(), cacheInstance.getLocation(), visibility, license)); |
| 1154 | } |
| 1155 | } |
| 1156 | |
| 1157 | /** |
| 1158 | * Creates a label for a target inside this package. |
| 1159 | * |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 1160 | * @throws LabelSyntaxException if the {@code targetName} is invalid |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1161 | */ |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 1162 | Label createLabel(String targetName) throws LabelSyntaxException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1163 | return Label.create(pkg.getPackageIdentifier(), targetName); |
| 1164 | } |
| 1165 | |
| 1166 | /** |
| 1167 | * Adds a package group to the package. |
| 1168 | */ |
| 1169 | void addPackageGroup(String name, Collection<String> packages, Collection<Label> includes, |
| 1170 | EventHandler eventHandler, Location location) |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 1171 | throws NameConflictException, LabelSyntaxException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1172 | PackageGroup group = |
| 1173 | new PackageGroup(createLabel(name), pkg, packages, includes, eventHandler, location); |
| 1174 | Target existing = targets.get(group.getName()); |
| 1175 | if (existing != null) { |
| 1176 | throw nameConflict(group, existing); |
| 1177 | } |
| 1178 | |
| 1179 | targets.put(group.getName(), group); |
| 1180 | |
| 1181 | if (group.containsErrors()) { |
| 1182 | setContainsErrors(); |
| 1183 | } |
| 1184 | } |
| 1185 | |
| 1186 | /** |
| 1187 | * Checks if any labels in the given list appear multiple times and reports an appropriate |
| 1188 | * error message if so. Returns true if no duplicates were found, false otherwise. |
| 1189 | * |
Laurent Le Brun | 41079bd | 2015-06-19 09:37:25 +0000 | [diff] [blame] | 1190 | * <p> TODO(bazel-team): apply this to all build functions (maybe automatically?), possibly |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1191 | * integrate with RuleClass.checkForDuplicateLabels. |
| 1192 | */ |
| 1193 | private static boolean checkForDuplicateLabels(Collection<Label> labels, String owner, |
| 1194 | String attrName, Location location, EventHandler eventHandler) { |
| 1195 | Set<Label> dupes = CollectionUtils.duplicatedElementsOf(labels); |
| 1196 | for (Label dupe : dupes) { |
| 1197 | eventHandler.handle(Event.error(location, String.format( |
| 1198 | "label '%s' is duplicated in the '%s' list of '%s'", dupe, attrName, owner))); |
| 1199 | } |
| 1200 | return dupes.isEmpty(); |
| 1201 | } |
| 1202 | |
| 1203 | /** |
| 1204 | * Adds an environment group to the package. |
| 1205 | */ |
| 1206 | void addEnvironmentGroup(String name, List<Label> environments, List<Label> defaults, |
| 1207 | EventHandler eventHandler, Location location) |
Lukacs Berki | a643436 | 2015-09-15 13:56:14 +0000 | [diff] [blame] | 1208 | throws NameConflictException, LabelSyntaxException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1209 | |
| 1210 | if (!checkForDuplicateLabels(environments, name, "environments", location, eventHandler) |
| 1211 | || !checkForDuplicateLabels(defaults, name, "defaults", location, eventHandler)) { |
| 1212 | setContainsErrors(); |
| 1213 | return; |
| 1214 | } |
| 1215 | |
| 1216 | EnvironmentGroup group = new EnvironmentGroup(createLabel(name), pkg, environments, |
| 1217 | defaults, location); |
| 1218 | Target existing = targets.get(group.getName()); |
| 1219 | if (existing != null) { |
| 1220 | throw nameConflict(group, existing); |
| 1221 | } |
| 1222 | |
| 1223 | targets.put(group.getName(), group); |
| 1224 | Collection<Event> membershipErrors = group.validateMembership(); |
| 1225 | if (!membershipErrors.isEmpty()) { |
| 1226 | for (Event error : membershipErrors) { |
| 1227 | eventHandler.handle(error); |
| 1228 | } |
| 1229 | setContainsErrors(); |
| 1230 | return; |
| 1231 | } |
| 1232 | |
| 1233 | // For each declared environment, make sure it doesn't also belong to some other group. |
| 1234 | for (Label environment : group.getEnvironments()) { |
| 1235 | EnvironmentGroup otherGroup = environmentGroups.get(environment); |
| 1236 | if (otherGroup != null) { |
| 1237 | eventHandler.handle(Event.error(location, "environment " + environment + " belongs to" |
| 1238 | + " both " + group.getLabel() + " and " + otherGroup.getLabel())); |
| 1239 | setContainsErrors(); |
| 1240 | } else { |
| 1241 | environmentGroups.put(environment, group); |
| 1242 | } |
| 1243 | } |
| 1244 | } |
| 1245 | |
Nathan Harmata | cf73844 | 2016-06-28 14:52:21 +0000 | [diff] [blame] | 1246 | /** |
| 1247 | * Same as {@link #addRule}, except with no name conflict checks. |
| 1248 | * |
| 1249 | * <p>Don't call this function unless you know what you're doing. |
| 1250 | */ |
| 1251 | void addRuleUnchecked(Rule rule) { |
Lukacs Berki | 4867ef7 | 2016-05-17 11:15:19 +0000 | [diff] [blame] | 1252 | Preconditions.checkArgument(rule.getPackage() == pkg); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1253 | // Now, modify the package: |
| 1254 | for (OutputFile outputFile : rule.getOutputFiles()) { |
| 1255 | targets.put(outputFile.getName(), outputFile); |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 1256 | PathFragment outputFileFragment = PathFragment.create(outputFile.getName()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1257 | for (int i = 1; i < outputFileFragment.segmentCount(); i++) { |
| 1258 | String prefix = outputFileFragment.subFragment(0, i).toString(); |
laurentlb | 3d2a68c | 2017-06-30 00:32:04 +0200 | [diff] [blame] | 1259 | outputFilePrefixes.putIfAbsent(prefix, outputFile); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1260 | } |
| 1261 | } |
| 1262 | targets.put(rule.getName(), rule); |
| 1263 | if (rule.containsErrors()) { |
| 1264 | this.setContainsErrors(); |
| 1265 | } |
| 1266 | } |
| 1267 | |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1268 | void addRule(Rule rule) throws NameConflictException, InterruptedException { |
Nathan Harmata | cf73844 | 2016-06-28 14:52:21 +0000 | [diff] [blame] | 1269 | checkForConflicts(rule); |
| 1270 | addRuleUnchecked(rule); |
| 1271 | } |
| 1272 | |
Michajlo Matijkiw | aa09648 | 2016-10-19 14:50:32 +0000 | [diff] [blame] | 1273 | private Builder beforeBuild(boolean discoverAssumedInputFiles) throws InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1274 | Preconditions.checkNotNull(pkg); |
| 1275 | Preconditions.checkNotNull(filename); |
| 1276 | Preconditions.checkNotNull(buildFileLabel); |
| 1277 | Preconditions.checkNotNull(makeEnv); |
| 1278 | // Freeze subincludes. |
| 1279 | subincludes = (subincludes == null) |
| 1280 | ? Collections.<Label, Path>emptyMap() |
| 1281 | : Collections.unmodifiableMap(subincludes); |
| 1282 | |
| 1283 | // We create the original BUILD InputFile when the package filename is set; however, the |
| 1284 | // visibility may be overridden with an exports_files directive, so we need to obtain the |
| 1285 | // current instance here. |
| 1286 | buildFile = (InputFile) Preconditions.checkNotNull(targets.get(buildFileLabel.getName())); |
| 1287 | |
| 1288 | List<Rule> rules = Lists.newArrayList(getTargets(Rule.class)); |
| 1289 | |
Michajlo Matijkiw | aa09648 | 2016-10-19 14:50:32 +0000 | [diff] [blame] | 1290 | if (discoverAssumedInputFiles) { |
| 1291 | // All labels mentioned in a rule that refer to an unknown target in the |
| 1292 | // current package are assumed to be InputFiles, so let's create them: |
| 1293 | for (final Rule rule : rules) { |
| 1294 | AggregatingAttributeMapper.of(rule).visitLabels(new AcceptsLabelAttribute() { |
| 1295 | @Override |
| 1296 | public void acceptLabelAttribute(Label label, Attribute attribute) { |
| 1297 | createInputFileMaybe(label, rule.getAttributeLocation(attribute.getName())); |
| 1298 | } |
| 1299 | }); |
| 1300 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1301 | } |
| 1302 | |
| 1303 | // "test_suite" rules have the idiosyncratic semantics of implicitly |
| 1304 | // depending on all tests in the package, iff tests=[] and suites=[]. |
| 1305 | // Note, we implement this here when the Package is fully constructed, |
| 1306 | // since clearly this information isn't available at Rule construction |
| 1307 | // time, as forward references are permitted. |
| 1308 | List<Label> allTests = new ArrayList<>(); |
| 1309 | for (Rule rule : rules) { |
Greg Estren | c48214d | 2015-03-17 22:43:19 +0000 | [diff] [blame] | 1310 | if (TargetUtils.isTestRule(rule) && !TargetUtils.hasManualTag(rule)) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1311 | allTests.add(rule.getLabel()); |
| 1312 | } |
| 1313 | } |
Laurent Le Brun | d6ff51c | 2015-09-03 23:45:05 +0000 | [diff] [blame] | 1314 | Collections.sort(allTests); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1315 | for (Rule rule : rules) { |
| 1316 | AttributeMap attributes = NonconfigurableAttributeMapper.of(rule); |
| 1317 | if (rule.getRuleClass().equals("test_suite") |
Greg Estren | c83a701 | 2016-12-21 00:05:46 +0000 | [diff] [blame] | 1318 | && attributes.get("tests", BuildType.LABEL_LIST).isEmpty()) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1319 | rule.setAttributeValueByName("$implicit_tests", allTests); |
| 1320 | } |
| 1321 | } |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 1322 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1323 | } |
| 1324 | |
Michajlo Matijkiw | 9140718 | 2015-05-29 14:27:09 +0000 | [diff] [blame] | 1325 | /** Intended for use by {@link com.google.devtools.build.lib.skyframe.PackageFunction} only. */ |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1326 | public Builder buildPartial() throws InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1327 | if (alreadyBuilt) { |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 1328 | return this; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1329 | } |
Michajlo Matijkiw | aa09648 | 2016-10-19 14:50:32 +0000 | [diff] [blame] | 1330 | return beforeBuild(/*discoverAssumedInputFiles=*/ true); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1331 | } |
| 1332 | |
Nathan Harmata | 6b253fa | 2016-05-24 15:27:18 +0000 | [diff] [blame] | 1333 | /** |
| 1334 | * Removes a target from the {@link Package} under construction. Intended to be used only by |
| 1335 | * {@link com.google.devtools.build.lib.skyframe.PackageFunction} to remove targets whose |
| 1336 | * labels cross subpackage boundaries. |
| 1337 | */ |
| 1338 | public void removeTarget(Target target) { |
| 1339 | if (target.getPackage() == pkg) { |
| 1340 | this.targets.remove(target.getName()); |
| 1341 | } |
| 1342 | } |
| 1343 | |
Michajlo Matijkiw | 9140718 | 2015-05-29 14:27:09 +0000 | [diff] [blame] | 1344 | /** Intended for use by {@link com.google.devtools.build.lib.skyframe.PackageFunction} only. */ |
Han-Wen Nienhuys | 086e5f2 | 2015-04-21 11:39:59 +0000 | [diff] [blame] | 1345 | public Package finishBuild() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1346 | if (alreadyBuilt) { |
| 1347 | return pkg; |
| 1348 | } |
Han-Wen Nienhuys | f172d39 | 2015-09-18 17:37:51 +0000 | [diff] [blame] | 1349 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1350 | // Freeze targets and distributions. |
| 1351 | targets = ImmutableMap.copyOf(targets); |
| 1352 | defaultDistributionSet = |
| 1353 | Collections.unmodifiableSet(defaultDistributionSet); |
| 1354 | |
Greg Estren | d0f10dc | 2015-03-17 22:19:37 +0000 | [diff] [blame] | 1355 | // Now all targets have been loaded, so we validate the group's member environments. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1356 | for (EnvironmentGroup envGroup : ImmutableSet.copyOf(environmentGroups.values())) { |
Greg Estren | d0f10dc | 2015-03-17 22:19:37 +0000 | [diff] [blame] | 1357 | Collection<Event> errors = envGroup.processMemberEnvironments(targets); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1358 | if (!errors.isEmpty()) { |
| 1359 | addEvents(errors); |
| 1360 | setContainsErrors(); |
| 1361 | } |
| 1362 | } |
| 1363 | |
| 1364 | // Build the package. |
| 1365 | pkg.finishInit(this); |
| 1366 | alreadyBuilt = true; |
| 1367 | return pkg; |
| 1368 | } |
| 1369 | |
Damien Martin-Guillerez | 653df88 | 2016-02-17 21:46:22 +0000 | [diff] [blame] | 1370 | public ExternalPackageBuilder externalPackageData() { |
Han-Wen Nienhuys | f172d39 | 2015-09-18 17:37:51 +0000 | [diff] [blame] | 1371 | return externalPackageData; |
| 1372 | } |
| 1373 | |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1374 | public Package build() throws InterruptedException { |
Michajlo Matijkiw | aa09648 | 2016-10-19 14:50:32 +0000 | [diff] [blame] | 1375 | return build(/*discoverAssumedInputFiles=*/ true); |
| 1376 | } |
| 1377 | |
| 1378 | /** |
| 1379 | * Build the package, optionally adding any labels in the package not already associated with |
| 1380 | * a target as an input file. |
| 1381 | */ |
| 1382 | public Package build(boolean discoverAssumedInputFiles) throws InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1383 | if (alreadyBuilt) { |
| 1384 | return pkg; |
| 1385 | } |
Michajlo Matijkiw | aa09648 | 2016-10-19 14:50:32 +0000 | [diff] [blame] | 1386 | beforeBuild(discoverAssumedInputFiles); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1387 | return finishBuild(); |
| 1388 | } |
| 1389 | |
| 1390 | /** |
| 1391 | * If "label" refers to a non-existent target in the current package, create |
| 1392 | * an InputFile target. |
| 1393 | */ |
| 1394 | void createInputFileMaybe(Label label, Location location) { |
Damien Martin-Guillerez | 4997805 | 2016-01-21 19:55:07 +0000 | [diff] [blame] | 1395 | if (label != null && label.getPackageIdentifier().equals(pkg.getPackageIdentifier())) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1396 | if (!targets.containsKey(label.getName())) { |
| 1397 | addInputFile(label, location); |
| 1398 | } |
| 1399 | } |
| 1400 | } |
| 1401 | |
| 1402 | private InputFile addInputFile(Label label, Location location) { |
| 1403 | InputFile inputFile = new InputFile(pkg, label, location); |
| 1404 | Target prev = targets.put(label.getName(), inputFile); |
| 1405 | Preconditions.checkState(prev == null); |
| 1406 | return inputFile; |
| 1407 | } |
| 1408 | |
| 1409 | /** |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1410 | * Precondition check for addRule. We must maintain these invariants of the package: |
| 1411 | * |
| 1412 | * <ul> |
| 1413 | * <li>Each name refers to at most one target. |
| 1414 | * <li>No rule with errors is inserted into the package. |
| 1415 | * <li>The generating rule of every output file in the package must itself be in the package. |
| 1416 | * </ul> |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1417 | */ |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1418 | private void checkForConflicts(Rule rule) throws NameConflictException, InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1419 | String name = rule.getName(); |
| 1420 | Target existing = targets.get(name); |
| 1421 | if (existing != null) { |
| 1422 | throw nameConflict(rule, existing); |
| 1423 | } |
| 1424 | Map<String, OutputFile> outputFiles = new HashMap<>(); |
| 1425 | |
| 1426 | for (OutputFile outputFile : rule.getOutputFiles()) { |
| 1427 | String outputFileName = outputFile.getName(); |
| 1428 | if (outputFiles.put(outputFileName, outputFile) != null) { // dups within a single rule: |
| 1429 | throw duplicateOutputFile(outputFile, outputFile); |
| 1430 | } |
| 1431 | existing = targets.get(outputFileName); |
| 1432 | if (existing != null) { |
| 1433 | throw duplicateOutputFile(outputFile, existing); |
| 1434 | } |
| 1435 | |
| 1436 | // Check if this output file is the prefix of an already existing one |
| 1437 | if (outputFilePrefixes.containsKey(outputFileName)) { |
| 1438 | throw conflictingOutputFile(outputFile, outputFilePrefixes.get(outputFileName)); |
| 1439 | } |
| 1440 | |
| 1441 | // Check if a prefix of this output file matches an already existing one |
nharmata | b4060b6 | 2017-04-04 17:11:39 +0000 | [diff] [blame] | 1442 | PathFragment outputFileFragment = PathFragment.create(outputFileName); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1443 | for (int i = 1; i < outputFileFragment.segmentCount(); i++) { |
| 1444 | String prefix = outputFileFragment.subFragment(0, i).toString(); |
| 1445 | if (outputFiles.containsKey(prefix)) { |
| 1446 | throw conflictingOutputFile(outputFile, outputFiles.get(prefix)); |
| 1447 | } |
| 1448 | if (targets.containsKey(prefix) |
| 1449 | && targets.get(prefix) instanceof OutputFile) { |
| 1450 | throw conflictingOutputFile(outputFile, (OutputFile) targets.get(prefix)); |
| 1451 | } |
| 1452 | |
laurentlb | 3d2a68c | 2017-06-30 00:32:04 +0200 | [diff] [blame] | 1453 | outputFilePrefixes.putIfAbsent(prefix, outputFile); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1454 | } |
| 1455 | } |
| 1456 | |
| 1457 | checkForInputOutputConflicts(rule, outputFiles.keySet()); |
| 1458 | } |
| 1459 | |
| 1460 | /** |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1461 | * A utility method that checks for conflicts between input file names and output file names for |
| 1462 | * a rule from a build file. |
| 1463 | * |
| 1464 | * @param rule the rule whose inputs and outputs are to be checked for conflicts. |
| 1465 | * @param outputFiles a set containing the names of output files to be generated by the rule. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1466 | * @throws NameConflictException if a conflict is found. |
| 1467 | */ |
| 1468 | private void checkForInputOutputConflicts(Rule rule, Set<String> outputFiles) |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 1469 | throws NameConflictException, InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1470 | PathFragment packageFragment = rule.getLabel().getPackageFragment(); |
| 1471 | for (Label inputLabel : rule.getLabels()) { |
| 1472 | if (packageFragment.equals(inputLabel.getPackageFragment()) |
| 1473 | && outputFiles.contains(inputLabel.getName())) { |
| 1474 | throw inputOutputNameConflict(rule, inputLabel.getName()); |
| 1475 | } |
| 1476 | } |
| 1477 | } |
| 1478 | |
| 1479 | /** An output file conflicts with another output file or the BUILD file. */ |
| 1480 | private NameConflictException duplicateOutputFile(OutputFile duplicate, Target existing) { |
| 1481 | return new NameConflictException(duplicate.getTargetKind() + " '" + duplicate.getName() |
| 1482 | + "' in rule '" + duplicate.getGeneratingRule().getName() + "' " |
| 1483 | + conflictsWith(existing)); |
| 1484 | } |
| 1485 | |
| 1486 | /** The package contains two targets with the same name. */ |
| 1487 | private NameConflictException nameConflict(Target duplicate, Target existing) { |
| 1488 | return new NameConflictException(duplicate.getTargetKind() + " '" + duplicate.getName() |
| 1489 | + "' in package '" + duplicate.getLabel().getPackageName() + "' " |
| 1490 | + conflictsWith(existing)); |
| 1491 | } |
| 1492 | |
| 1493 | /** A a rule has a input/output name conflict. */ |
| 1494 | private NameConflictException inputOutputNameConflict(Rule rule, String conflictingName) { |
| 1495 | return new NameConflictException("rule '" + rule.getName() + "' has file '" |
| 1496 | + conflictingName + "' as both an input and an output"); |
| 1497 | } |
| 1498 | |
| 1499 | private static NameConflictException conflictingOutputFile( |
| 1500 | OutputFile added, OutputFile existing) { |
| 1501 | if (added.getGeneratingRule() == existing.getGeneratingRule()) { |
| 1502 | return new NameConflictException(String.format( |
| 1503 | "rule '%s' has conflicting output files '%s' and '%s'", added.getGeneratingRule() |
| 1504 | .getName(), added.getName(), existing.getName())); |
| 1505 | } else { |
| 1506 | return new NameConflictException(String.format( |
| 1507 | "output file '%s' of rule '%s' conflicts with output file '%s' of rule '%s'", added |
| 1508 | .getName(), added.getGeneratingRule().getName(), existing.getName(), existing |
| 1509 | .getGeneratingRule().getName())); |
| 1510 | } |
| 1511 | } |
| 1512 | |
| 1513 | /** |
| 1514 | * Utility function for generating exception messages. |
| 1515 | */ |
| 1516 | private static String conflictsWith(Target target) { |
| 1517 | String message = "conflicts with existing "; |
| 1518 | if (target instanceof OutputFile) { |
Laurent Le Brun | 1159cc2 | 2017-01-04 12:27:39 +0000 | [diff] [blame] | 1519 | message += |
| 1520 | "generated file from rule '" |
| 1521 | + ((OutputFile) target).getGeneratingRule().getName() |
| 1522 | + "'"; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1523 | } else { |
Laurent Le Brun | 1159cc2 | 2017-01-04 12:27:39 +0000 | [diff] [blame] | 1524 | message += target.getTargetKind(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1525 | } |
Laurent Le Brun | 1159cc2 | 2017-01-04 12:27:39 +0000 | [diff] [blame] | 1526 | return message + ", defined at " + target.getLocation(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1527 | } |
| 1528 | } |
| 1529 | } |