blob: 479b0b51a58258b21ddda73e82112d3c68534433 [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.packages;
16
17import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
18import static com.google.devtools.build.lib.packages.Attribute.attr;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000019import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
20import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021
22import com.google.common.annotations.VisibleForTesting;
Lukacs Berki0e1a9942015-06-18 08:53:18 +000023import com.google.common.base.Function;
24import com.google.common.base.Functions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.common.base.Preconditions;
26import com.google.common.base.Predicate;
27import com.google.common.base.Predicates;
28import com.google.common.collect.ImmutableList;
Lukacs Berki0e1a9942015-06-18 08:53:18 +000029import com.google.common.collect.ImmutableMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.common.collect.ImmutableSet;
31import com.google.common.collect.Ordering;
Lukacs Berkia6434362015-09-15 13:56:14 +000032import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033import com.google.devtools.build.lib.events.EventHandler;
34import com.google.devtools.build.lib.events.Location;
Florian Weikert3f8aac92015-09-07 12:06:02 +000035import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000036import com.google.devtools.build.lib.packages.BuildType.SelectorList;
Mark Schaller4fa83ac2015-07-10 16:59:37 +000037import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.syntax.Argument;
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +000039import com.google.devtools.build.lib.syntax.BaseFunction;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000040import com.google.devtools.build.lib.syntax.Environment;
Florian Weikert3f53fbb2015-08-07 22:25:50 +000041import com.google.devtools.build.lib.syntax.FragmentClassNameResolver;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010042import com.google.devtools.build.lib.syntax.FuncallExpression;
43import com.google.devtools.build.lib.syntax.GlobList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044import com.google.devtools.build.lib.syntax.Label;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000045import com.google.devtools.build.lib.syntax.Runtime;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000046import com.google.devtools.build.lib.syntax.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047import com.google.devtools.build.lib.util.StringUtil;
48import com.google.devtools.build.lib.vfs.PathFragment;
49
50import java.util.ArrayList;
51import java.util.BitSet;
52import java.util.Collection;
53import java.util.Collections;
54import java.util.HashMap;
55import java.util.LinkedHashMap;
56import java.util.LinkedHashSet;
57import java.util.List;
58import java.util.Map;
59import java.util.Set;
60import java.util.regex.Pattern;
61
62import javax.annotation.Nullable;
63import javax.annotation.concurrent.Immutable;
64
65/**
66 * Instances of RuleClass encapsulate the set of attributes of a given "class" of rule, such as
67 * <code>cc_binary</code>.
68 *
69 * <p>This is an instance of the "meta-class" pattern for Rules: we achieve using <i>values</i>
70 * what subclasses achieve using <i>types</i>. (The "Design Patterns" book doesn't include this
71 * pattern, so think of it as something like a cross between a Flyweight and a State pattern. Like
72 * Flyweight, we avoid repeatedly storing data that belongs to many instances. Like State, we
73 * delegate from Rule to RuleClass for the specific behavior of that rule (though unlike state, a
74 * Rule object never changes its RuleClass). This avoids the need to declare one Java class per
75 * class of Rule, yet achieves the same behavior.)
76 *
77 * <p>The use of a metaclass also allows us to compute a mapping from Attributes to small integers
78 * and share this between all rules of the same metaclass. This means we can save the attribute
79 * dictionary for each rule instance using an array, which is much more compact than a hashtable.
80 *
81 * <p>Rule classes whose names start with "$" are considered "abstract"; since they are not valid
82 * identifiers, they cannot be named in the build language. However, they are useful for grouping
83 * related attributes which are inherited.
84 *
85 * <p>The exact values in this class are important. In particular:
86 * <ul>
87 * <li>Changing an attribute from MANDATORY to OPTIONAL creates the potential for null-pointer
88 * exceptions in code that expects a value.
89 * <li>Attributes whose names are preceded by a "$" or a ":" are "hidden", and cannot be redefined
90 * in a BUILD file. They are a useful way of adding a special dependency. By convention,
91 * attributes starting with "$" are implicit dependencies, and those starting with a ":" are
92 * late-bound implicit dependencies, i.e. dependencies that can only be resolved when the
93 * configuration is known.
94 * <li>Attributes should not be introduced into the hierarchy higher then necessary.
95 * <li>The 'deps' and 'data' attributes are treated specially by the code that builds the runfiles
96 * tree. All targets appearing in these attributes appears beneath the ".runfiles" tree; in
97 * addition, "deps" may have rule-specific semantics.
98 * </ul>
99 */
100@Immutable
101public final class RuleClass {
Lukacs Berki0e1a9942015-06-18 08:53:18 +0000102 public static final Function<? super Rule, Map<String, Label>> NO_EXTERNAL_BINDINGS =
103 Functions.<Map<String, Label>>constant(ImmutableMap.<String, Label>of());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104 /**
105 * A constraint for the package name of the Rule instances.
106 */
107 public static class PackageNameConstraint implements PredicateWithMessage<Rule> {
108
109 public static final int ANY_SEGMENT = 0;
110
111 private final int pathSegment;
112
113 private final Set<String> values;
114
115 /**
116 * The pathSegment-th segment of the package must be one of the specified values.
117 * The path segment indexing starts from 1.
118 */
119 public PackageNameConstraint(int pathSegment, String... values) {
120 this.values = ImmutableSet.copyOf(values);
121 this.pathSegment = pathSegment;
122 }
123
124 @Override
125 public boolean apply(Rule input) {
126 PathFragment path = input.getLabel().getPackageFragment();
127 if (pathSegment == ANY_SEGMENT) {
128 return path.getFirstSegment(values) != PathFragment.INVALID_SEGMENT;
129 } else {
130 return path.segmentCount() >= pathSegment
131 && values.contains(path.getSegment(pathSegment - 1));
132 }
133 }
134
135 @Override
136 public String getErrorReason(Rule param) {
137 if (pathSegment == ANY_SEGMENT) {
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000138 return param.getRuleClass() + " rules have to be under a "
139 + StringUtil.joinEnglishList(values, "or", "'") + " directory";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100140 } else if (pathSegment == 1) {
141 return param.getRuleClass() + " rules are only allowed in "
142 + StringUtil.joinEnglishList(StringUtil.append(values, "//", ""), "or");
143 } else {
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000144 return param.getRuleClass() + " rules are only allowed in packages which "
145 + StringUtil.ordinal(pathSegment) + " is " + StringUtil.joinEnglishList(values, "or");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146 }
147 }
148
149 @VisibleForTesting
150 public int getPathSegment() {
151 return pathSegment;
152 }
153
154 @VisibleForTesting
155 public Collection<String> getValues() {
156 return values;
157 }
158 }
159
160 /**
161 * Using this callback function, rules can override their own configuration during the
162 * analysis phase.
163 */
164 public interface Configurator<TConfig, TRule> {
165 TConfig apply(TRule rule, TConfig configuration);
Greg Estren00049432015-08-25 16:43:47 +0000166
167 /**
168 * Describes the Bazel feature this configurator is used for. Used for checking that dynamic
169 * configuration transitions are only applied to expected configurator types.
170 */
171 String getCategory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172 }
173
174 /**
175 * A factory or builder class for rule implementations.
176 */
177 public interface ConfiguredTargetFactory<TConfiguredTarget, TContext> {
178 /**
179 * Returns a fully initialized configured target instance using the given context.
180 */
181 TConfiguredTarget create(TContext ruleContext) throws InterruptedException;
182 }
183
184 /**
185 * Default rule configurator, it doesn't change the assigned configuration.
186 */
187 public static final RuleClass.Configurator<Object, Object> NO_CHANGE =
188 new RuleClass.Configurator<Object, Object>() {
189 @Override
190 public Object apply(Object rule, Object configuration) {
191 return configuration;
192 }
Greg Estren00049432015-08-25 16:43:47 +0000193
194 @Override
195 public String getCategory() {
196 return "core";
Ulf Adams3ab82f72015-09-04 12:10:53 +0000197 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 };
199
200 /**
Ulf Adams71423eb2015-08-06 11:51:17 +0000201 * How to handle the case if the configuration is missing fragments that are required according
202 * to the rule class.
203 */
204 public enum MissingFragmentPolicy {
205 /**
206 * Some rules are monolithic across languages, and we want them to continue to work even when
207 * individual languages are disabled. Use this policy if the rule implementation is handling
208 * missing fragments.
209 */
210 IGNORE,
211
212 /**
213 * Use this policy to generate fail actions for the target rather than failing the analysis
214 * outright. Again, this is used when rules are monolithic across languages, but we still need
215 * to analyze the dependent libraries. (Instead of this mechanism, consider annotating
216 * attributes as unused if certain fragments are unavailable.)
217 */
218 CREATE_FAIL_ACTIONS,
219
220 /**
221 * Use this policy to fail the analysis of that target with an error message; this is the
222 * default.
223 */
224 FAIL_ANALYSIS;
225 }
226
227 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100228 * For Bazel's constraint system: the attribute that declares the set of environments a rule
229 * supports, overriding the defaults for their respective groups.
230 */
231 public static final String RESTRICTED_ENVIRONMENT_ATTR = "restricted_to";
232
233 /**
234 * For Bazel's constraint system: the attribute that declares the set of environments a rule
235 * supports, appending them to the defaults for their respective groups.
236 */
237 public static final String COMPATIBLE_ENVIRONMENT_ATTR = "compatible_with";
238
239 /**
240 * For Bazel's constraint system: the implicit attribute used to store rule class restriction
241 * defaults as specified by {@link Builder#restrictedTo}.
242 */
243 public static final String DEFAULT_RESTRICTED_ENVIRONMENT_ATTR =
244 "$" + RESTRICTED_ENVIRONMENT_ATTR;
245
246 /**
247 * For Bazel's constraint system: the implicit attribute used to store rule class compatibility
248 * defaults as specified by {@link Builder#compatibleWith}.
249 */
250 public static final String DEFAULT_COMPATIBLE_ENVIRONMENT_ATTR =
251 "$" + COMPATIBLE_ENVIRONMENT_ATTR;
252
253 /**
254 * Checks if an attribute is part of the constraint system.
255 */
256 public static boolean isConstraintAttribute(String attr) {
257 return RESTRICTED_ENVIRONMENT_ATTR.equals(attr)
258 || COMPATIBLE_ENVIRONMENT_ATTR.equals(attr)
259 || DEFAULT_RESTRICTED_ENVIRONMENT_ATTR.equals(attr)
260 || DEFAULT_COMPATIBLE_ENVIRONMENT_ATTR.equals(attr);
261 }
262
263 /**
264 * A support class to make it easier to create {@code RuleClass} instances.
265 * This class follows the 'fluent builder' pattern.
266 *
267 * <p>The {@link #addAttribute} method will throw an exception if an attribute
268 * of that name already exists. Use {@link #overrideAttribute} in that case.
269 */
270 public static final class Builder {
Laurent Le Brun6ce51e12015-07-07 11:54:41 +0000271 private static final Pattern RULE_NAME_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100272
273 /**
274 * The type of the rule class, which determines valid names and required
275 * attributes.
276 */
277 public enum RuleClassType {
278 /**
279 * Abstract rules are intended for rule classes that are just used to
280 * factor out common attributes, and for rule classes that are used only
281 * internally. These rules cannot be instantiated by a BUILD file.
282 *
283 * <p>The rule name must contain a '$' and {@link
284 * TargetUtils#isTestRuleName} must return false for the name.
285 */
286 ABSTRACT {
287 @Override
288 public void checkName(String name) {
289 Preconditions.checkArgument(
Ulf Adams07dba942015-03-05 14:47:37 +0000290 (name.contains("$") && !TargetUtils.isTestRuleName(name)) || name.isEmpty());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100291 }
292
293 @Override
294 public void checkAttributes(Map<String, Attribute> attributes) {
295 // No required attributes.
296 }
297 },
298
299 /**
300 * Invisible rule classes should contain a dollar sign so that they cannot be instantiated
301 * by the user. They are different from abstract rules in that they can be instantiated
302 * at will.
303 */
304 INVISIBLE {
305 @Override
306 public void checkName(String name) {
307 Preconditions.checkArgument(name.contains("$"));
308 }
309
310 @Override
311 public void checkAttributes(Map<String, Attribute> attributes) {
312 // No required attributes.
313 }
314 },
315
316 /**
317 * Normal rules are instantiable by BUILD files. Their names must therefore
318 * obey the rules for identifiers in the BUILD language. In addition,
319 * {@link TargetUtils#isTestRuleName} must return false for the name.
320 */
321 NORMAL {
322 @Override
323 public void checkName(String name) {
Ulf Adams07dba942015-03-05 14:47:37 +0000324 Preconditions.checkArgument(
325 !TargetUtils.isTestRuleName(name) && RULE_NAME_PATTERN.matcher(name).matches(),
326 "Invalid rule name: %s", name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100327 }
328
329 @Override
330 public void checkAttributes(Map<String, Attribute> attributes) {
331 for (Attribute attribute : REQUIRED_ATTRIBUTES_FOR_NORMAL_RULES) {
332 Attribute presentAttribute = attributes.get(attribute.getName());
333 Preconditions.checkState(presentAttribute != null,
334 "Missing mandatory '%s' attribute in normal rule class.", attribute.getName());
335 Preconditions.checkState(presentAttribute.getType().equals(attribute.getType()),
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000336 "Mandatory attribute '%s' in normal rule class has incorrect type (expected"
337 + " %s).", attribute.getName(), attribute.getType());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100338 }
339 }
340 },
341
342 /**
343 * Workspace rules can only be instantiated from a WORKSPACE file. Their names obey the
344 * rule for identifiers.
345 */
346 WORKSPACE {
347 @Override
348 public void checkName(String name) {
349 Preconditions.checkArgument(RULE_NAME_PATTERN.matcher(name).matches());
350 }
351
352 @Override
353 public void checkAttributes(Map<String, Attribute> attributes) {
354 // No required attributes.
355 }
356 },
357
358 /**
359 * Test rules are instantiable by BUILD files and are handled specially
360 * when run with the 'test' command. Their names must obey the rules
361 * for identifiers in the BUILD language and {@link
362 * TargetUtils#isTestRuleName} must return true for the name.
363 *
364 * <p>In addition, test rules must contain certain attributes. See {@link
365 * Builder#REQUIRED_ATTRIBUTES_FOR_TESTS}.
366 */
367 TEST {
368 @Override
369 public void checkName(String name) {
370 Preconditions.checkArgument(TargetUtils.isTestRuleName(name)
371 && RULE_NAME_PATTERN.matcher(name).matches());
372 }
373
374 @Override
375 public void checkAttributes(Map<String, Attribute> attributes) {
376 for (Attribute attribute : REQUIRED_ATTRIBUTES_FOR_TESTS) {
377 Attribute presentAttribute = attributes.get(attribute.getName());
378 Preconditions.checkState(presentAttribute != null,
379 "Missing mandatory '%s' attribute in test rule class.", attribute.getName());
380 Preconditions.checkState(presentAttribute.getType().equals(attribute.getType()),
381 "Mandatory attribute '%s' in test rule class has incorrect type (expcected %s).",
382 attribute.getName(), attribute.getType());
383 }
384 }
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000385 },
386
387 /**
388 * Placeholder rules are only instantiated when packages which refer to non-native rule
389 * classes are deserialized. At this time, non-native rule classes can't be serialized. To
390 * prevent crashes on deserialization, when a package containing a rule with a non-native rule
391 * class is deserialized, the rule is assigned a placeholder rule class. This is compatible
392 * with our limited set of package serialization use cases.
393 *
394 * Placeholder rule class names obey the rule for identifiers.
395 */
396 PLACEHOLDER {
397 @Override
398 public void checkName(String name) {
399 Preconditions.checkArgument(RULE_NAME_PATTERN.matcher(name).matches(), name);
400 }
401
402 @Override
403 public void checkAttributes(Map<String, Attribute> attributes) {
404 // No required attributes; this rule class cannot have the wrong set of attributes now
405 // because, if it did, the rule class would have failed to build before the package
406 // referring to it was serialized.
407 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100408 };
409
410 /**
411 * Checks whether the given name is valid for the current rule class type.
412 *
413 * @throws IllegalArgumentException if the name is not valid
414 */
415 public abstract void checkName(String name);
416
417 /**
418 * Checks whether the given set of attributes contains all the required
419 * attributes for the current rule class type.
420 *
421 * @throws IllegalArgumentException if a required attribute is missing
422 */
423 public abstract void checkAttributes(Map<String, Attribute> attributes);
424 }
425
426 /**
427 * A predicate that filters rule classes based on their names.
428 */
429 public static class RuleClassNamePredicate implements Predicate<RuleClass> {
430
431 private final Set<String> ruleClasses;
432
433 public RuleClassNamePredicate(Iterable<String> ruleClasses) {
434 this.ruleClasses = ImmutableSet.copyOf(ruleClasses);
435 }
436
437 public RuleClassNamePredicate(String... ruleClasses) {
438 this.ruleClasses = ImmutableSet.copyOf(ruleClasses);
439 }
440
441 public RuleClassNamePredicate() {
442 this(ImmutableSet.<String>of());
443 }
444
445 @Override
446 public boolean apply(RuleClass ruleClass) {
447 return ruleClasses.contains(ruleClass.getName());
448 }
449
450 @Override
451 public int hashCode() {
452 return ruleClasses.hashCode();
453 }
454
455 @Override
456 public boolean equals(Object o) {
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +0000457 return (o instanceof RuleClassNamePredicate)
458 && ruleClasses.equals(((RuleClassNamePredicate) o).ruleClasses);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100459 }
460
461 @Override
462 public String toString() {
463 return ruleClasses.isEmpty() ? "nothing" : StringUtil.joinEnglishList(ruleClasses);
464 }
465 }
466
467 /**
468 * List of required attributes for normal rules, name and type.
469 */
470 public static final List<Attribute> REQUIRED_ATTRIBUTES_FOR_NORMAL_RULES = ImmutableList.of(
471 attr("tags", Type.STRING_LIST).build()
472 );
473
474 /**
475 * List of required attributes for test rules, name and type.
476 */
477 public static final List<Attribute> REQUIRED_ATTRIBUTES_FOR_TESTS = ImmutableList.of(
478 attr("tags", Type.STRING_LIST).build(),
479 attr("size", Type.STRING).build(),
480 attr("timeout", Type.STRING).build(),
481 attr("flaky", Type.BOOLEAN).build(),
482 attr("shard_count", Type.INTEGER).build(),
483 attr("local", Type.BOOLEAN).build()
484 );
485
486 private String name;
487 private final RuleClassType type;
488 private final boolean skylark;
489 private boolean documented;
490 private boolean publicByDefault = false;
491 private boolean binaryOutput = true;
492 private boolean workspaceOnly = false;
493 private boolean outputsDefaultExecutable = false;
494 private ImplicitOutputsFunction implicitOutputsFunction = ImplicitOutputsFunction.NONE;
495 private Configurator<?, ?> configurator = NO_CHANGE;
496 private ConfiguredTargetFactory<?, ?> configuredTargetFactory = null;
497 private PredicateWithMessage<Rule> validityPredicate =
498 PredicatesWithMessage.<Rule>alwaysTrue();
499 private Predicate<String> preferredDependencyPredicate = Predicates.alwaysFalse();
500 private List<Class<?>> advertisedProviders = new ArrayList<>();
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000501 private BaseFunction configuredTargetFunction = null;
Lukacs Berki0e1a9942015-06-18 08:53:18 +0000502 private Function<? super Rule, Map<String, Label>> externalBindingsFunction =
503 NO_EXTERNAL_BINDINGS;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000504 private Environment ruleDefinitionEnvironment = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100505 private Set<Class<?>> configurationFragments = new LinkedHashSet<>();
Ulf Adams71423eb2015-08-06 11:51:17 +0000506 private MissingFragmentPolicy missingFragmentPolicy = MissingFragmentPolicy.FAIL_ANALYSIS;
Florian Weikert3f8aac92015-09-07 12:06:02 +0000507 private Map<ConfigurationTransition, ImmutableSet<String>> requiredFragmentNames =
508 new LinkedHashMap<>();
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000509 private FragmentClassNameResolver fragmentNameResolver;
510
Greg Estrenc04c88f2015-03-06 19:45:50 +0000511 private boolean supportsConstraintChecking = true;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100512
513 private final Map<String, Attribute> attributes = new LinkedHashMap<>();
514
515 /**
516 * Constructs a new {@code RuleClassBuilder} using all attributes from all
517 * parent rule classes. An attribute cannot exist in more than one parent.
518 *
519 * <p>The rule type affects the the allowed names and the required
520 * attributes (see {@link RuleClassType}).
521 *
522 * @throws IllegalArgumentException if an attribute with the same name exists
523 * in more than one parent
524 */
525 public Builder(String name, RuleClassType type, boolean skylark, RuleClass... parents) {
526 this.name = name;
527 this.skylark = skylark;
528 this.type = type;
529 this.documented = type != RuleClassType.ABSTRACT;
530 for (RuleClass parent : parents) {
531 if (parent.getValidityPredicate() != PredicatesWithMessage.<Rule>alwaysTrue()) {
532 setValidityPredicate(parent.getValidityPredicate());
533 }
534 if (parent.preferredDependencyPredicate != Predicates.<String>alwaysFalse()) {
535 setPreferredDependencyPredicate(parent.preferredDependencyPredicate);
536 }
537 configurationFragments.addAll(parent.requiredConfigurationFragments);
Ulf Adams71423eb2015-08-06 11:51:17 +0000538 missingFragmentPolicy = parent.missingFragmentPolicy;
Greg Estrenc04c88f2015-03-06 19:45:50 +0000539 supportsConstraintChecking = parent.supportsConstraintChecking;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100540
541 for (Attribute attribute : parent.getAttributes()) {
542 String attrName = attribute.getName();
543 Preconditions.checkArgument(
544 !attributes.containsKey(attrName) || attributes.get(attrName) == attribute,
Googler2935d672015-03-12 01:25:11 +0000545 "Attribute %s is inherited multiple times in %s ruleclass",
546 attrName,
547 name);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100548 attributes.put(attrName, attribute);
549 }
Lukacs Berkiac4f9422015-05-29 10:06:30 +0000550
551 advertisedProviders.addAll(parent.getAdvertisedProviders());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100552 }
553 // TODO(bazel-team): move this testonly attribute setting to somewhere else
554 // preferably to some base RuleClass implementation.
555 if (this.type.equals(RuleClassType.TEST)) {
556 Attribute.Builder<Boolean> testOnlyAttr = attr("testonly", BOOLEAN).value(true)
557 .nonconfigurable("policy decision: this shouldn't depend on the configuration");
558 if (attributes.containsKey("testonly")) {
559 override(testOnlyAttr);
560 } else {
561 add(testOnlyAttr);
562 }
563 }
564 }
565
566 /**
567 * Checks that required attributes for test rules are present, creates the
568 * {@link RuleClass} object and returns it.
569 *
570 * @throws IllegalStateException if any of the required attributes is missing
571 */
572 public RuleClass build() {
573 return build(name);
574 }
575
576 /**
577 * Same as {@link #build} except with setting the name parameter.
578 */
579 public RuleClass build(String name) {
580 Preconditions.checkArgument(this.name.isEmpty() || this.name.equals(name));
581 type.checkName(name);
582 type.checkAttributes(attributes);
583 boolean skylarkExecutable =
584 skylark && (type == RuleClassType.NORMAL || type == RuleClassType.TEST);
585 Preconditions.checkState(
586 (type == RuleClassType.ABSTRACT)
587 == (configuredTargetFactory == null && configuredTargetFunction == null));
588 Preconditions.checkState(skylarkExecutable == (configuredTargetFunction != null));
589 Preconditions.checkState(skylarkExecutable == (ruleDefinitionEnvironment != null));
Lukacs Berki0e1a9942015-06-18 08:53:18 +0000590 Preconditions.checkState(workspaceOnly || externalBindingsFunction == NO_EXTERNAL_BINDINGS);
591
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100592 return new RuleClass(name, skylarkExecutable, documented, publicByDefault, binaryOutput,
593 workspaceOnly, outputsDefaultExecutable, implicitOutputsFunction, configurator,
594 configuredTargetFactory, validityPredicate, preferredDependencyPredicate,
595 ImmutableSet.copyOf(advertisedProviders), configuredTargetFunction,
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000596 externalBindingsFunction, ruleDefinitionEnvironment, configurationFragments,
Florian Weikert3f8aac92015-09-07 12:06:02 +0000597 ImmutableMap.copyOf(requiredFragmentNames), fragmentNameResolver, missingFragmentPolicy,
Greg Estrenc04c88f2015-03-06 19:45:50 +0000598 supportsConstraintChecking, attributes.values().toArray(new Attribute[0]));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 }
600
601 /**
602 * Declares that the implementation of this rule class requires the given configuration
603 * fragments to be present in the configuration. The value is inherited by subclasses.
604 *
605 * <p>For backwards compatibility, if the set is empty, all fragments may be accessed. But note
606 * that this is only enforced in the {@link com.google.devtools.build.lib.analysis.RuleContext}
607 * class.
608 */
609 public Builder requiresConfigurationFragments(Class<?>... configurationFragment) {
610 Collections.addAll(configurationFragments, configurationFragment);
611 return this;
612 }
613
Ulf Adams71423eb2015-08-06 11:51:17 +0000614 /**
615 * Sets the policy for the case where the configuration is missing required fragments (see
616 * {@link #requiresConfigurationFragments}).
617 */
618 public Builder setMissingFragmentPolicy(MissingFragmentPolicy missingFragmentPolicy) {
619 this.missingFragmentPolicy = missingFragmentPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100620 return this;
621 }
Florian Weikert3f8aac92015-09-07 12:06:02 +0000622
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000623 /**
Florian Weikert3f8aac92015-09-07 12:06:02 +0000624 * Declares the configuration fragments that are required by this rule.
625 *
626 * <p>In contrast to {@link #requiresConfigurationFragments(Class...)}, this method a) takes the
627 * names of fragments instead of their classes and b) distinguishes whether the fragments can be
628 * accessed in host (HOST) or target (NONE) configuration.
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000629 */
630 public Builder requiresConfigurationFragments(
Florian Weikert3f8aac92015-09-07 12:06:02 +0000631 FragmentClassNameResolver fragmentNameResolver,
632 Map<ConfigurationTransition, ImmutableSet<String>> configurationFragmentNames) {
633 requiredFragmentNames.putAll(configurationFragmentNames);
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000634 this.fragmentNameResolver = fragmentNameResolver;
635 return this;
636 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100637
638 public Builder setUndocumented() {
639 documented = false;
640 return this;
641 }
642
643 public Builder publicByDefault() {
644 publicByDefault = true;
645 return this;
646 }
647
648 public Builder setWorkspaceOnly() {
649 workspaceOnly = true;
650 return this;
651 }
652
653 /**
654 * Determines the outputs of this rule to be created beneath the {@code
655 * genfiles} directory. By default, files are created beneath the {@code bin}
656 * directory.
657 *
658 * <p>This property is not inherited and this method should not be called by
659 * builder of {@link RuleClassType#ABSTRACT} rule class.
660 *
661 * @throws IllegalStateException if called for abstract rule class builder
662 */
663 public Builder setOutputToGenfiles() {
664 Preconditions.checkState(type != RuleClassType.ABSTRACT,
665 "Setting not inherited property (output to genrules) of abstract rule class '%s'", name);
666 this.binaryOutput = false;
667 return this;
668 }
669
670 /**
671 * Sets the implicit outputs function of the rule class. The default implicit
672 * outputs function is {@link ImplicitOutputsFunction#NONE}.
673 *
674 * <p>This property is not inherited and this method should not be called by
675 * builder of {@link RuleClassType#ABSTRACT} rule class.
676 *
677 * @throws IllegalStateException if called for abstract rule class builder
678 */
679 public Builder setImplicitOutputsFunction(
680 ImplicitOutputsFunction implicitOutputsFunction) {
681 Preconditions.checkState(type != RuleClassType.ABSTRACT,
682 "Setting not inherited property (implicit output function) of abstract rule class '%s'",
683 name);
684 this.implicitOutputsFunction = implicitOutputsFunction;
685 return this;
686 }
687
688 public Builder cfg(Configurator<?, ?> configurator) {
689 Preconditions.checkState(type != RuleClassType.ABSTRACT,
690 "Setting not inherited property (cfg) of abstract rule class '%s'", name);
691 this.configurator = configurator;
692 return this;
693 }
694
695 public Builder factory(ConfiguredTargetFactory<?, ?> factory) {
696 this.configuredTargetFactory = factory;
697 return this;
698 }
699
700 public Builder setValidityPredicate(PredicateWithMessage<Rule> predicate) {
701 this.validityPredicate = predicate;
702 return this;
703 }
704
705 public Builder setPreferredDependencyPredicate(Predicate<String> predicate) {
706 this.preferredDependencyPredicate = predicate;
707 return this;
708 }
709
710 /**
711 * State that the rule class being built possibly supplies the specified provider to its direct
712 * dependencies.
713 *
714 * <p>When computing the set of aspects required for a rule, only the providers listed here are
715 * considered. The presence of a provider here does not mean that the rule <b>must</b> implement
716 * said provider, merely that it <b>can</b>. After the configured target is constructed from
717 * this rule, aspects will be filtered according to the set of actual providers.
718 *
719 * <p>This is here so that we can do the loading phase overestimation required for
720 * "blaze query", which does not have the configured targets available.
721 *
722 * <p>It's okay for the rule class eventually not to supply it (possibly based on analysis phase
723 * logic), but if a provider is not advertised but is supplied, aspects that require the it will
724 * not be evaluated for the rule.
725 */
726 public Builder advertiseProvider(Class<?>... providers) {
727 Collections.addAll(advertisedProviders, providers);
728 return this;
729 }
730
731 private void addAttribute(Attribute attribute) {
732 Preconditions.checkState(!attributes.containsKey(attribute.getName()),
733 "An attribute with the name '%s' already exists.", attribute.getName());
734 attributes.put(attribute.getName(), attribute);
735 }
736
737 private void overrideAttribute(Attribute attribute) {
738 String attrName = attribute.getName();
739 Preconditions.checkState(attributes.containsKey(attrName),
740 "No such attribute '%s' to override in ruleclass '%s'.", attrName, name);
741 Type<?> origType = attributes.get(attrName).getType();
742 Type<?> newType = attribute.getType();
743 Preconditions.checkState(origType.equals(newType),
744 "The type of the new attribute '%s' is different from the original one '%s'.",
745 newType, origType);
746 attributes.put(attrName, attribute);
747 }
748
749 /**
750 * Builds attribute from the attribute builder and adds it to this rule
751 * class.
752 *
753 * @param attr attribute builder
754 */
755 public <TYPE> Builder add(Attribute.Builder<TYPE> attr) {
756 addAttribute(attr.build());
757 return this;
758 }
759
760 /**
761 * Builds attribute from the attribute builder and overrides the attribute
762 * with the same name.
763 *
764 * @throws IllegalArgumentException if the attribute does not override one of the same name
765 */
766 public <TYPE> Builder override(Attribute.Builder<TYPE> attr) {
767 overrideAttribute(attr.build());
768 return this;
769 }
770
771 /**
772 * Adds or overrides the attribute in the rule class. Meant for Skylark usage.
773 */
774 public void addOrOverrideAttribute(Attribute attribute) {
Han-Wen Nienhuysd39e1e12015-08-20 13:52:19 +0000775 if (attributes.containsKey(attribute.getName())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100776 overrideAttribute(attribute);
777 } else {
778 addAttribute(attribute);
779 }
780 }
781
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000782 /** True if the rule class contains an attribute named {@code name}. */
783 public boolean contains(String name) {
784 return attributes.containsKey(name);
785 }
786
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100787 /**
788 * Sets the rule implementation function. Meant for Skylark usage.
789 */
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000790 public Builder setConfiguredTargetFunction(BaseFunction func) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100791 this.configuredTargetFunction = func;
792 return this;
793 }
794
Lukacs Berki0e1a9942015-06-18 08:53:18 +0000795 public Builder setExternalBindingsFunction(Function<? super Rule, Map<String, Label>> func) {
796 this.externalBindingsFunction = func;
797 return this;
798 }
799
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100800 /**
801 * Sets the rule definition environment. Meant for Skylark usage.
802 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000803 public Builder setRuleDefinitionEnvironment(Environment env) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100804 this.ruleDefinitionEnvironment = env;
805 return this;
806 }
807
808 /**
809 * Removes an attribute with the same name from this rule class.
810 *
811 * @throws IllegalArgumentException if the attribute with this name does
812 * not exist
813 */
814 public <TYPE> Builder removeAttribute(String name) {
815 Preconditions.checkState(attributes.containsKey(name), "No such attribute '%s' to remove.",
816 name);
817 attributes.remove(name);
818 return this;
819 }
820
821 /**
822 * This rule class outputs a default executable for every rule with the same name as
823 * the rules's. Only works for Skylark.
824 */
825 public <TYPE> Builder setOutputsDefaultExecutable() {
826 this.outputsDefaultExecutable = true;
827 return this;
828 }
829
830 /**
831 * Declares that instances of this rule are compatible with the specified environments,
832 * in addition to the defaults declared by their environment groups. This can be overridden
833 * by rule-specific declarations. See
834 * {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics} for details.
835 */
836 public <TYPE> Builder compatibleWith(Label... environments) {
837 add(attr(DEFAULT_COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST).cfg(HOST)
838 .value(ImmutableList.copyOf(environments)));
839 return this;
840 }
841
842 /**
843 * Declares that instances of this rule are restricted to the specified environments, i.e.
844 * these override the defaults declared by their environment groups. This can be overridden
845 * by rule-specific declarations. See
846 * {@link com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics} for details.
847 *
848 * <p>The input list cannot be empty.
849 */
850 public <TYPE> Builder restrictedTo(Label firstEnvironment, Label... otherEnvironments) {
851 ImmutableList<Label> environments = ImmutableList.<Label>builder().add(firstEnvironment)
852 .add(otherEnvironments).build();
853 add(attr(DEFAULT_RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST).cfg(HOST).value(environments));
854 return this;
855
856 }
857
858 /**
Greg Estrenc04c88f2015-03-06 19:45:50 +0000859 * Exempts rules of this type from the constraint enforcement system. This should only be
860 * applied to rules that are intrinsically incompatible with constraint checking (any
861 * application of this method weakens the reach and strength of the system).
862 *
863 * @param reason user-informative message explaining the reason for exemption (not used)
864 */
865 public <TYPE> Builder exemptFromConstraintChecking(String reason) {
866 Preconditions.checkState(this.supportsConstraintChecking);
867 this.supportsConstraintChecking = false;
Greg Estren2ffc4fe2015-06-02 19:40:41 +0000868 attributes.remove(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR);
869 attributes.remove(RuleClass.RESTRICTED_ENVIRONMENT_ATTR);
Greg Estrenc04c88f2015-03-06 19:45:50 +0000870 return this;
871 }
872
873 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100874 * Returns an Attribute.Builder object which contains a replica of the
875 * same attribute in the parent rule if exists.
876 *
877 * @param name the name of the attribute
878 */
879 public Attribute.Builder<?> copy(String name) {
880 Preconditions.checkArgument(attributes.containsKey(name),
881 "Attribute %s does not exist in parent rule class.", name);
882 return attributes.get(name).cloneBuilder();
883 }
884 }
885
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000886 public static Builder createPlaceholderBuilder(final String name, final Location ruleLocation,
887 ImmutableList<RuleClass> parents) {
888 return new Builder(name, RuleClassType.PLACEHOLDER, /*skylark=*/true,
889 parents.toArray(new RuleClass[parents.size()])).factory(
890 new ConfiguredTargetFactory<Object, Object>() {
891 @Override
892 public Object create(Object ruleContext) throws InterruptedException {
893 throw new IllegalStateException(
894 "Cannot create configured targets from rule with placeholder class named \"" + name
895 + "\" at " + ruleLocation);
896 }
897 });
898 }
899
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100900 private final String name; // e.g. "cc_library"
901
902 /**
903 * The kind of target represented by this RuleClass (e.g. "cc_library rule").
904 * Note: Even though there is partial duplication with the {@link RuleClass#name} field,
905 * we want to store this as a separate field instead of generating it on demand in order to
906 * avoid string duplication.
907 */
908 private final String targetKind;
909
910 private final boolean skylarkExecutable;
911 private final boolean documented;
912 private final boolean publicByDefault;
913 private final boolean binaryOutput;
914 private final boolean workspaceOnly;
915 private final boolean outputsDefaultExecutable;
916
917 /**
918 * A (unordered) mapping from attribute names to small integers indexing into
919 * the {@code attributes} array.
920 */
921 private final Map<String, Integer> attributeIndex = new HashMap<>();
922
923 /**
924 * All attributes of this rule class (including inherited ones) ordered by
925 * attributeIndex value.
926 */
Eric Fellheimera954fae2015-08-27 19:37:52 +0000927 private final ImmutableList<Attribute> attributes;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100928
929 /**
930 * The set of implicit outputs generated by a rule, expressed as a function
931 * of that rule.
932 */
933 private final ImplicitOutputsFunction implicitOutputsFunction;
934
935 /**
936 * The set of implicit outputs generated by a rule, expressed as a function
937 * of that rule.
938 */
939 private final Configurator<?, ?> configurator;
940
941 /**
942 * The factory that creates configured targets from this rule.
943 */
944 private final ConfiguredTargetFactory<?, ?> configuredTargetFactory;
945
946 /**
947 * The constraint the package name of the rule instance must fulfill
948 */
949 private final PredicateWithMessage<Rule> validityPredicate;
950
951 /**
952 * See {@link #isPreferredDependency}.
953 */
954 private final Predicate<String> preferredDependencyPredicate;
955
956 /**
957 * The list of transitive info providers this class advertises to aspects.
958 */
959 private final ImmutableSet<Class<?>> advertisedProviders;
960
961 /**
962 * The Skylark rule implementation of this RuleClass. Null for non Skylark executable RuleClasses.
963 */
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000964 @Nullable private final BaseFunction configuredTargetFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100965
966 /**
Lukacs Berki0e1a9942015-06-18 08:53:18 +0000967 * Returns the extra bindings a workspace function adds to the WORKSPACE file.
968 */
969 private final Function<? super Rule, Map<String, Label>> externalBindingsFunction;
970
971 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100972 * The Skylark rule definition environment of this RuleClass.
973 * Null for non Skylark executable RuleClasses.
974 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000975 @Nullable private final Environment ruleDefinitionEnvironment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100976
977 /**
978 * The set of required configuration fragments; this should list all fragments that can be
979 * accessed by the rule implementation. If empty, all fragments are allowed to be accessed for
980 * backwards compatibility.
981 */
982 private final ImmutableSet<Class<?>> requiredConfigurationFragments;
983
984 /**
Florian Weikert3f8aac92015-09-07 12:06:02 +0000985 * A dictionary that maps configurations (NONE for target configuration, HOST for host
986 * configuration) to lists of names of required configuration fragments.
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000987 */
Florian Weikert3f8aac92015-09-07 12:06:02 +0000988 private final ImmutableMap<ConfigurationTransition, ImmutableSet<String>>
989 requiredConfigurationFragmentNames;
990
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000991 /**
992 * Used to resolve the names of fragments in order to compare them to values in {@link
993 * #requiredConfigurationFragmentNames}
994 */
995 private final FragmentClassNameResolver fragmentNameResolver;
996
997 /**
Ulf Adams71423eb2015-08-06 11:51:17 +0000998 * What to do during analysis if a configuration fragment is missing.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100999 */
Ulf Adams71423eb2015-08-06 11:51:17 +00001000 private final MissingFragmentPolicy missingFragmentPolicy;
Greg Estrenc04c88f2015-03-06 19:45:50 +00001001
1002 /**
1003 * Determines whether instances of this rule should be checked for constraint compatibility
1004 * with their dependencies and the rules that depend on them. This should be true for
1005 * everything except for rules that are intrinsically incompatible with the constraint system.
1006 */
1007 private final boolean supportsConstraintChecking;
1008
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001009 /**
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001010 * Helper constructor that skips allowedConfigurationFragmentNames and fragmentNameResolver
1011 */
1012 @VisibleForTesting
1013 RuleClass(String name,
1014 boolean skylarkExecutable,
1015 boolean documented,
1016 boolean publicByDefault,
1017 boolean binaryOutput,
1018 boolean workspaceOnly,
1019 boolean outputsDefaultExecutable,
1020 ImplicitOutputsFunction implicitOutputsFunction,
1021 Configurator<?, ?> configurator,
1022 ConfiguredTargetFactory<?, ?> configuredTargetFactory,
1023 PredicateWithMessage<Rule> validityPredicate,
1024 Predicate<String> preferredDependencyPredicate,
1025 ImmutableSet<Class<?>> advertisedProviders,
1026 @Nullable BaseFunction configuredTargetFunction,
1027 Function<? super Rule, Map<String, Label>> externalBindingsFunction,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001028 @Nullable Environment ruleDefinitionEnvironment,
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001029 Set<Class<?>> allowedConfigurationFragments,
1030 MissingFragmentPolicy missingFragmentPolicy,
1031 boolean supportsConstraintChecking,
1032 Attribute... attributes) {
1033 this(name,
1034 skylarkExecutable,
1035 documented,
1036 publicByDefault,
1037 binaryOutput,
1038 workspaceOnly,
1039 outputsDefaultExecutable,
1040 implicitOutputsFunction,
1041 configurator,
1042 configuredTargetFactory,
1043 validityPredicate,
1044 preferredDependencyPredicate,
1045 advertisedProviders,
1046 configuredTargetFunction,
1047 externalBindingsFunction,
1048 ruleDefinitionEnvironment,
1049 allowedConfigurationFragments,
Florian Weikert3f8aac92015-09-07 12:06:02 +00001050 ImmutableMap.<ConfigurationTransition, ImmutableSet<String>>of(),
1051 null, // FragmentClassNameResolver
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001052 missingFragmentPolicy,
1053 supportsConstraintChecking,
1054 attributes);
1055 }
1056
1057 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001058 * Constructs an instance of RuleClass whose name is 'name', attributes
1059 * are 'attributes'. The {@code srcsAllowedFiles} determines which types of
1060 * files are allowed as parameters to the "srcs" attribute; rules are always
1061 * allowed. For the "deps" attribute, there are four cases:
1062 * <ul>
1063 * <li>if the parameter is a file, it is allowed if its file type is given
1064 * in {@code depsAllowedFiles},
1065 * <li>if the parameter is a rule and the rule class is accepted by
1066 * {@code depsAllowedRules}, then it is allowed,
1067 * <li>if the parameter is a rule and the rule class is not accepted by
1068 * {@code depsAllowedRules}, but accepted by
1069 * {@code depsAllowedRulesWithWarning}, then it is allowed, but
1070 * triggers a warning;
1071 * <li>all other parameters trigger an error.
1072 * </ul>
1073 *
1074 * <p>The {@code depsAllowedRules} predicate should have a {@code toString}
1075 * method which returns a plain English enumeration of the allowed rule class
1076 * names, if it does not allow all rule classes.
1077 * @param workspaceOnly
1078 */
1079 @VisibleForTesting
1080 RuleClass(String name,
1081 boolean skylarkExecutable, boolean documented, boolean publicByDefault,
1082 boolean binaryOutput, boolean workspaceOnly, boolean outputsDefaultExecutable,
1083 ImplicitOutputsFunction implicitOutputsFunction,
1084 Configurator<?, ?> configurator,
1085 ConfiguredTargetFactory<?, ?> configuredTargetFactory,
1086 PredicateWithMessage<Rule> validityPredicate, Predicate<String> preferredDependencyPredicate,
1087 ImmutableSet<Class<?>> advertisedProviders,
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +00001088 @Nullable BaseFunction configuredTargetFunction,
Lukacs Berki0e1a9942015-06-18 08:53:18 +00001089 Function<? super Rule, Map<String, Label>> externalBindingsFunction,
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001090 @Nullable Environment ruleDefinitionEnvironment,
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001091 Set<Class<?>> allowedConfigurationFragments,
Florian Weikert3f8aac92015-09-07 12:06:02 +00001092 ImmutableMap<ConfigurationTransition, ImmutableSet<String>> allowedConfigurationFragmentNames,
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001093 @Nullable FragmentClassNameResolver fragmentNameResolver,
1094 MissingFragmentPolicy missingFragmentPolicy,
Greg Estrenc04c88f2015-03-06 19:45:50 +00001095 boolean supportsConstraintChecking,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001096 Attribute... attributes) {
1097 this.name = name;
1098 this.targetKind = name + " rule";
1099 this.skylarkExecutable = skylarkExecutable;
1100 this.documented = documented;
1101 this.publicByDefault = publicByDefault;
1102 this.binaryOutput = binaryOutput;
1103 this.implicitOutputsFunction = implicitOutputsFunction;
1104 this.configurator = Preconditions.checkNotNull(configurator);
1105 this.configuredTargetFactory = configuredTargetFactory;
1106 this.validityPredicate = validityPredicate;
1107 this.preferredDependencyPredicate = preferredDependencyPredicate;
1108 this.advertisedProviders = advertisedProviders;
1109 this.configuredTargetFunction = configuredTargetFunction;
Lukacs Berki0e1a9942015-06-18 08:53:18 +00001110 this.externalBindingsFunction = externalBindingsFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001111 this.ruleDefinitionEnvironment = ruleDefinitionEnvironment;
Eric Fellheimera954fae2015-08-27 19:37:52 +00001112 this.attributes = ImmutableList.copyOf(attributes);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001113 this.workspaceOnly = workspaceOnly;
1114 this.outputsDefaultExecutable = outputsDefaultExecutable;
1115 this.requiredConfigurationFragments = ImmutableSet.copyOf(allowedConfigurationFragments);
Florian Weikert3f8aac92015-09-07 12:06:02 +00001116 this.requiredConfigurationFragmentNames = allowedConfigurationFragmentNames;
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001117 this.fragmentNameResolver = fragmentNameResolver;
Ulf Adams71423eb2015-08-06 11:51:17 +00001118 this.missingFragmentPolicy = missingFragmentPolicy;
Greg Estrenc04c88f2015-03-06 19:45:50 +00001119 this.supportsConstraintChecking = supportsConstraintChecking;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001120
1121 // create the index:
1122 int index = 0;
1123 for (Attribute attribute : attributes) {
1124 attributeIndex.put(attribute.getName(), index++);
1125 }
1126 }
1127
1128 /**
1129 * Returns the function which determines the set of implicit outputs
1130 * generated by a given rule.
1131 *
1132 * <p>An implicit output is an OutputFile that automatically comes into
1133 * existence when a rule of this class is declared, and whose name is derived
1134 * from the name of the rule.
1135 *
1136 * <p>Implicit outputs are a widely-relied upon. All ".so",
1137 * and "_deploy.jar" targets referenced in BUILD files are examples.
1138 */
1139 @VisibleForTesting
1140 public ImplicitOutputsFunction getImplicitOutputsFunction() {
1141 return implicitOutputsFunction;
1142 }
1143
1144 @SuppressWarnings("unchecked")
1145 public <C, R> Configurator<C, R> getConfigurator() {
1146 return (Configurator<C, R>) configurator;
1147 }
1148
1149 @SuppressWarnings("unchecked")
1150 public <CT, RC> ConfiguredTargetFactory<CT, RC> getConfiguredTargetFactory() {
1151 return (ConfiguredTargetFactory<CT, RC>) configuredTargetFactory;
1152 }
1153
1154 /**
1155 * Returns the class of rule that this RuleClass represents (e.g. "cc_library").
1156 */
1157 public String getName() {
1158 return name;
1159 }
1160
1161 /**
1162 * Returns the target kind of this class of rule (e.g. "cc_library rule").
1163 */
1164 String getTargetKind() {
1165 return targetKind;
1166 }
1167
1168 public boolean getWorkspaceOnly() {
1169 return workspaceOnly;
1170 }
1171
1172 /**
1173 * Returns true iff the attribute 'attrName' is defined for this rule class,
1174 * and has type 'type'.
1175 */
1176 public boolean hasAttr(String attrName, Type<?> type) {
1177 Integer index = getAttributeIndex(attrName);
1178 return index != null && getAttribute(index).getType() == type;
1179 }
1180
1181 /**
1182 * Returns the index of the specified attribute name. Use of indices allows
1183 * space-efficient storage of attribute values in rules, since hashtables are
1184 * not required. (The index mapping is specific to each RuleClass and an
1185 * attribute may have a different index in the parent RuleClass.)
1186 *
1187 * <p>Returns null if the named attribute is not defined for this class of Rule.
1188 */
1189 Integer getAttributeIndex(String attrName) {
1190 return attributeIndex.get(attrName);
1191 }
1192
1193 /**
1194 * Returns the attribute whose index is 'attrIndex'. Fails if attrIndex is
1195 * not in range.
1196 */
1197 Attribute getAttribute(int attrIndex) {
Eric Fellheimera954fae2015-08-27 19:37:52 +00001198 return attributes.get(attrIndex);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001199 }
1200
1201 /**
1202 * Returns the attribute whose name is 'attrName'; fails if not found.
1203 */
1204 public Attribute getAttributeByName(String attrName) {
Eric Fellheimera954fae2015-08-27 19:37:52 +00001205 return attributes.get(getAttributeIndex(attrName));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001206 }
1207
1208 /**
1209 * Returns the attribute whose name is {@code attrName}, or null if not
1210 * found.
1211 */
1212 Attribute getAttributeByNameMaybe(String attrName) {
1213 Integer i = getAttributeIndex(attrName);
Eric Fellheimera954fae2015-08-27 19:37:52 +00001214 return i == null ? null : attributes.get(i);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001215 }
1216
1217 /**
1218 * Returns the number of attributes defined for this rule class.
1219 */
1220 public int getAttributeCount() {
1221 return attributeIndex.size();
1222 }
1223
1224 /**
1225 * Returns an (immutable) list of all Attributes defined for this class of
1226 * rule, ordered by increasing index.
1227 */
1228 public List<Attribute> getAttributes() {
Eric Fellheimera954fae2015-08-27 19:37:52 +00001229 return attributes;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001230 }
1231
1232 public PredicateWithMessage<Rule> getValidityPredicate() {
1233 return validityPredicate;
1234 }
1235
1236 /**
1237 * Returns the set of advertised transitive info providers.
1238 *
1239 * <p>When computing the set of aspects required for a rule, only the providers listed here are
1240 * considered. The presence of a provider here does not mean that the rule <b>must</b> implement
1241 * said provider, merely that it <b>can</b>. After the configured target is constructed from this
1242 * rule, aspects will be filtered according to the set of actual providers.
1243 *
1244 * <p>This is here so that we can do the loading phase overestimation required for "blaze query",
1245 * which does not have the configured targets available.
1246 *
1247 * <p>This should in theory only contain subclasses of
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +00001248 * {@link com.google.devtools.build.lib.analysis.TransitiveInfoProvider}, but
1249 * our current dependency structure does not allow a reference to that class here.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001250 */
1251 public ImmutableSet<Class<?>> getAdvertisedProviders() {
1252 return advertisedProviders;
1253 }
1254
1255 /**
1256 * For --compile_one_dependency: if multiple rules consume the specified target,
1257 * should we choose this one over the "unpreferred" options?
1258 */
1259 public boolean isPreferredDependency(String filename) {
1260 return preferredDependencyPredicate.apply(filename);
1261 }
1262
1263 /**
1264 * The set of required configuration fragments; this contains all fragments that can be
1265 * accessed by the rule implementation. If empty, all fragments are allowed to be accessed for
1266 * backwards compatibility.
1267 */
1268 public Set<Class<?>> getRequiredConfigurationFragments() {
1269 return requiredConfigurationFragments;
1270 }
1271
1272 /**
Florian Weikert3f8aac92015-09-07 12:06:02 +00001273 * Checks if the configuration fragment may be accessed (i.e., if it's declared) in the specified
1274 * configuration (target or host).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001275 */
Florian Weikert3f8aac92015-09-07 12:06:02 +00001276 public boolean isLegalConfigurationFragment(
1277 Class<?> configurationFragment, ConfigurationTransition config) {
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001278 return requiredConfigurationFragments.contains(configurationFragment)
Florian Weikert3f8aac92015-09-07 12:06:02 +00001279 || hasLegalFragmentName(configurationFragment, config);
1280 }
1281
1282 public boolean isLegalConfigurationFragment(Class<?> configurationFragment) {
1283 // NONE means target configuration.
1284 return isLegalConfigurationFragment(configurationFragment, ConfigurationTransition.NONE);
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001285 }
1286
1287 /**
Florian Weikert3f8aac92015-09-07 12:06:02 +00001288 * Checks whether the name of the given fragment class was declared as required fragment in the
1289 * specified configuration (target or host).
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001290 */
Florian Weikert3f8aac92015-09-07 12:06:02 +00001291 private boolean hasLegalFragmentName(
1292 Class<?> configurationFragment, ConfigurationTransition config) {
Florian Weikert3f53fbb2015-08-07 22:25:50 +00001293 if (fragmentNameResolver == null) {
1294 return false;
1295 }
1296
1297 String name = fragmentNameResolver.resolveName(configurationFragment);
Googler21da7262015-09-19 00:29:50 +00001298 ImmutableSet<String> fragmentNames = requiredConfigurationFragmentNames.get(config);
1299 return (name != null && fragmentNames != null && fragmentNames.contains(name));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001300 }
1301
1302 /**
1303 * Whether to fail analysis if any of the required configuration fragments are missing.
1304 */
Ulf Adams71423eb2015-08-06 11:51:17 +00001305 public MissingFragmentPolicy missingFragmentPolicy() {
1306 return missingFragmentPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001307 }
1308
1309 /**
Greg Estrenc04c88f2015-03-06 19:45:50 +00001310 * Returns true if rules of this type can be used with the constraint enforcement system.
1311 */
1312 public boolean supportsConstraintChecking() {
1313 return supportsConstraintChecking;
1314 }
1315
1316 /**
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +00001317 * Helper function for {@link RuleFactory#createAndAddRule}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001318 */
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001319 Rule createRuleWithLabel(Package.Builder pkgBuilder, Label ruleLabel,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001320 Map<String, Object> attributeValues, EventHandler eventHandler, FuncallExpression ast,
Lukacs Berkia6434362015-09-15 13:56:14 +00001321 Location location) throws LabelSyntaxException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001322 Rule rule = pkgBuilder.newRuleWithLabel(ruleLabel, this, null, location);
1323 createRuleCommon(rule, pkgBuilder, attributeValues, eventHandler, ast);
1324 return rule;
1325 }
1326
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001327 private void createRuleCommon(Rule rule, Package.Builder pkgBuilder,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001328 Map<String, Object> attributeValues, EventHandler eventHandler, FuncallExpression ast)
Lukacs Berkia6434362015-09-15 13:56:14 +00001329 throws LabelSyntaxException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001330 populateRuleAttributeValues(
1331 rule, pkgBuilder, attributeValues, eventHandler, ast);
1332 rule.populateOutputFiles(eventHandler, pkgBuilder);
1333 rule.checkForNullLabels();
1334 rule.checkValidityPredicate(eventHandler);
1335 }
1336
1337 static class ParsedAttributeValue {
1338 private final boolean explicitlySpecified;
1339 private final Object value;
1340 private final Location location;
1341
1342 ParsedAttributeValue(boolean explicitlySpecified, Object value, Location location) {
1343 this.explicitlySpecified = explicitlySpecified;
1344 this.value = value;
1345 this.location = location;
1346 }
1347
1348 public boolean getExplicitlySpecified() {
1349 return explicitlySpecified;
1350 }
1351
1352 public Object getValue() {
1353 return value;
1354 }
1355
1356 public Location getLocation() {
1357 return location;
1358 }
1359 }
1360
1361 /**
1362 * Creates a rule with the attribute values that are already parsed.
1363 *
1364 * <p><b>WARNING:</b> This assumes that the attribute values here have the right type and
1365 * bypasses some sanity checks. If they are of the wrong type, everything will come down burning.
1366 */
1367 @SuppressWarnings("unchecked")
1368 Rule createRuleWithParsedAttributeValues(Label label,
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001369 Package.Builder pkgBuilder, Location ruleLocation,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001370 Map<String, ParsedAttributeValue> attributeValues, EventHandler eventHandler)
Lukacs Berkia6434362015-09-15 13:56:14 +00001371 throws LabelSyntaxException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001372 Rule rule = pkgBuilder.newRuleWithLabel(label, this, null, ruleLocation);
1373 rule.checkValidityPredicate(eventHandler);
1374
1375 for (Attribute attribute : rule.getRuleClassObject().getAttributes()) {
1376 ParsedAttributeValue value = attributeValues.get(attribute.getName());
1377 if (attribute.isMandatory()) {
1378 Preconditions.checkState(value != null);
1379 }
1380
1381 if (value == null) {
1382 continue;
1383 }
1384
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001385 rule.setAttributeValue(attribute, value.getValue(), value.getExplicitlySpecified());
1386 rule.setAttributeLocation(attribute, value.getLocation());
Michael Staibff938b32015-08-27 19:22:59 +00001387 checkAllowedValues(rule, attribute, eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001388
1389 if (attribute.getName().equals("visibility")) {
1390 // TODO(bazel-team): Verify that this cast works
1391 rule.setVisibility(PackageFactory.getVisibility((List<Label>) value.getValue()));
1392 }
1393 }
1394
1395 rule.populateOutputFiles(eventHandler, pkgBuilder);
1396 Preconditions.checkState(!rule.containsErrors());
1397 return rule;
1398 }
1399
1400 /**
1401 * Populates the attributes table of new rule "rule" from the
1402 * "attributeValues" mapping from attribute names to values in the build
1403 * language. Errors are reported on "reporter". "ast" is used to associate
1404 * location information with each rule attribute.
1405 */
1406 private void populateRuleAttributeValues(Rule rule,
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001407 Package.Builder pkgBuilder,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001408 Map<String, Object> attributeValues,
1409 EventHandler eventHandler,
Florian Weikert4b67d4f2015-09-14 13:35:34 +00001410 FuncallExpression ast)
1411 throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001412 BitSet definedAttrs = new BitSet(); // set of attr indices
1413
1414 for (Map.Entry<String, Object> entry : attributeValues.entrySet()) {
1415 String attributeName = entry.getKey();
1416 Object attributeValue = entry.getValue();
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +00001417 if (attributeValue == Runtime.NONE) { // Ignore all None values.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001418 continue;
1419 }
1420 Integer attrIndex = setRuleAttributeValue(rule, eventHandler, attributeName, attributeValue);
1421 if (attrIndex != null) {
1422 definedAttrs.set(attrIndex);
1423 checkAttrValNonEmpty(rule, eventHandler, attributeValue, attrIndex);
1424 }
1425 }
1426
1427 // Save the location of each non-default attribute definition:
1428 if (ast != null) {
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +00001429 for (Argument.Passed arg : ast.getArguments()) {
1430 if (arg.isKeyword()) {
1431 String name = arg.getName();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001432 Integer attrIndex = getAttributeIndex(name);
1433 if (attrIndex != null) {
1434 rule.setAttributeLocation(attrIndex, arg.getValue().getLocation());
1435 }
1436 }
1437 }
1438 }
1439
1440 List<Attribute> attrsWithComputedDefaults = new ArrayList<>();
1441
1442 // Set defaults; ensure that every mandatory attribute has a value. Use
1443 // the default if none is specified.
1444 int numAttributes = getAttributeCount();
1445 for (int attrIndex = 0; attrIndex < numAttributes; ++attrIndex) {
1446 if (!definedAttrs.get(attrIndex)) {
1447 Attribute attr = getAttribute(attrIndex);
1448 if (attr.isMandatory()) {
1449 rule.reportError(rule.getLabel() + ": missing value for mandatory "
1450 + "attribute '" + attr.getName() + "' in '"
1451 + name + "' rule", eventHandler);
1452 }
1453
1454 if (attr.hasComputedDefault()) {
1455 attrsWithComputedDefaults.add(attr);
1456 } else {
1457 Object defaultValue = getAttributeNoncomputedDefaultValue(attr, pkgBuilder);
1458 checkAttrValNonEmpty(rule, eventHandler, defaultValue, attrIndex);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001459 rule.setAttributeValue(attr, defaultValue, /*explicit=*/false);
Michael Staibff938b32015-08-27 19:22:59 +00001460 checkAllowedValues(rule, attr, eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001461 }
1462 }
1463 }
1464
1465 // Evaluate and set any computed defaults now that all non-computed
1466 // TODO(bazel-team): remove this special casing. Thanks to configurable attributes refactoring,
1467 // computed defaults don't get bound to their final values at this point, so we no longer
1468 // have to wait until regular attributes have been initialized.
1469 for (Attribute attr : attrsWithComputedDefaults) {
1470 rule.setAttributeValue(attr, attr.getDefaultValue(rule), /*explicit=*/false);
1471 }
1472
1473 // Now that all attributes are bound to values, collect and store configurable attribute keys.
1474 populateConfigDependenciesAttribute(rule);
1475 checkForDuplicateLabels(rule, eventHandler);
1476 checkThirdPartyRuleHasLicense(rule, pkgBuilder, eventHandler);
1477 checkForValidSizeAndTimeoutValues(rule, eventHandler);
1478 }
1479
1480 /**
1481 * Collects all labels used as keys for configurable attributes and places them into
1482 * the special implicit attribute that tracks them.
1483 */
1484 private static void populateConfigDependenciesAttribute(Rule rule) {
1485 RawAttributeMapper attributes = RawAttributeMapper.of(rule);
1486 Attribute configDepsAttribute = attributes.getAttributeDefinition("$config_dependencies");
1487 if (configDepsAttribute == null) {
1488 // Not currently compatible with Skylark rules.
1489 return;
1490 }
1491
1492 Set<Label> configLabels = new LinkedHashSet<>();
1493 for (Attribute attr : rule.getAttributes()) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001494 SelectorList<?> selectors = attributes.getSelectorList(attr.getName(), attr.getType());
Greg Estrena4fc8772015-04-13 19:58:23 +00001495 if (selectors != null) {
1496 configLabels.addAll(selectors.getKeyLabels());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001497 }
1498 }
1499
1500 rule.setAttributeValue(configDepsAttribute, ImmutableList.copyOf(configLabels),
1501 /*explicit=*/false);
1502 }
1503
1504 private void checkAttrValNonEmpty(
1505 Rule rule, EventHandler eventHandler, Object attributeValue, Integer attrIndex) {
1506 if (attributeValue instanceof List<?>) {
1507 Attribute attr = getAttribute(attrIndex);
1508 if (attr.isNonEmpty() && ((List<?>) attributeValue).isEmpty()) {
1509 rule.reportError(rule.getLabel() + ": non empty " + "attribute '" + attr.getName()
1510 + "' in '" + name + "' rule '" + rule.getLabel() + "' has to have at least one value",
1511 eventHandler);
1512 }
1513 }
1514 }
1515
1516 /**
1517 * Report an error for each label that appears more than once in a LABEL_LIST attribute
1518 * of the given rule.
1519 *
1520 * @param rule The rule.
1521 * @param eventHandler The eventHandler to use to report the duplicated deps.
1522 */
1523 private static void checkForDuplicateLabels(Rule rule, EventHandler eventHandler) {
1524 for (Attribute attribute : rule.getAttributes()) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001525 if (attribute.getType() == BuildType.LABEL_LIST) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001526 checkForDuplicateLabels(rule, attribute, eventHandler);
1527 }
1528 }
1529 }
1530
1531 /**
1532 * Reports an error against the specified rule if it's beneath third_party
1533 * but does not have a declared license.
1534 */
1535 private static void checkThirdPartyRuleHasLicense(Rule rule,
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001536 Package.Builder pkgBuilder, EventHandler eventHandler) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001537 if (rule.getLabel().getPackageName().startsWith("third_party/")) {
1538 License license = rule.getLicense();
1539 if (license == null) {
1540 license = pkgBuilder.getDefaultLicense();
1541 }
1542 if (license == License.NO_LICENSE) {
1543 rule.reportError("third-party rule '" + rule.getLabel() + "' lacks a license declaration "
1544 + "with one of the following types: notice, reciprocal, permissive, "
1545 + "restricted, unencumbered, by_exception_only",
1546 eventHandler);
1547 }
1548 }
1549 }
1550
1551 /**
1552 * Report an error for each label that appears more than once in the given attribute
1553 * of the given rule.
1554 *
1555 * @param rule The rule.
1556 * @param attribute The attribute to check. Must exist in rule and be of type LABEL_LIST.
1557 * @param eventHandler The eventHandler to use to report the duplicated deps.
1558 */
1559 private static void checkForDuplicateLabels(Rule rule, Attribute attribute,
1560 EventHandler eventHandler) {
Greg Estren14348c02015-04-16 16:26:19 +00001561 Set<Label> duplicates = AggregatingAttributeMapper.of(rule).checkForDuplicateLabels(attribute);
1562 for (Label label : duplicates) {
1563 rule.reportError(
1564 String.format("Label '%s' is duplicated in the '%s' attribute of rule '%s'",
1565 label, attribute.getName(), rule.getName()), eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001566 }
1567 }
1568
1569 /**
1570 * Report an error if the rule has a timeout or size attribute that is not a
1571 * legal value. These attributes appear on all tests.
1572 *
1573 * @param rule the rule to check
1574 * @param eventHandler the eventHandler to use to report the duplicated deps
1575 */
1576 private static void checkForValidSizeAndTimeoutValues(Rule rule, EventHandler eventHandler) {
1577 if (rule.getRuleClassObject().hasAttr("size", Type.STRING)) {
1578 String size = NonconfigurableAttributeMapper.of(rule).get("size", Type.STRING);
1579 if (TestSize.getTestSize(size) == null) {
1580 rule.reportError(
1581 String.format("In rule '%s', size '%s' is not a valid size.", rule.getName(), size),
1582 eventHandler);
1583 }
1584 }
1585 if (rule.getRuleClassObject().hasAttr("timeout", Type.STRING)) {
1586 String timeout = NonconfigurableAttributeMapper.of(rule).get("timeout", Type.STRING);
1587 if (TestTimeout.getTestTimeout(timeout) == null) {
1588 rule.reportError(
1589 String.format(
1590 "In rule '%s', timeout '%s' is not a valid timeout.", rule.getName(), timeout),
1591 eventHandler);
1592 }
1593 }
1594 }
1595
1596 /**
1597 * Returns the default value for the specified rule attribute.
1598 *
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +00001599 * <p>For most rule attributes, the default value is either explicitly specified
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001600 * in the attribute, or implicitly based on the type of the attribute, except
1601 * for some special cases (e.g. "licenses", "distribs") where it comes from
1602 * some other source, such as state in the package.
1603 *
Francois-Rene Rideau5dcdbf92015-02-19 18:36:17 +00001604 * <p>Precondition: {@code !attr.hasComputedDefault()}. (Computed defaults are
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001605 * evaluated in second pass.)
1606 */
1607 private static Object getAttributeNoncomputedDefaultValue(Attribute attr,
Han-Wen Nienhuys086e5f22015-04-21 11:39:59 +00001608 Package.Builder pkgBuilder) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001609 if (attr.getName().equals("licenses")) {
1610 return pkgBuilder.getDefaultLicense();
1611 }
1612 if (attr.getName().equals("distribs")) {
1613 return pkgBuilder.getDefaultDistribs();
1614 }
1615 return attr.getDefaultValue(null);
1616 }
1617
1618 /**
1619 * Sets the value of attribute "attrName" in rule "rule", by converting the
1620 * build-language value "attrVal" to the appropriate type for the attribute.
1621 * Returns the attribute index iff successful, null otherwise.
1622 *
1623 * <p>In case of failure, error messages are reported on "handler", and "rule"
1624 * is marked as containing errors.
1625 */
1626 @SuppressWarnings("unchecked")
1627 private Integer setRuleAttributeValue(Rule rule,
1628 EventHandler eventHandler,
1629 String attrName,
1630 Object attrVal) {
1631 if (attrName.equals("name")) {
1632 return null; // "name" is handled specially
1633 }
1634
1635 Integer attrIndex = getAttributeIndex(attrName);
1636 if (attrIndex == null) {
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +00001637 rule.reportError(rule.getLabel() + ": no such attribute '" + attrName
1638 + "' in '" + name + "' rule", eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001639 return null;
1640 }
1641
1642 Attribute attr = getAttribute(attrIndex);
1643 Object converted;
1644 try {
1645 String what = "attribute '" + attrName + "' in '" + name + "' rule";
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001646 converted = BuildType.selectableConvert(attr.getType(), attrVal, what, rule.getLabel());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001647
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001648 if ((converted instanceof SelectorList<?>) && !attr.isConfigurable()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001649 rule.reportError(rule.getLabel() + ": attribute \"" + attr.getName()
1650 + "\" is not configurable", eventHandler);
1651 return null;
1652 }
1653
1654 if ((converted instanceof List<?>) && !(converted instanceof GlobList<?>)) {
1655 if (attr.isOrderIndependent()) {
1656 converted = Ordering.natural().sortedCopy((List<? extends Comparable<?>>) converted);
1657 }
1658 converted = ImmutableList.copyOf((List<?>) converted);
1659 }
1660 } catch (Type.ConversionException e) {
1661 rule.reportError(rule.getLabel() + ": " + e.getMessage(), eventHandler);
1662 return null;
1663 }
1664
1665 if (attrName.equals("visibility")) {
1666 List<Label> attrList = (List<Label>) converted;
Francois-Rene Rideaucbebd632015-02-11 16:56:37 +00001667 if (!attrList.isEmpty()
1668 && ConstantRuleVisibility.LEGACY_PUBLIC_LABEL.equals(attrList.get(0))) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001669 rule.reportError(rule.getLabel() + ": //visibility:legacy_public only allowed in package "
1670 + "declaration", eventHandler);
1671 }
1672 rule.setVisibility(PackageFactory.getVisibility(attrList));
1673 }
1674
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001675 rule.setAttributeValue(attr, converted, /*explicit=*/true);
Michael Staibff938b32015-08-27 19:22:59 +00001676 checkAllowedValues(rule, attr, eventHandler);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001677 return attrIndex;
1678 }
1679
Michael Staibff938b32015-08-27 19:22:59 +00001680 /**
1681 * Verifies that the rule has a valid value for the attribute according to its allowed values.
1682 *
1683 * <p>If the value for the given attribute on the given rule is invalid, an error will be recorded
1684 * in the given EventHandler.
1685 *
1686 * <p>If the rule is configurable, all of its potential values are evaluated, and errors for each
1687 * of the invalid values are reported.
1688 */
1689 private void checkAllowedValues(Rule rule, Attribute attribute, EventHandler eventHandler) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001690 if (attribute.checkAllowedValues()) {
1691 PredicateWithMessage<Object> allowedValues = attribute.getAllowedValues();
Michael Staibff938b32015-08-27 19:22:59 +00001692 Iterable<?> values =
1693 AggregatingAttributeMapper.of(rule).visitAttribute(
1694 attribute.getName(), attribute.getType());
1695 for (Object value : values) {
1696 if (!allowedValues.apply(value)) {
1697 rule.reportError(String.format(rule.getLabel() + ": invalid value in '%s' attribute: %s",
1698 attribute.getName(),
1699 allowedValues.getErrorReason(value)), eventHandler);
1700 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001701 }
1702 }
1703 }
1704
1705 @Override
1706 public String toString() {
1707 return name;
1708 }
1709
1710 public boolean isDocumented() {
1711 return documented;
1712 }
1713
1714 public boolean isPublicByDefault() {
1715 return publicByDefault;
1716 }
1717
1718 /**
1719 * Returns true iff the outputs of this rule should be created beneath the
1720 * <i>bin</i> directory, false if beneath <i>genfiles</i>. For most rule
1721 * classes, this is a constant, but for genrule, it is a property of the
1722 * individual rule instance, derived from the 'output_to_bindir' attribute;
1723 * see Rule.hasBinaryOutput().
1724 */
Laurent Le Brunc2efe452015-04-08 16:27:21 +00001725 @VisibleForTesting
1726 public boolean hasBinaryOutput() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001727 return binaryOutput;
1728 }
1729
1730 /**
1731 * Returns this RuleClass's custom Skylark rule implementation.
1732 */
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +00001733 @Nullable public BaseFunction getConfiguredTargetFunction() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001734 return configuredTargetFunction;
1735 }
1736
1737 /**
Lukacs Berki0e1a9942015-06-18 08:53:18 +00001738 * Returns a function that computes the external bindings a repository function contributes to
1739 * the WORKSPACE file.
1740 */
1741 public Function<? super Rule, Map<String, Label>> getExternalBindingsFunction() {
1742 return externalBindingsFunction;
1743 }
1744
1745 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001746 * Returns this RuleClass's rule definition environment.
1747 */
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001748 @Nullable public Environment getRuleDefinitionEnvironment() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001749 return ruleDefinitionEnvironment;
1750 }
1751
1752 /**
1753 * Returns true if this RuleClass is an executable Skylark RuleClass (i.e. it is
1754 * Skylark and Normal or Test RuleClass).
1755 */
1756 public boolean isSkylarkExecutable() {
1757 return skylarkExecutable;
1758 }
1759
1760 /**
1761 * Returns true if this rule class outputs a default executable for every rule.
1762 */
1763 public boolean outputsDefaultExecutable() {
1764 return outputsDefaultExecutable;
1765 }
1766}