blob: e5a5598a385a0ec2baf028df997d9da454735fa3 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.packages;
16
17import com.google.common.annotations.VisibleForTesting;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.ImmutableSortedSet;
22import com.google.common.collect.Iterables;
23import com.google.common.collect.Lists;
24import com.google.common.collect.Maps;
25import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000026import com.google.devtools.build.lib.cmdline.Label;
Lukacs Berkia6434362015-09-15 13:56:14 +000027import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000028import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.collect.CollectionUtils;
30import com.google.devtools.build.lib.collect.ImmutableSortedKeyMap;
31import com.google.devtools.build.lib.events.Event;
32import com.google.devtools.build.lib.events.EventHandler;
Klaus Aehlig5c04d2a2017-06-13 18:14:15 +020033import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import com.google.devtools.build.lib.events.Location;
35import com.google.devtools.build.lib.packages.AttributeMap.AcceptsLabelAttribute;
36import com.google.devtools.build.lib.packages.License.DistributionType;
Mark Schaller6df81792015-12-10 18:47:47 +000037import com.google.devtools.build.lib.util.Preconditions;
Laurent Le Brund8b80c32016-06-03 14:38:19 +000038import com.google.devtools.build.lib.util.SpellChecker;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039import com.google.devtools.build.lib.vfs.Canonicalizer;
40import com.google.devtools.build.lib.vfs.Path;
41import com.google.devtools.build.lib.vfs.PathFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import java.io.PrintStream;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import java.util.ArrayList;
44import java.util.Collection;
45import java.util.Collections;
46import java.util.HashMap;
47import java.util.List;
48import java.util.Map;
49import java.util.Set;
Han-Wen Nienhuys67e6f982016-01-15 15:54:12 +000050import javax.annotation.Nullable;
51
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052/**
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 Matijkiw17f11eb2015-10-14 04:19:28 +000062public class Package {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010063
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 Chodorow91876f02015-02-27 17:14:12 +0000100 * 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 Adamsd13207c2015-09-04 14:53:43 +0000103 protected String workspaceName;
Kristina Chodorow91876f02015-02-27 17:14:12 +0000104
105 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100106 * The root of the source tree in which this package was found. It is an invariant that
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000107 * {@code sourceRoot.getRelative(packageId.getSourceRoot()).equals(packageDirectory)}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 */
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
carmi31c6a612017-05-17 22:56:19 +0200117 /** The collection of all targets defined in this package, indexed by name. */
118 protected ImmutableSortedKeyMap<String, Target> targets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100119
120 /**
Laurent Le Brun41079bd2015-06-19 09:37:25 +0000121 * Default visibility for rules that do not specify it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100122 */
123 private RuleVisibility defaultVisibility;
124 private boolean defaultVisibilitySet;
125
126 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 * 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 Grushko81dca612016-07-27 02:12:15 +0000141 /** Default copts for cc_* rules. The rules' individual copts will append to this value. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100142 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 Nienhuysd08b27f2015-02-25 16:45:20 +0100161 * 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 Aehlig5c04d2a2017-06-13 18:14:15 +0200194 private ImmutableList<Postable> posts;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100195
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100196 /**
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 Harmata42fb5602016-05-25 20:32:08 +0000208 protected Package(PackageIdentifier packageId, String runfilesPrefix) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100209 this.packageIdentifier = packageId;
Ulf Adamsd13207c2015-09-04 14:53:43 +0000210 this.workspaceName = runfilesPrefix;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 this.nameFragment = Canonicalizer.fragments().intern(packageId.getPackageFragment());
212 this.name = nameFragment.getPathString();
213 }
214
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100215 /** 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 Nienhuysd08b27f2015-02-25 16:45:20 +0100233 * 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 Adamsa28b5402017-02-24 09:28:44 +0000262 // 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 Chodorowdc30c072015-04-22 13:48:47 +0000264 private static Path getSourceRoot(Path buildFile, PathFragment packageFragment) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100265 Path current = buildFile.getParentDirectory();
Kristina Chodorowb6fbab72016-01-28 14:38:31 +0000266 for (int i = 0, len = packageFragment.segmentCount();
267 i < len && !packageFragment.equals(PathFragment.EMPTY_FRAGMENT); i++) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100268 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 Nienhuys086e5f22015-04-21 11:39:59 +0000280 protected void finishInit(Builder builder) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100281 // 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 Brun41079bd2015-06-19 09:37:25 +0000291 this.filename = builder.getFilename();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100292 this.packageDirectory = filename.getParentDirectory();
293
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000294 this.sourceRoot = getSourceRoot(filename, packageIdentifier.getSourceRoot());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295 if ((sourceRoot == null
Dmitry Lomove36a66c2017-02-17 14:48:48 +0000296 || !sourceRoot.getRelative(packageIdentifier.getSourceRoot()).equals(packageDirectory))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100297 && !filename.getBaseName().equals("WORKSPACE")) {
298 throw new IllegalArgumentException(
Kristina Chodorowdc30c072015-04-22 13:48:47 +0000299 "Invalid BUILD file name for package '" + packageIdentifier + "': " + filename);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300 }
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 Nienhuysd08b27f2015-02-25 16:45:20 +0100313 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 Aehlig5c04d2a2017-06-13 18:14:15 +0200319 this.posts = ImmutableList.copyOf(builder.posts);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100320 }
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 Brun41079bd2015-06-19 09:37:25 +0000347 * <p> Assumes invariant:
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000348 * {@code getSourceRoot().getRelative(packageId.getSourceRoot()).equals(getPackageDirectory())}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100349 */
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 Brune8ce8472015-03-12 14:20:01 +0000393 * Returns all make variables for a given platform.
394 */
hlopkoa4778052017-05-26 11:37:28 +0200395 public ImmutableMap<String, String> getAllMakeVariables(String platform) {
Laurent Le Brune8ce8472015-03-12 14:20:01 +0000396 ImmutableMap.Builder<String, String> map = ImmutableMap.builder();
397 for (String var : makeEnv.getBindings().keySet()) {
Dmitry Lomov7d9c6502016-06-30 19:53:51 +0000398 String value = makeEnv.lookup(var, platform);
399 if (value != null) {
400 map.put(var, value);
401 }
Laurent Le Brune8ce8472015-03-12 14:20:01 +0000402 }
403 return map.build();
404 }
405
406 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100407 * Returns the label of this package's BUILD file.
408 *
Laurent Le Brun41079bd2015-06-19 09:37:25 +0000409 * <p> Typically <code>getBuildFileLabel().getName().equals("BUILD")</code> --
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 * 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 Schaller4fa83ac2015-07-10 16:59:37 +0000413 public Label getBuildFileLabel() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100414 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 Aehlig5c04d2a2017-06-13 18:14:15 +0200435 public List<Postable> getPosts() {
436 return posts;
437 }
438
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100439 public List<Event> getEvents() {
440 return events;
441 }
442
carmi31c6a612017-05-17 22:56:19 +0200443 /** Returns an (immutable, unordered) view of all the targets belonging to this package. */
444 public ImmutableSortedKeyMap<String, Target> getTargets() {
445 return targets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100446 }
447
448 /**
carmi31c6a612017-05-17 22:56:19 +0200449 * Common getTargets implementation, accessible by {@link Package.Builder}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100450 */
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 Nienhuys086e5f22015-04-21 11:39:59 +0000465 * {@link Package.Builder}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100466 */
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 Nienhuysd08b27f2015-02-25 16:45:20 +0100473 * 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 Adamsb313a972015-05-04 12:05:57 +0000477 @VisibleForTesting // Should be package-private
478 public Rule getRule(String targetName) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100479 return (Rule) targets.get(targetName);
480 }
481
John Caterb4f461e2016-10-25 16:16:35 +0000482 /** 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);
laurentlb3d2a68c2017-06-30 00:32:04 +0200485 return Iterables.filter(targets, rule -> rule.getRuleClass().equals(ruleClass));
John Caterb4f461e2016-10-25 16:16:35 +0000486 }
487
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100488 /**
Kristina Chodorow91876f02015-02-27 17:14:12 +0000489 * Returns this package's workspace name.
Kristina Chodorow91876f02015-02-27 17:14:12 +0000490 */
Kristina Chodorow734e7f72016-01-29 15:04:31 +0000491 public String getWorkspaceName() {
Kristina Chodorow91876f02015-02-27 17:14:12 +0000492 return workspaceName;
493 }
494
495 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100496 * 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;
nharmatab4060b62017-04-04 17:11:39 +0000523 if (!PathFragment.create(targetName).isNormalized()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100524 // 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 Brun57badf42017-01-02 15:12:24 +0000537 suffix = SpellChecker.didYouMean(targetName, targets.keySet());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100538 }
539
Nathan Harmata42fb5602016-05-25 20:32:08 +0000540 throw makeNoSuchTargetException(targetName, suffix);
541 }
542
543 protected NoSuchTargetException makeNoSuchTargetException(String targetName, String suffix) {
544 Label label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100545 try {
Nathan Harmata42fb5602016-05-25 20:32:08 +0000546 label = createLabel(targetName);
Lukacs Berkia6434362015-09-15 13:56:14 +0000547 } catch (LabelSyntaxException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100548 throw new IllegalArgumentException(targetName);
549 }
Nathan Harmata42fb5602016-05-25 20:32:08 +0000550 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 Nienhuysd08b27f2015-02-25 16:45:20 +0100557 }
558
559 /**
560 * Creates a label for a target inside this package.
561 *
Lukacs Berkia6434362015-09-15 13:56:14 +0000562 * @throws LabelSyntaxException if the {@code targetName} is invalid
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100563 */
Lukacs Berkia6434362015-09-15 13:56:14 +0000564 public Label createLabel(String targetName) throws LabelSyntaxException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100565 return Label.create(packageIdentifier, targetName);
566 }
567
568 /**
569 * Returns the default visibility for this package.
570 */
571 public RuleVisibility getDefaultVisibility() {
Laurent Le Brun41079bd2015-06-19 09:37:25 +0000572 return defaultVisibility;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100573 }
574
575 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100576 * Returns the default testonly value.
577 */
578 public Boolean getDefaultTestOnly() {
579 return defaultTestOnly;
580 }
581
582 /**
Greg Estrenc48214d2015-03-17 22:43:19 +0000583 * Returns the default deprecation value.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100584 */
585 public String getDefaultDeprecation() {
586 return defaultDeprecation;
587 }
588
Carmi Grushko81dca612016-07-27 02:12:15 +0000589 /** Gets the default header checking mode. */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100590 public String getDefaultHdrsCheck() {
Googler198a54f2016-01-18 11:02:10 +0000591 return defaultHdrsCheck != null ? defaultHdrsCheck : "strict";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100592 }
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 Nienhuys58070da2015-04-20 11:37:58 +0000648 return "Package(" + name + ")="
649 + (targets != null ? getTargets(Rule.class) : "initializing...");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100650 }
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 Nienhuysd08b27f2015-02-25 16:45:20 +0100681 }
682
Nathan Harmata42fb5602016-05-25 20:32:08 +0000683 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 Nienhuysf172d392015-09-18 17:37:51 +0000687 b.setFilename(workspacePath);
688 b.setMakeEnv(new MakeEnvironment.Builder());
689 return b;
690 }
691
Nathan Harmatacaa000a2016-06-07 17:46:19 +0000692 /**
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 Nienhuysf172d392015-09-18 17:37:51 +0000696 public static class Builder {
Nathan Harmata42fb5602016-05-25 20:32:08 +0000697 public static interface Helper {
698 /**
699 * Returns a fresh {@link Package} instance that a {@link Builder} will internally mutate
Nathan Harmatacaa000a2016-06-07 17:46:19 +0000700 * during package loading. Called by {@link PackageFactory}.
Nathan Harmata42fb5602016-05-25 20:32:08 +0000701 */
702 Package createFreshPackage(PackageIdentifier packageId, String runfilesPrefix);
Nathan Harmatacaa000a2016-06-07 17:46:19 +0000703
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 Harmata42fb5602016-05-25 20:32:08 +0000709 }
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 Harmatacaa000a2016-06-07 17:46:19 +0000722
723 @Override
724 public void onLoadingComplete(Package pkg) {
725 }
Ulf Adams4226be22015-09-03 17:00:54 +0000726 }
727
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100728 /**
729 * The output instance for this builder. Needs to be instantiated and
730 * available with name info throughout initialization. All other settings
Michajlo Matijkiw17f11eb2015-10-14 04:19:28 +0000731 * are applied during {@link #build}. See {@link Package#Package}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100732 * and {@link Package#finishInit} for details.
733 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000734 protected Package pkg;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100735
Laurent Le Brun41079bd2015-06-19 09:37:25 +0000736 private Path filename = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100737 private Label buildFileLabel = null;
738 private InputFile buildFile = null;
739 private MakeEnvironment.Builder makeEnv = null;
Laurent Le Brun41079bd2015-06-19 09:37:25 +0000740 private RuleVisibility defaultVisibility = ConstantRuleVisibility.PRIVATE;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100741 private boolean defaultVisibilitySet;
742 private List<String> defaultCopts = null;
743 private List<String> features = new ArrayList<>();
744 private List<Event> events = Lists.newArrayList();
Klaus Aehlig5c04d2a2017-06-13 18:14:15 +0200745 private List<Postable> posts = Lists.newArrayList();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100746 private boolean containsErrors = false;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100747
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 Chodorow91876f02015-02-27 17:14:12 +0000755 protected ImmutableList<Label> skylarkFileDependencies = ImmutableList.of();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100756
Han-Wen Nienhuysf172d392015-09-18 17:37:51 +0000757 protected ExternalPackageBuilder externalPackageData = new ExternalPackageBuilder();
758
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100759 /**
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 Nienhuys086e5f22015-04-21 11:39:59 +0000784 protected Builder(Package pkg) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100785 this.pkg = pkg;
786 if (pkg.getName().startsWith("javatests/")) {
787 setDefaultTestonly(true);
788 }
789 }
790
Nathan Harmata42fb5602016-05-25 20:32:08 +0000791 public Builder(Helper helper, PackageIdentifier id, String runfilesPrefix) {
792 this(helper.createFreshPackage(id, runfilesPrefix));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100793 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100794
795 protected PackageIdentifier getPackageIdentifier() {
796 return pkg.getPackageIdentifier();
797 }
798
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000799 /** Determine if we are in the WORKSPACE file or not */
800 public boolean isWorkspace() {
Lukacs Berkie19ee272015-12-10 11:34:29 +0000801 return pkg.getPackageIdentifier().equals(Label.EXTERNAL_PACKAGE_IDENTIFIER);
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000802 }
803
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100804 /**
805 * Sets the name of this package's BUILD file.
806 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000807 Builder setFilename(Path filename) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100808 this.filename = filename;
809 try {
810 buildFileLabel = createLabel(filename.getBaseName());
811 addInputFile(buildFileLabel, Location.fromFile(filename));
Lukacs Berkia6434362015-09-15 13:56:14 +0000812 } catch (LabelSyntaxException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100813 // This can't actually happen.
814 throw new AssertionError("Package BUILD file has an illegal name: " + filename);
815 }
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000816 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100817 }
818
819 public Label getBuildFileLabel() {
820 return buildFileLabel;
821 }
822
823 Path getFilename() {
824 return filename;
825 }
826
Klaus Aehlig5c04d2a2017-06-13 18:14:15 +0200827 public List<Postable> getPosts() {
828 return posts;
829 }
830
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000831 public List<Event> getEvents() {
832 return events;
833 }
834
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100835 /**
836 * Sets this package's Make environment.
837 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000838 Builder setMakeEnv(MakeEnvironment.Builder makeEnv) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100839 this.makeEnv = makeEnv;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000840 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100841 }
842
Googler94296442015-03-25 09:47:41 +0000843 MakeEnvironment.Builder getMakeEnvironment() {
844 return makeEnv;
845 }
846
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100847 /**
848 * Sets the default visibility for this package. Called at most once per
849 * package from PackageFactory.
850 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000851 Builder setDefaultVisibility(RuleVisibility visibility) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100852 this.defaultVisibility = visibility;
853 this.defaultVisibilitySet = true;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000854 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100855 }
856
857 /**
858 * Sets whether the default visibility is set in the BUILD file.
859 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000860 Builder setDefaultVisibilitySet(boolean defaultVisibilitySet) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100861 this.defaultVisibilitySet = defaultVisibilitySet;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000862 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100863 }
864
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100865 /** Sets the default value of 'testonly'. Rule-level 'testonly' will override this. */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000866 Builder setDefaultTestonly(boolean defaultTestonly) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100867 pkg.setDefaultTestOnly(defaultTestonly);
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000868 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100869 }
870
871 /**
872 * Sets the default value of 'deprecation'. Rule-level 'deprecation' will append to this.
873 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000874 Builder setDefaultDeprecation(String defaultDeprecation) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100875 pkg.setDefaultDeprecation(defaultDeprecation);
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000876 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100877 }
878
879 /**
Kristina Chodorow91876f02015-02-27 17:14:12 +0000880 * Uses the workspace name from {@code //external} to set this package's workspace name.
881 */
Han-Wen Nienhuys2cffe032015-09-21 15:28:40 +0000882 @VisibleForTesting
883 public Builder setWorkspaceName(String workspaceName) {
Kristina Chodorow91876f02015-02-27 17:14:12 +0000884 pkg.workspaceName = workspaceName;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000885 return this;
Kristina Chodorow91876f02015-02-27 17:14:12 +0000886 }
887
888 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100889 * 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 Nienhuys086e5f22015-04-21 11:39:59 +0000902 public Builder setDefaultHdrsCheck(String hdrsCheck) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100903 // 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 Nienhuys086e5f22015-04-21 11:39:59 +0000907 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100908 }
909
Carmi Grushko81dca612016-07-27 02:12:15 +0000910 /** Sets the default value of copts. Rule-level copts will append to this. */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000911 public Builder setDefaultCopts(List<String> defaultCopts) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100912 this.defaultCopts = defaultCopts;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000913 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100914 }
915
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000916 public Builder addFeatures(Iterable<String> features) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100917 Iterables.addAll(this.features, features);
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000918 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100919 }
920
921 /**
922 * Declares that errors were encountering while loading this package.
923 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000924 public Builder setContainsErrors() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100925 containsErrors = true;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000926 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100927 }
928
929 public boolean containsErrors() {
930 return containsErrors;
931 }
932
Klaus Aehlig5c04d2a2017-06-13 18:14:15 +0200933 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 Nienhuys086e5f22015-04-21 11:39:59 +0000945 public Builder addEvents(Iterable<Event> events) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100946 for (Event event : events) {
947 addEvent(event);
948 }
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000949 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100950 }
951
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000952 public Builder addEvent(Event event) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100953 this.events.add(event);
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000954 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100955 }
956
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000957 Builder setSkylarkFileDependencies(ImmutableList<Label> skylarkFileDependencies) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100958 this.skylarkFileDependencies = skylarkFileDependencies;
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +0000959 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100960 }
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 Brun41079bd2015-06-19 09:37:25 +0000976 * <p> TODO(bazel-team): (2011) consider moving the license & distribs info into Metadata--maybe
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100977 * 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 Schalleree624452016-01-13 18:41:24 +00001015 * Creates a new {@link Rule} {@code r} where {@code r.getPackage()} is the {@link Package}
1016 * associated with this {@link Builder}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001017 *
Mark Schalleree624452016-01-13 18:41:24 +00001018 * <p>The created {@link Rule} will have no attribute values, no output files, and therefore
1019 * will be in an invalid state.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001020 */
Mark Schalleree624452016-01-13 18:41:24 +00001021 Rule createRule(
1022 Label label,
1023 RuleClass ruleClass,
1024 Location location,
Michajlo Matijkiw1a759072015-10-22 13:52:00 +00001025 AttributeContainer attributeContainer) {
Michajlo Matijkiw6d471412016-08-09 20:35:45 +00001026 return new Rule(
1027 pkg,
1028 label,
1029 ruleClass,
1030 location,
1031 attributeContainer);
1032 }
1033
1034 /**
Michajlo Matijkiw0433a682016-08-15 15:02:36 +00001035 * Same as {@link #createRule(Label, RuleClass, Location, AttributeContainer)}, except
Michajlo Matijkiw6d471412016-08-09 20:35:45 +00001036 * 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001052 }
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 Nienhuys67e6f982016-01-15 15:54:12 +00001084 @Nullable
1085 public Target getTarget(String name) {
1086 return targets.get(name);
1087 }
1088
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001089 /**
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 Berkia6434362015-09-15 13:56:14 +00001123 } catch (LabelSyntaxException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001124 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 Adams07dba942015-03-05 14:47:37 +00001145 if (!(cacheInstance instanceof InputFile)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001146 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 Berkia6434362015-09-15 13:56:14 +00001160 * @throws LabelSyntaxException if the {@code targetName} is invalid
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001161 */
Lukacs Berkia6434362015-09-15 13:56:14 +00001162 Label createLabel(String targetName) throws LabelSyntaxException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001163 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 Berkia6434362015-09-15 13:56:14 +00001171 throws NameConflictException, LabelSyntaxException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001172 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 Brun41079bd2015-06-19 09:37:25 +00001190 * <p> TODO(bazel-team): apply this to all build functions (maybe automatically?), possibly
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001191 * 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 Berkia6434362015-09-15 13:56:14 +00001208 throws NameConflictException, LabelSyntaxException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001209
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 Harmatacf738442016-06-28 14:52:21 +00001246 /**
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 Berki4867ef72016-05-17 11:15:19 +00001252 Preconditions.checkArgument(rule.getPackage() == pkg);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001253 // Now, modify the package:
1254 for (OutputFile outputFile : rule.getOutputFiles()) {
1255 targets.put(outputFile.getName(), outputFile);
nharmatab4060b62017-04-04 17:11:39 +00001256 PathFragment outputFileFragment = PathFragment.create(outputFile.getName());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001257 for (int i = 1; i < outputFileFragment.segmentCount(); i++) {
1258 String prefix = outputFileFragment.subFragment(0, i).toString();
laurentlb3d2a68c2017-06-30 00:32:04 +02001259 outputFilePrefixes.putIfAbsent(prefix, outputFile);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001260 }
1261 }
1262 targets.put(rule.getName(), rule);
1263 if (rule.containsErrors()) {
1264 this.setContainsErrors();
1265 }
1266 }
1267
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001268 void addRule(Rule rule) throws NameConflictException, InterruptedException {
Nathan Harmatacf738442016-06-28 14:52:21 +00001269 checkForConflicts(rule);
1270 addRuleUnchecked(rule);
1271 }
1272
Michajlo Matijkiwaa096482016-10-19 14:50:32 +00001273 private Builder beforeBuild(boolean discoverAssumedInputFiles) throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001274 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 Matijkiwaa096482016-10-19 14:50:32 +00001290 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001301 }
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 Estrenc48214d2015-03-17 22:43:19 +00001310 if (TargetUtils.isTestRule(rule) && !TargetUtils.hasManualTag(rule)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001311 allTests.add(rule.getLabel());
1312 }
1313 }
Laurent Le Brund6ff51c2015-09-03 23:45:05 +00001314 Collections.sort(allTests);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001315 for (Rule rule : rules) {
1316 AttributeMap attributes = NonconfigurableAttributeMapper.of(rule);
1317 if (rule.getRuleClass().equals("test_suite")
Greg Estrenc83a7012016-12-21 00:05:46 +00001318 && attributes.get("tests", BuildType.LABEL_LIST).isEmpty()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001319 rule.setAttributeValueByName("$implicit_tests", allTests);
1320 }
1321 }
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001322 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001323 }
1324
Michajlo Matijkiw91407182015-05-29 14:27:09 +00001325 /** Intended for use by {@link com.google.devtools.build.lib.skyframe.PackageFunction} only. */
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001326 public Builder buildPartial() throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001327 if (alreadyBuilt) {
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001328 return this;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001329 }
Michajlo Matijkiwaa096482016-10-19 14:50:32 +00001330 return beforeBuild(/*discoverAssumedInputFiles=*/ true);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001331 }
1332
Nathan Harmata6b253fa2016-05-24 15:27:18 +00001333 /**
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 Matijkiw91407182015-05-29 14:27:09 +00001344 /** Intended for use by {@link com.google.devtools.build.lib.skyframe.PackageFunction} only. */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001345 public Package finishBuild() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001346 if (alreadyBuilt) {
1347 return pkg;
1348 }
Han-Wen Nienhuysf172d392015-09-18 17:37:51 +00001349
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001350 // Freeze targets and distributions.
1351 targets = ImmutableMap.copyOf(targets);
1352 defaultDistributionSet =
1353 Collections.unmodifiableSet(defaultDistributionSet);
1354
Greg Estrend0f10dc2015-03-17 22:19:37 +00001355 // Now all targets have been loaded, so we validate the group's member environments.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001356 for (EnvironmentGroup envGroup : ImmutableSet.copyOf(environmentGroups.values())) {
Greg Estrend0f10dc2015-03-17 22:19:37 +00001357 Collection<Event> errors = envGroup.processMemberEnvironments(targets);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001358 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-Guillerez653df882016-02-17 21:46:22 +00001370 public ExternalPackageBuilder externalPackageData() {
Han-Wen Nienhuysf172d392015-09-18 17:37:51 +00001371 return externalPackageData;
1372 }
1373
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001374 public Package build() throws InterruptedException {
Michajlo Matijkiwaa096482016-10-19 14:50:32 +00001375 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001383 if (alreadyBuilt) {
1384 return pkg;
1385 }
Michajlo Matijkiwaa096482016-10-19 14:50:32 +00001386 beforeBuild(discoverAssumedInputFiles);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001387 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-Guillerez49978052016-01-21 19:55:07 +00001395 if (label != null && label.getPackageIdentifier().equals(pkg.getPackageIdentifier())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001396 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 Ramakrishnan3c0adb22016-08-15 21:54:55 +00001410 * 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001417 */
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001418 private void checkForConflicts(Rule rule) throws NameConflictException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001419 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
nharmatab4060b62017-04-04 17:11:39 +00001442 PathFragment outputFileFragment = PathFragment.create(outputFileName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001443 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
laurentlb3d2a68c2017-06-30 00:32:04 +02001453 outputFilePrefixes.putIfAbsent(prefix, outputFile);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001454 }
1455 }
1456
1457 checkForInputOutputConflicts(rule, outputFiles.keySet());
1458 }
1459
1460 /**
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001461 * 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 Nienhuysd08b27f2015-02-25 16:45:20 +01001466 * @throws NameConflictException if a conflict is found.
1467 */
1468 private void checkForInputOutputConflicts(Rule rule, Set<String> outputFiles)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +00001469 throws NameConflictException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001470 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 Brun1159cc22017-01-04 12:27:39 +00001519 message +=
1520 "generated file from rule '"
1521 + ((OutputFile) target).getGeneratingRule().getName()
1522 + "'";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001523 } else {
Laurent Le Brun1159cc22017-01-04 12:27:39 +00001524 message += target.getTargetKind();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001525 }
Laurent Le Brun1159cc22017-01-04 12:27:39 +00001526 return message + ", defined at " + target.getLocation();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001527 }
1528 }
1529}