blob: 9fd4fd46660f1ef943b41249a98ba7998d089523 [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.analysis;
16
Yun Peng5c34e962016-02-22 15:13:19 +000017import com.google.common.base.Joiner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.base.Predicate;
Florian Weikert082c0542015-08-06 10:24:29 +000019import com.google.common.collect.ImmutableCollection;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.ImmutableList;
21import com.google.common.collect.ImmutableListMultimap;
22import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableSet;
Manuel Klimek6d9fb362015-04-30 12:50:55 +000024import com.google.common.collect.ImmutableSortedSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.common.collect.Iterables;
26import com.google.common.collect.ListMultimap;
27import com.google.common.collect.Multimaps;
28import com.google.common.collect.Sets;
29import com.google.devtools.build.lib.actions.Action;
30import com.google.devtools.build.lib.actions.ActionOwner;
31import com.google.devtools.build.lib.actions.ActionRegistry;
32import com.google.devtools.build.lib.actions.Artifact;
33import com.google.devtools.build.lib.actions.ArtifactOwner;
34import com.google.devtools.build.lib.actions.Root;
35import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.PrerequisiteValidator;
36import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
37import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
Ulf Adamsc272e0f2015-04-22 19:56:21 +000038import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
40import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
41import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
Florian Weikert3f8aac92015-09-07 12:06:02 +000042import com.google.devtools.build.lib.analysis.config.FragmentCollection;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000043import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010044import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap;
45import com.google.devtools.build.lib.collect.nestedset.NestedSet;
46import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
47import com.google.devtools.build.lib.events.Event;
48import com.google.devtools.build.lib.events.Location;
49import com.google.devtools.build.lib.packages.Attribute;
50import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
51import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
52import com.google.devtools.build.lib.packages.AttributeMap;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000053import com.google.devtools.build.lib.packages.BuildType;
Michael Staibb51251e2015-09-29 23:31:51 +000054import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import com.google.devtools.build.lib.packages.FileTarget;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000056import com.google.devtools.build.lib.packages.FilesetEntry;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
58import com.google.devtools.build.lib.packages.InputFile;
59import com.google.devtools.build.lib.packages.OutputFile;
60import com.google.devtools.build.lib.packages.PackageSpecification;
61import com.google.devtools.build.lib.packages.RawAttributeMapper;
62import com.google.devtools.build.lib.packages.Rule;
63import com.google.devtools.build.lib.packages.RuleClass;
64import com.google.devtools.build.lib.packages.RuleErrorConsumer;
65import com.google.devtools.build.lib.packages.Target;
66import com.google.devtools.build.lib.packages.TargetUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067import com.google.devtools.build.lib.rules.fileset.FilesetProvider;
68import com.google.devtools.build.lib.shell.ShellUtils;
69import com.google.devtools.build.lib.syntax.EvalException;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000070import com.google.devtools.build.lib.syntax.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071import com.google.devtools.build.lib.util.FileTypeSet;
Mark Schaller6df81792015-12-10 18:47:47 +000072import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import com.google.devtools.build.lib.vfs.FileSystemUtils;
74import com.google.devtools.build.lib.vfs.PathFragment;
75
76import java.util.ArrayList;
77import java.util.Collection;
78import java.util.HashMap;
79import java.util.HashSet;
80import java.util.List;
81import java.util.Map;
82import java.util.Set;
83
84import javax.annotation.Nullable;
85
86/**
87 * A helper class for rule implementations building and initialization. Objects of this
88 * class are intended to be passed to the builder for the configured target, which then creates the
89 * configured target.
90 */
91public final class RuleContext extends TargetContext
92 implements ActionConstructionContext, ActionRegistry, RuleErrorConsumer {
93
94 /**
95 * The configured version of FilesetEntry.
96 */
97 @Immutable
98 public static final class ConfiguredFilesetEntry {
99 private final FilesetEntry entry;
100 private final TransitiveInfoCollection src;
101 private final ImmutableList<TransitiveInfoCollection> files;
102
103 ConfiguredFilesetEntry(FilesetEntry entry, TransitiveInfoCollection src) {
104 this.entry = entry;
105 this.src = src;
106 this.files = null;
107 }
108
109 ConfiguredFilesetEntry(FilesetEntry entry, ImmutableList<TransitiveInfoCollection> files) {
110 this.entry = entry;
111 this.src = null;
112 this.files = files;
113 }
114
115 public FilesetEntry getEntry() {
116 return entry;
117 }
118
119 public TransitiveInfoCollection getSrc() {
120 return src;
121 }
122
123 /**
124 * Targets from FilesetEntry.files, or null if the user omitted it.
125 */
126 @Nullable
127 public List<TransitiveInfoCollection> getFiles() {
128 return files;
129 }
130 }
131
132 static final String HOST_CONFIGURATION_PROGRESS_TAG = "for host";
133
134 private final Rule rule;
135 private final ListMultimap<String, ConfiguredTarget> targetMap;
136 private final ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap;
137 private final Set<ConfigMatchingProvider> configConditions;
138 private final AttributeMap attributes;
139 private final ImmutableSet<String> features;
Michael Staib8824d5e2016-01-20 21:37:05 +0000140 private final String ruleClassNameForLogging;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000141 private final ImmutableMap<String, Attribute> aspectAttributes;
Greg Estren9eb1cf02015-06-26 22:18:35 +0000142 private final BuildConfiguration hostConfiguration;
Michael Staibb51251e2015-09-29 23:31:51 +0000143 private final ConfigurationFragmentPolicy configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +0000144 private final Class<? extends BuildConfiguration.Fragment> universalFragment;
Florian Weikertb8a6a942015-09-25 12:36:08 +0000145 private final ErrorReporter reporter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146
147 private ActionOwner actionOwner;
148
149 /* lazily computed cache for Make variables, computed from the above. See get... method */
150 private transient ConfigurationMakeVariableContext configurationMakeVariableContext = null;
151
Dmitry Lomovace678e2015-12-16 15:10:20 +0000152 private RuleContext(
153 Builder builder,
154 ListMultimap<String, ConfiguredTarget> targetMap,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap,
Greg Estrenc5a352f2015-11-13 17:25:36 +0000156 Set<ConfigMatchingProvider> configConditions,
157 Class<? extends BuildConfiguration.Fragment> universalFragment,
Michael Staib8824d5e2016-01-20 21:37:05 +0000158 String ruleClassNameForLogging,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000159 ImmutableMap<String, Attribute> aspectAttributes) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160 super(builder.env, builder.rule, builder.configuration, builder.prerequisiteMap.get(null),
161 builder.visibility);
162 this.rule = builder.rule;
Michael Staibb51251e2015-09-29 23:31:51 +0000163 this.configurationFragmentPolicy = builder.configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +0000164 this.universalFragment = universalFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100165 this.targetMap = targetMap;
166 this.filesetEntryMap = filesetEntryMap;
167 this.configConditions = configConditions;
168 this.attributes =
169 ConfiguredAttributeMapper.of(builder.rule, configConditions);
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000170 this.features = getEnabledFeatures();
Michael Staib8824d5e2016-01-20 21:37:05 +0000171 this.ruleClassNameForLogging = ruleClassNameForLogging;
Carmi Grushko06f65f72015-11-02 22:42:24 +0000172 this.aspectAttributes = aspectAttributes;
Greg Estren9eb1cf02015-06-26 22:18:35 +0000173 this.hostConfiguration = builder.hostConfiguration;
Florian Weikertb8a6a942015-09-25 12:36:08 +0000174 reporter = builder.reporter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 }
176
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000177 private ImmutableSet<String> getEnabledFeatures() {
178 Set<String> globallyEnabled = new HashSet<>();
179 Set<String> globallyDisabled = new HashSet<>();
180 parseFeatures(getConfiguration().getDefaultFeatures(), globallyEnabled, globallyDisabled);
Googler21da7262015-09-19 00:29:50 +0000181 for (ImmutableMap.Entry<Class<? extends Fragment>, Fragment> entry :
182 getConfiguration().getAllFragments().entrySet()) {
Michael Staibff66c192016-01-14 22:40:37 +0000183 if (isLegalFragment(entry.getKey())) {
Googler21da7262015-09-19 00:29:50 +0000184 globallyEnabled.addAll(entry.getValue().configurationEnabledFeatures(this));
185 }
186 }
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000187 Set<String> packageEnabled = new HashSet<>();
188 Set<String> packageDisabled = new HashSet<>();
189 parseFeatures(getRule().getPackage().getFeatures(), packageEnabled, packageDisabled);
190 Set<String> packageFeatures =
191 Sets.difference(Sets.union(globallyEnabled, packageEnabled), packageDisabled);
192 Set<String> ruleFeatures = packageFeatures;
193 if (attributes().has("features", Type.STRING_LIST)) {
194 Set<String> ruleEnabled = new HashSet<>();
195 Set<String> ruleDisabled = new HashSet<>();
196 parseFeatures(attributes().get("features", Type.STRING_LIST), ruleEnabled, ruleDisabled);
197 ruleFeatures = Sets.difference(Sets.union(packageFeatures, ruleEnabled), ruleDisabled);
198 }
199 return ImmutableSortedSet.copyOf(Sets.difference(ruleFeatures, globallyDisabled));
200 }
201
202 private void parseFeatures(Iterable<String> features, Set<String> enabled, Set<String> disabled) {
203 for (String feature : features) {
204 if (feature.startsWith("-")) {
205 disabled.add(feature.substring(1));
206 } else if (feature.equals("no_layering_check")) {
207 // TODO(bazel-team): Remove once we do not have BUILD files left that contain
208 // 'no_layering_check'.
209 disabled.add(feature.substring(3));
210 } else {
211 enabled.add(feature);
212 }
213 }
214 }
215
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100216 @Override
217 public Rule getRule() {
218 return rule;
219 }
220
221 /**
Michael Staib8824d5e2016-01-20 21:37:05 +0000222 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
223 */
224 public String getRuleClassNameForLogging() {
225 return ruleClassNameForLogging;
226 }
227
228 /**
Kristina Chodorow91876f02015-02-27 17:14:12 +0000229 * Returns the workspace name for the rule.
230 */
231 public String getWorkspaceName() {
232 return rule.getWorkspaceName();
233 }
234
235 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100236 * The configuration conditions that trigger this rule's configurable attributes.
237 */
238 Set<ConfigMatchingProvider> getConfigConditions() {
239 return configConditions;
240 }
241
242 /**
Lukacs Berki4313d372015-06-29 07:28:44 +0000243 * Returns the host configuration for this rule.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100244 */
245 public BuildConfiguration getHostConfiguration() {
Greg Estren9eb1cf02015-06-26 22:18:35 +0000246 return hostConfiguration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100247 }
248
249 /**
Dmitry Lomovace678e2015-12-16 15:10:20 +0000250 * Attributes from aspects.
251 */
252 public ImmutableMap<String, Attribute> getAspectAttributes() {
253 return aspectAttributes;
254 }
255
256 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100257 * Accessor for the Rule's attribute values.
258 */
259 public AttributeMap attributes() {
260 return attributes;
261 }
262
263 /**
264 * Returns whether this instance is known to have errors at this point during analysis. Do not
265 * call this method after the initializationHook has returned.
266 */
267 public boolean hasErrors() {
268 return getAnalysisEnvironment().hasErrors();
269 }
270
271 /**
272 * Returns an immutable map from attribute name to list of configured targets for that attribute.
273 */
274 public ListMultimap<String, ? extends TransitiveInfoCollection> getConfiguredTargetMap() {
275 return targetMap;
276 }
277
278 /**
279 * Returns an immutable map from attribute name to list of fileset entries.
280 */
281 public ListMultimap<String, ConfiguredFilesetEntry> getFilesetEntryMap() {
282 return filesetEntryMap;
283 }
284
285 @Override
286 public ActionOwner getActionOwner() {
287 if (actionOwner == null) {
288 actionOwner = new RuleActionOwner(rule, getConfiguration());
289 }
290 return actionOwner;
291 }
292
293 /**
294 * Returns a configuration fragment for this this target.
295 */
296 @Nullable
Florian Weikert3f8aac92015-09-07 12:06:02 +0000297 public <T extends Fragment> T getFragment(Class<T> fragment, ConfigurationTransition config) {
Florian Weikertd2a24612015-09-07 14:35:23 +0000298 return getFragment(fragment, fragment.getSimpleName(), "", config);
299 }
300
301 @Nullable
302 protected <T extends Fragment> T getFragment(Class<T> fragment, String name,
303 String additionalErrorMessage, ConfigurationTransition config) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100304 // TODO(bazel-team): The fragments can also be accessed directly through BuildConfiguration.
305 // Can we lock that down somehow?
Florian Weikert3f8aac92015-09-07 12:06:02 +0000306 Preconditions.checkArgument(isLegalFragment(fragment, config),
Florian Weikertd2a24612015-09-07 14:35:23 +0000307 "%s has to declare '%s' as a required fragment "
308 + "in %s configuration in order to access it.%s",
Michael Staib8824d5e2016-01-20 21:37:05 +0000309 getRuleClassNameForLogging(), name, FragmentCollection.getConfigurationName(config),
Florian Weikertd2a24612015-09-07 14:35:23 +0000310 additionalErrorMessage);
Florian Weikert1c2eeac2015-10-28 10:00:53 +0000311 return getConfiguration(config).getFragment(fragment);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100312 }
313
Florian Weikert082c0542015-08-06 10:24:29 +0000314 @Nullable
Florian Weikert3f8aac92015-09-07 12:06:02 +0000315 public <T extends Fragment> T getFragment(Class<T> fragment) {
316 // NONE means target configuration.
317 return getFragment(fragment, ConfigurationTransition.NONE);
Florian Weikert082c0542015-08-06 10:24:29 +0000318 }
319
Florian Weikert3f8aac92015-09-07 12:06:02 +0000320 @Nullable
321 public Fragment getSkylarkFragment(String name, ConfigurationTransition config) {
Florian Weikert1c2eeac2015-10-28 10:00:53 +0000322 Class<? extends Fragment> fragmentClass =
323 getConfiguration(config).getSkylarkFragmentByName(name);
Florian Weikertd2a24612015-09-07 14:35:23 +0000324 if (fragmentClass == null) {
325 return null;
326 }
327 return getFragment(fragmentClass, name,
328 String.format(
329 " Please update the '%1$sfragments' argument of the rule definition "
330 + "(for example: %1$sfragments = [\"%2$s\"])",
331 (config == ConfigurationTransition.HOST) ? "host_" : "", name),
332 config);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000333 }
334
335 public ImmutableCollection<String> getSkylarkFragmentNames(ConfigurationTransition config) {
336 return getConfiguration(config).getSkylarkFragmentNames();
337 }
338
339 public <T extends Fragment> boolean isLegalFragment(
340 Class<T> fragment, ConfigurationTransition config) {
Greg Estrenc5a352f2015-11-13 17:25:36 +0000341 return fragment == universalFragment
342 || configurationFragmentPolicy.isLegalConfigurationFragment(fragment, config);
Florian Weikert082c0542015-08-06 10:24:29 +0000343 }
344
Ulf Adamsea11fc52015-08-04 14:23:58 +0000345 public <T extends Fragment> boolean isLegalFragment(Class<T> fragment) {
Florian Weikert3f8aac92015-09-07 12:06:02 +0000346 // NONE means target configuration.
347 return isLegalFragment(fragment, ConfigurationTransition.NONE);
348 }
Florian Weikertd2a24612015-09-07 14:35:23 +0000349
Florian Weikert3f8aac92015-09-07 12:06:02 +0000350 protected BuildConfiguration getConfiguration(ConfigurationTransition config) {
351 return config.equals(ConfigurationTransition.HOST) ? hostConfiguration : getConfiguration();
Ulf Adamsea11fc52015-08-04 14:23:58 +0000352 }
353
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100354 @Override
355 public ArtifactOwner getOwner() {
356 return getAnalysisEnvironment().getOwner();
357 }
358
Ulf Adamsc272e0f2015-04-22 19:56:21 +0000359 public ImmutableList<Artifact> getBuildInfo(BuildInfoKey key) {
360 return getAnalysisEnvironment().getBuildInfo(this, key);
361 }
362
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100363 // TODO(bazel-team): This class could be simpler if Rule and BuildConfiguration classes
364 // were immutable. Then we would need to store only references those two.
365 @Immutable
366 private static final class RuleActionOwner implements ActionOwner {
367 private final Label label;
368 private final Location location;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100369 private final String mnemonic;
370 private final String targetKind;
Lukacs Berkicf492942015-06-29 07:48:59 +0000371 private final String configurationChecksum;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 private final boolean hostConfiguration;
373
374 private RuleActionOwner(Rule rule, BuildConfiguration configuration) {
375 this.label = rule.getLabel();
376 this.location = rule.getLocation();
377 this.targetKind = rule.getTargetKind();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100378 this.mnemonic = configuration.getMnemonic();
Lukacs Berkicf492942015-06-29 07:48:59 +0000379 this.configurationChecksum = configuration.checksum();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100380 this.hostConfiguration = configuration.isHostConfiguration();
381 }
382
383 @Override
384 public Location getLocation() {
385 return location;
386 }
387
388 @Override
389 public Label getLabel() {
390 return label;
391 }
392
393 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100394 public String getConfigurationMnemonic() {
395 return mnemonic;
396 }
397
398 @Override
Lukacs Berkicf492942015-06-29 07:48:59 +0000399 public String getConfigurationChecksum() {
400 return configurationChecksum;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100401 }
402
403 @Override
404 public String getTargetKind() {
405 return targetKind;
406 }
407
408 @Override
409 public String getAdditionalProgressInfo() {
410 return hostConfiguration ? HOST_CONFIGURATION_PROGRESS_TAG : null;
411 }
412 }
413
414 @Override
415 public void registerAction(Action... action) {
416 getAnalysisEnvironment().registerAction(action);
417 }
418
419 /**
420 * Convenience function for subclasses to report non-attribute-specific
421 * errors in the current rule.
422 */
423 @Override
424 public void ruleError(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000425 reporter.ruleError(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100426 }
427
428 /**
429 * Convenience function for subclasses to report non-attribute-specific
430 * warnings in the current rule.
431 */
432 @Override
433 public void ruleWarning(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000434 reporter.ruleWarning(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435 }
436
437 /**
438 * Convenience function for subclasses to report attribute-specific errors in
439 * the current rule.
440 *
441 * <p>If the name of the attribute starts with <code>$</code>
442 * it is replaced with a string <code>(an implicit dependency)</code>.
443 */
444 @Override
445 public void attributeError(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000446 reporter.attributeError(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100447 }
448
449 /**
450 * Like attributeError, but does not mark the configured target as errored.
451 *
452 * <p>If the name of the attribute starts with <code>$</code>
453 * it is replaced with a string <code>(an implicit dependency)</code>.
454 */
455 @Override
456 public void attributeWarning(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000457 reporter.attributeWarning(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100458 }
459
460 /**
461 * Returns an artifact beneath the root of either the "bin" or "genfiles"
462 * tree, whose path is based on the name of this target and the current
463 * configuration. The choice of which tree to use is based on the rule with
464 * which this target (which must be an OutputFile or a Rule) is associated.
465 */
466 public Artifact createOutputArtifact() {
467 return internalCreateOutputArtifact(getTarget());
468 }
469
470 /**
471 * Returns the output artifact of an {@link OutputFile} of this target.
472 *
473 * @see #createOutputArtifact()
474 */
475 public Artifact createOutputArtifact(OutputFile out) {
476 return internalCreateOutputArtifact(out);
477 }
478
479 /**
480 * Implementation for {@link #createOutputArtifact()} and
481 * {@link #createOutputArtifact(OutputFile)}. This is private so that
482 * {@link #createOutputArtifact(OutputFile)} can have a more specific
483 * signature.
484 */
485 private Artifact internalCreateOutputArtifact(Target target) {
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000486 Preconditions.checkState(
487 target.getLabel().getPackageIdentifier().equals(getLabel().getPackageIdentifier()),
488 "Creating output artifact for target '%s' in different package than the rule '%s' "
489 + "being analyzed", target.getLabel(), getLabel());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100490 Root root = getBinOrGenfilesDirectory();
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000491 return getPackageRelativeArtifact(target.getName(), root);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100492 }
493
494 /**
495 * Returns the root of either the "bin" or "genfiles"
496 * tree, based on this target and the current configuration.
497 * The choice of which tree to use is based on the rule with
498 * which this target (which must be an OutputFile or a Rule) is associated.
499 */
500 public Root getBinOrGenfilesDirectory() {
501 return rule.hasBinaryOutput()
502 ? getConfiguration().getBinDirectory()
503 : getConfiguration().getGenfilesDirectory();
504 }
505
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000506 /**
507 * Creates an artifact in a directory that is unique to the package that contains the rule,
508 * thus guaranteeing that it never clashes with artifacts created by rules in other packages.
509 */
510 public Artifact getPackageRelativeArtifact(String relative, Root root) {
511 return getPackageRelativeArtifact(new PathFragment(relative), root);
512 }
513
514 /**
Lukacs Berki21a04f22015-08-20 13:31:24 +0000515 * Returns an artifact that can be an output of shared actions. Only use when there is no other
516 * option.
517 *
518 * <p>This artifact can be created anywhere in the output tree, which, in addition to making
519 * sharing possible, opens up the possibility of action conflicts and makes it impossible to
520 * infer the label of the rule creating the artifact from the path of the artifact.
521 */
522 public Artifact getShareableArtifact(PathFragment rootRelativePath, Root root) {
523 return getAnalysisEnvironment().getDerivedArtifact(rootRelativePath, root);
524 }
525
526 /**
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000527 * Creates an artifact in a directory that is unique to the package that contains the rule,
528 * thus guaranteeing that it never clashes with artifacts created by rules in other packages.
529 */
530 public Artifact getPackageRelativeArtifact(PathFragment relative, Root root) {
531 return getDerivedArtifact(getPackageDirectory().getRelative(relative), root);
532 }
533
534 /**
535 * Returns the root-relative path fragment under which output artifacts of this rule should go.
536 *
537 * <p>Note that:
538 * <ul>
539 * <li>This doesn't guarantee that there are no clashes with rules in the same package.
540 * <li>If possible, {@link #getPackageRelativeArtifact(PathFragment, Root)} should be used
541 * instead of this method.
542 * </ul>
543 *
544 * Ideally, user-visible artifacts should all have corresponding output file targets, all others
545 * should go into a rule-specific directory.
546 * {@link #getUniqueDirectoryArtifact(String, PathFragment, Root)}) ensures that this is the case.
547 */
548 public PathFragment getPackageDirectory() {
549 return getLabel().getPackageIdentifier().getPathFragment();
550 }
551
552 /**
553 * Creates an artifact under a given root with the given root-relative path.
554 *
555 * <p>Verifies that it is in the root-relative directory corresponding to the package of the rule,
556 * thus ensuring that it doesn't clash with other artifacts generated by other rules using this
557 * method.
558 */
Ulf Adams3ab82f72015-09-04 12:10:53 +0000559 @Override
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000560 public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
561 Preconditions.checkState(rootRelativePath.startsWith(getPackageDirectory()),
562 "Output artifact '%s' not under package directory '%s' for target '%s'",
563 rootRelativePath, getPackageDirectory(), getLabel());
564 return getAnalysisEnvironment().getDerivedArtifact(rootRelativePath, root);
565 }
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000566
567 /**
568 * Creates a TreeArtifact under a given root with the given root-relative path.
569 *
570 * <p>Verifies that it is in the root-relative directory corresponding to the package of the rule,
571 * thus ensuring that it doesn't clash with other artifacts generated by other rules using this
572 * method.
573 */
574 public Artifact getTreeArtifact(PathFragment rootRelativePath, Root root) {
575 Preconditions.checkState(rootRelativePath.startsWith(getPackageDirectory()),
576 "Output artifact '%s' not under package directory '%s' for target '%s'",
577 rootRelativePath, getPackageDirectory(), getLabel());
578 return getAnalysisEnvironment().getTreeArtifact(rootRelativePath, root);
579 }
580
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000581 /**
582 * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
583 * clashes with artifacts created by other rules.
584 */
585 public Artifact getUniqueDirectoryArtifact(
586 String uniqueDirectory, String relative, Root root) {
587 return getUniqueDirectoryArtifact(uniqueDirectory, new PathFragment(relative), root);
588 }
589
Lukacs Berkid6e64242015-07-30 17:06:23 +0000590 /**
591 * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
592 * clashes with artifacts created by other rules.
593 */
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000594 public Artifact getUniqueDirectoryArtifact(
595 String uniqueDirectory, PathFragment relative, Root root) {
596 return getDerivedArtifact(getUniqueDirectory(uniqueDirectory).getRelative(relative), root);
597 }
598
Googler03083852015-12-06 18:31:53 +0000599 /**
600 * Returns the Attribute associated with this name, if it's a valid attribute for this rule,
601 * or is associated with an attached aspect. Otherwise returns null.
602 */
603 @Nullable
604 public Attribute getAttribute(String attributeName) {
Marian Loburca3cf532015-02-27 09:14:50 +0000605 Attribute result = getRule().getAttributeDefinition(attributeName);
606 if (result != null) {
607 return result;
608 }
Carmi Grushko06f65f72015-11-02 22:42:24 +0000609 return aspectAttributes.get(attributeName);
Marian Loburca3cf532015-02-27 09:14:50 +0000610 }
611
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100612 /**
613 * Returns the list of transitive info collections that feed into this target through the
614 * specified attribute. Note that you need to specify the correct mode for the attribute,
615 * otherwise an assertion will be raised.
616 */
617 public List<? extends TransitiveInfoCollection> getPrerequisites(String attributeName,
618 Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +0000619 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100620 if ((mode == Mode.TARGET)
621 && (attributeDefinition.getConfigurationTransition() instanceof SplitTransition)) {
622 // TODO(bazel-team): If you request a split-configured attribute in the target configuration,
623 // we return only the list of configured targets for the first architecture; this is for
624 // backwards compatibility with existing code in cases where the call to getPrerequisites is
625 // deeply nested and we can't easily inject the behavior we want. However, we should fix all
626 // such call sites.
627 checkAttribute(attributeName, Mode.SPLIT);
628 Map<String, ? extends List<? extends TransitiveInfoCollection>> map =
629 getSplitPrerequisites(attributeName, /*requireSplit=*/false);
630 return map.isEmpty()
631 ? ImmutableList.<TransitiveInfoCollection>of()
632 : map.entrySet().iterator().next().getValue();
633 }
634
635 checkAttribute(attributeName, mode);
636 return targetMap.get(attributeName);
637 }
638
639 /**
640 * Returns the a prerequisites keyed by the CPU of their configurations; this method throws an
641 * exception if the split transition is not active.
642 */
643 public Map<String, ? extends List<? extends TransitiveInfoCollection>>
644 getSplitPrerequisites(String attributeName) {
645 return getSplitPrerequisites(attributeName, /*requireSplit*/true);
646 }
647
648 private Map<String, ? extends List<? extends TransitiveInfoCollection>>
649 getSplitPrerequisites(String attributeName, boolean requireSplit) {
650 checkAttribute(attributeName, Mode.SPLIT);
651
Marian Loburca3cf532015-02-27 09:14:50 +0000652 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100653 SplitTransition<?> transition =
654 (SplitTransition<?>) attributeDefinition.getConfigurationTransition();
655 List<BuildConfiguration> configurations =
656 getConfiguration().getTransitions().getSplitConfigurations(transition);
657 if (configurations.size() == 1) {
658 // There are two cases here:
659 // 1. Splitting is enabled, but only one target cpu.
660 // 2. Splitting is disabled, and no --cpu value was provided on the command line.
661 // In the first case, the cpu value is non-null, but in the second case it is null. We only
662 // allow that to proceed if the caller specified that he is going to ignore the cpu value
663 // anyway.
664 String cpu = configurations.get(0).getCpu();
665 if (cpu == null) {
666 Preconditions.checkState(!requireSplit);
667 cpu = "DO_NOT_USE";
668 }
669 return ImmutableMap.of(cpu, targetMap.get(attributeName));
670 }
671
672 Set<String> cpus = new HashSet<>();
673 for (BuildConfiguration config : configurations) {
674 // This method should only be called when the split config is enabled on the command line, in
675 // which case this cpu can't be null.
676 Preconditions.checkNotNull(config.getCpu());
677 cpus.add(config.getCpu());
678 }
679
680 // Use an ImmutableListMultimap.Builder here to preserve ordering.
681 ImmutableListMultimap.Builder<String, TransitiveInfoCollection> result =
682 ImmutableListMultimap.builder();
683 for (TransitiveInfoCollection t : targetMap.get(attributeName)) {
684 if (t.getConfiguration() != null) {
685 result.put(t.getConfiguration().getCpu(), t);
686 } else {
687 // Source files don't have a configuration, so we add them to all architecture entries.
688 for (String cpu : cpus) {
689 result.put(cpu, t);
690 }
691 }
692 }
693 return Multimaps.asMap(result.build());
694 }
695
696 /**
697 * Returns the specified provider of the prerequisite referenced by the attribute in the
698 * argument. Note that you need to specify the correct mode for the attribute, otherwise an
699 * assertion will be raised. If the attribute is empty of it does not support the specified
700 * provider, returns null.
701 */
702 public <C extends TransitiveInfoProvider> C getPrerequisite(
703 String attributeName, Mode mode, Class<C> provider) {
704 TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
705 return prerequisite == null ? null : prerequisite.getProvider(provider);
706 }
707
708 /**
709 * Returns the transitive info collection that feeds into this target through the specified
710 * attribute. Note that you need to specify the correct mode for the attribute, otherwise an
711 * assertion will be raised. Returns null if the attribute is empty.
712 */
713 public TransitiveInfoCollection getPrerequisite(String attributeName, Mode mode) {
714 checkAttribute(attributeName, mode);
715 List<? extends TransitiveInfoCollection> elements = targetMap.get(attributeName);
716 if (elements.size() > 1) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000717 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
718 + " produces more than one prerequisite");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100719 }
720 return elements.isEmpty() ? null : elements.get(0);
721 }
722
723 /**
724 * Returns all the providers of the specified type that are listed under the specified attribute
725 * of this target in the BUILD file.
726 */
727 public <C extends TransitiveInfoProvider> Iterable<C> getPrerequisites(String attributeName,
728 Mode mode, final Class<C> classType) {
729 AnalysisUtils.checkProvider(classType);
730 return AnalysisUtils.getProviders(getPrerequisites(attributeName, mode), classType);
731 }
732
733 /**
734 * Returns all the providers of the specified type that are listed under the specified attribute
735 * of this target in the BUILD file, and that contain the specified provider.
736 */
737 public <C extends TransitiveInfoProvider> Iterable<? extends TransitiveInfoCollection>
738 getPrerequisitesIf(String attributeName, Mode mode, final Class<C> classType) {
739 AnalysisUtils.checkProvider(classType);
740 return AnalysisUtils.filterByProvider(getPrerequisites(attributeName, mode), classType);
741 }
742
743 /**
744 * Returns the prerequisite referred to by the specified attribute. Also checks whether
745 * the attribute is marked as executable and that the target referred to can actually be
746 * executed.
747 *
748 * <p>The {@code mode} argument must match the configuration transition specified in the
749 * definition of the attribute.
750 *
751 * @param attributeName the name of the attribute
752 * @param mode the configuration transition of the attribute
753 *
754 * @return the {@link FilesToRunProvider} interface of the prerequisite.
755 */
756 public FilesToRunProvider getExecutablePrerequisite(String attributeName, Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +0000757 Attribute ruleDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100758
759 if (ruleDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000760 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100761 + " is not defined");
762 }
763 if (!ruleDefinition.isExecutable()) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000764 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100765 + " is not configured to be executable");
766 }
767
768 TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
769 if (prerequisite == null) {
770 return null;
771 }
772
773 FilesToRunProvider result = prerequisite.getProvider(FilesToRunProvider.class);
774 if (result == null || result.getExecutable() == null) {
775 attributeError(
776 attributeName, prerequisite.getLabel() + " does not refer to a valid executable target");
777 }
778 return result;
779 }
780
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000781 /** Indicates whether a string list attribute should be tokenized. */
782 public enum Tokenize {
783 YES,
784 NO
785 }
786
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100787 /**
Ulf Adamse685ef32015-07-29 15:28:01 +0000788 * Gets an attribute of type STRING_LIST expanding Make variables, $(location) tags into the
789 * dependency location (see {@link LocationExpander} for details) and tokenizes the result.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100790 *
791 * @param attributeName the name of the attribute to process
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000792 * @return a list of strings containing the expanded and tokenized values for the attribute
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100793 */
794 public List<String> getTokenizedStringListAttr(String attributeName) {
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000795 return getExpandedStringListAttr(attributeName, Tokenize.YES);
796 }
797
798 /**
799 * Gets an attribute of type STRING_LIST expanding Make variables and $(location) tags,
800 * and optionally tokenizes the result.
801 *
802 * @param attributeName the name of the attribute to process
803 * @return a list of strings containing the processed values for the attribute
804 */
805 public List<String> getExpandedStringListAttr(String attributeName, Tokenize tokenize) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100806 if (!getRule().isAttrDefined(attributeName, Type.STRING_LIST)) {
807 // TODO(bazel-team): This should be an error.
808 return ImmutableList.of();
809 }
810 List<String> original = attributes().get(attributeName, Type.STRING_LIST);
811 if (original.isEmpty()) {
812 return ImmutableList.of();
813 }
814 List<String> tokens = new ArrayList<>();
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000815 LocationExpander locationExpander =
Ulf Adamse685ef32015-07-29 15:28:01 +0000816 new LocationExpander(this, LocationExpander.Options.ALLOW_DATA);
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000817
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100818 for (String token : original) {
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000819 expandValue(tokens, attributeName, token, locationExpander, tokenize);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100820 }
821 return ImmutableList.copyOf(tokens);
822 }
823
824 /**
825 * Expands make variables in value and tokenizes the result into tokens.
826 *
827 * <p>This methods should be called only during initialization.
828 */
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000829 public void tokenizeAndExpandMakeVars(List<String> tokens, String attributeName, String value) {
830 tokenizeAndExpandMakeVars(tokens, attributeName, value, null);
831 }
832
833 /**
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000834 * Expands make variables and $(location) tags in value and tokenizes the result into tokens.
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000835 *
836 * <p>This methods should be called only during initialization.
837 */
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000838 public void tokenizeAndExpandMakeVars(
839 List<String> tokens,
840 String attributeName,
841 String value,
842 @Nullable LocationExpander locationExpander) {
843 expandValue(tokens, attributeName, value, locationExpander, Tokenize.YES);
844 }
845
846 /**
847 * Expands make variables and $(location) tags in value, and optionally tokenizes the result.
848 *
849 * <p>This methods should be called only during initialization.
850 */
851 public void expandValue(
852 List<String> tokens,
853 String attributeName,
854 String value,
855 @Nullable LocationExpander locationExpander,
856 Tokenize tokenize) {
857 if (locationExpander != null) {
858 value = locationExpander.expandAttribute(attributeName, value);
859 }
860 value = expandMakeVariables(attributeName, value);
861 if (tokenize == Tokenize.YES) {
862 try {
863 ShellUtils.tokenize(tokens, value);
864 } catch (ShellUtils.TokenizationException e) {
865 attributeError(attributeName, e.getMessage());
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000866 }
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000867 } else {
868 tokens.add(value);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100869 }
870 }
871
872 /**
873 * Return a context that maps Make variable names (string) to values (string).
874 *
875 * @return a ConfigurationMakeVariableContext.
876 **/
877 public ConfigurationMakeVariableContext getConfigurationMakeVariableContext() {
878 if (configurationMakeVariableContext == null) {
879 configurationMakeVariableContext = new ConfigurationMakeVariableContext(
880 getRule().getPackage(), getConfiguration());
881 }
882 return configurationMakeVariableContext;
883 }
884
885 /**
886 * Returns the string "expression" after expanding all embedded references to
887 * "Make" variables. If any errors are encountered, they are reported, and
888 * "expression" is returned unchanged.
889 *
890 * @param attributeName the name of the attribute from which "expression" comes;
891 * used for error reporting.
892 * @param expression the string to expand.
893 * @return the expansion of "expression".
894 */
895 public String expandMakeVariables(String attributeName, String expression) {
896 return expandMakeVariables(attributeName, expression, getConfigurationMakeVariableContext());
897 }
898
899 /**
900 * Returns the string "expression" after expanding all embedded references to
901 * "Make" variables. If any errors are encountered, they are reported, and
902 * "expression" is returned unchanged.
903 *
904 * @param attributeName the name of the attribute from which "expression" comes;
905 * used for error reporting.
906 * @param expression the string to expand.
907 * @param context the ConfigurationMakeVariableContext which can have a customized
908 * lookupMakeVariable(String) method.
909 * @return the expansion of "expression".
910 */
911 public String expandMakeVariables(String attributeName, String expression,
912 ConfigurationMakeVariableContext context) {
913 try {
914 return MakeVariableExpander.expand(expression, context);
915 } catch (MakeVariableExpander.ExpansionException e) {
916 attributeError(attributeName, e.getMessage());
917 return expression;
918 }
919 }
920
921 /**
922 * Gets the value of the STRING_LIST attribute expanding all make variables.
923 */
924 public List<String> expandedMakeVariablesList(String attrName) {
925 List<String> variables = new ArrayList<>();
926 for (String variable : attributes().get(attrName, Type.STRING_LIST)) {
927 variables.add(expandMakeVariables(attrName, variable));
928 }
929 return variables;
930 }
931
932 /**
933 * If the string consists of a single variable, returns the expansion of
934 * that variable. Otherwise, returns null. Syntax errors are reported.
935 *
936 * @param attrName the name of the attribute from which "expression" comes;
937 * used for error reporting.
938 * @param expression the string to expand.
939 * @return the expansion of "expression", or null.
940 */
941 public String expandSingleMakeVariable(String attrName, String expression) {
942 try {
943 return MakeVariableExpander.expandSingleVariable(expression,
944 new ConfigurationMakeVariableContext(getRule().getPackage(), getConfiguration()));
945 } catch (MakeVariableExpander.ExpansionException e) {
946 attributeError(attrName, e.getMessage());
947 return expression;
948 }
949 }
950
951 private void checkAttribute(String attributeName, Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +0000952 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100953 if (attributeDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000954 throw new IllegalStateException(getRule().getLocation() + ": " + getRuleClassNameForLogging()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100955 + " attribute " + attributeName + " is not defined");
956 }
Lukacs Berkiffa73ad2015-09-18 11:40:12 +0000957 if (!(attributeDefinition.getType() == BuildType.LABEL
958 || attributeDefinition.getType() == BuildType.LABEL_LIST)) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000959 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100960 + " is not a label type attribute");
961 }
962 if (mode == Mode.HOST) {
963 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.HOST) {
964 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +0000965 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100966 + " is not configured for the host configuration");
967 }
968 } else if (mode == Mode.TARGET) {
969 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.NONE) {
970 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +0000971 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100972 + " is not configured for the target configuration");
973 }
974 } else if (mode == Mode.DATA) {
975 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.DATA) {
976 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +0000977 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100978 + " is not configured for the data configuration");
979 }
980 } else if (mode == Mode.SPLIT) {
981 if (!(attributeDefinition.getConfigurationTransition() instanceof SplitTransition)) {
982 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +0000983 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100984 + " is not configured for a split transition");
985 }
986 }
987 }
988
989 /**
990 * Returns the Mode for which the attribute is configured.
991 * This is intended for Skylark, where the Mode is implicitly chosen.
992 */
993 public Mode getAttributeMode(String attributeName) {
Marian Loburca3cf532015-02-27 09:14:50 +0000994 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100995 if (attributeDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000996 throw new IllegalStateException(getRule().getLocation() + ": " + getRuleClassNameForLogging()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100997 + " attribute " + attributeName + " is not defined");
998 }
Lukacs Berkiffa73ad2015-09-18 11:40:12 +0000999 if (!(attributeDefinition.getType() == BuildType.LABEL
1000 || attributeDefinition.getType() == BuildType.LABEL_LIST)) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001001 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001002 + " is not a label type attribute");
1003 }
1004 if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.HOST) {
1005 return Mode.HOST;
1006 } else if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.NONE) {
1007 return Mode.TARGET;
1008 } else if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.DATA) {
1009 return Mode.DATA;
1010 } else if (attributeDefinition.getConfigurationTransition() instanceof SplitTransition) {
1011 return Mode.SPLIT;
1012 }
1013 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001014 + getRuleClassNameForLogging() + " attribute " + attributeName + " is not configured");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001015 }
1016
1017 /**
1018 * For the specified attribute "attributeName" (which must be of type
1019 * list(label)), resolve all the labels into ConfiguredTargets (for the
1020 * configuration appropriate to the attribute) and return their build
1021 * artifacts as a {@link PrerequisiteArtifacts} instance.
1022 *
1023 * @param attributeName the name of the attribute to traverse
1024 */
1025 public PrerequisiteArtifacts getPrerequisiteArtifacts(String attributeName, Mode mode) {
1026 return PrerequisiteArtifacts.get(this, attributeName, mode);
1027 }
1028
1029 /**
1030 * For the specified attribute "attributeName" (which must be of type label),
1031 * resolves the ConfiguredTarget and returns its single build artifact.
1032 *
1033 * <p>If the attribute is optional, has no default and was not specified, then
1034 * null will be returned. Note also that null is returned (and an attribute
1035 * error is raised) if there wasn't exactly one build artifact for the target.
1036 */
1037 public Artifact getPrerequisiteArtifact(String attributeName, Mode mode) {
1038 TransitiveInfoCollection target = getPrerequisite(attributeName, mode);
1039 return transitiveInfoCollectionToArtifact(attributeName, target);
1040 }
1041
1042 /**
1043 * Equivalent to getPrerequisiteArtifact(), but also asserts that
1044 * host-configuration is appropriate for the specified attribute.
1045 */
1046 public Artifact getHostPrerequisiteArtifact(String attributeName) {
1047 TransitiveInfoCollection target = getPrerequisite(attributeName, Mode.HOST);
1048 return transitiveInfoCollectionToArtifact(attributeName, target);
1049 }
1050
1051 private Artifact transitiveInfoCollectionToArtifact(
1052 String attributeName, TransitiveInfoCollection target) {
1053 if (target != null) {
1054 Iterable<Artifact> artifacts = target.getProvider(FileProvider.class).getFilesToBuild();
1055 if (Iterables.size(artifacts) == 1) {
1056 return Iterables.getOnlyElement(artifacts);
1057 } else {
1058 attributeError(attributeName, target.getLabel() + " expected a single artifact");
1059 }
1060 }
1061 return null;
1062 }
1063
1064 /**
1065 * Returns the sole file in the "srcs" attribute. Reports an error and
1066 * (possibly) returns null if "srcs" does not identify a single file of the
1067 * expected type.
1068 */
1069 public Artifact getSingleSource(String fileTypeName) {
1070 List<Artifact> srcs = PrerequisiteArtifacts.get(this, "srcs", Mode.TARGET).list();
1071 switch (srcs.size()) {
1072 case 0 : // error already issued by getSrc()
1073 return null;
1074 case 1 : // ok
1075 return Iterables.getOnlyElement(srcs);
1076 default :
1077 attributeError("srcs", "only a single " + fileTypeName + " is allowed here");
1078 return srcs.get(0);
1079 }
1080 }
1081
1082 public Artifact getSingleSource() {
Michael Staib8824d5e2016-01-20 21:37:05 +00001083 return getSingleSource(getRuleClassNameForLogging() + " source file");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001084 }
1085
1086 /**
1087 * Returns a path fragment qualified by the rule name and unique fragment to
1088 * disambiguate artifacts produced from the source file appearing in
1089 * multiple rules.
1090 *
1091 * <p>For example "pkg/dir/name" -> "pkg/&lt;fragment>/rule/dir/name.
1092 */
1093 public final PathFragment getUniqueDirectory(String fragment) {
1094 return AnalysisUtils.getUniqueDirectory(getLabel(), new PathFragment(fragment));
1095 }
1096
1097 /**
1098 * Check that all targets that were specified as sources are from the same
1099 * package as this rule. Output a warning or an error for every target that is
1100 * imported from a different package.
1101 */
1102 public void checkSrcsSamePackage(boolean onlyWarn) {
1103 PathFragment packageName = getLabel().getPackageFragment();
1104 for (Artifact srcItem : PrerequisiteArtifacts.get(this, "srcs", Mode.TARGET).list()) {
1105 if (!srcItem.isSourceArtifact()) {
1106 // In theory, we should not do this check. However, in practice, we
1107 // have a couple of rules that do not obey the "srcs must contain
1108 // files and only files" rule. Thus, we are stuck with this hack here :(
1109 continue;
1110 }
1111 Label associatedLabel = srcItem.getOwner();
1112 PathFragment itemPackageName = associatedLabel.getPackageFragment();
1113 if (!itemPackageName.equals(packageName)) {
1114 String message = "please do not import '" + associatedLabel + "' directly. "
1115 + "You should either move the file to this package or depend on "
1116 + "an appropriate rule there";
1117 if (onlyWarn) {
1118 attributeWarning("srcs", message);
1119 } else {
1120 attributeError("srcs", message);
1121 }
1122 }
1123 }
1124 }
1125
1126
1127 /**
1128 * Returns the label to which the {@code NODEP_LABEL} attribute
1129 * {@code attrName} refers, checking that it is a valid label, and that it is
1130 * referring to a local target. Reports a warning otherwise.
1131 */
1132 public Label getLocalNodepLabelAttribute(String attrName) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001133 Label label = attributes().get(attrName, BuildType.NODEP_LABEL);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001134 if (label == null) {
1135 return null;
1136 }
1137
1138 if (!getTarget().getLabel().getPackageFragment().equals(label.getPackageFragment())) {
1139 attributeWarning(attrName, "does not reference a local rule");
1140 }
1141
1142 return label;
1143 }
1144
1145 /**
1146 * Returns the implicit output artifact for a given template function. If multiple or no artifacts
1147 * can be found as a result of the template, an exception is thrown.
1148 */
Florian Weikert4b67d4f2015-09-14 13:35:34 +00001149 public Artifact getImplicitOutputArtifact(ImplicitOutputsFunction function)
1150 throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001151 Iterable<String> result;
1152 try {
1153 result = function.getImplicitOutputs(RawAttributeMapper.of(rule));
1154 } catch (EvalException e) {
1155 // It's ok as long as we don't use this method from Skylark.
1156 throw new IllegalStateException(e);
1157 }
1158 return getImplicitOutputArtifact(Iterables.getOnlyElement(result));
1159 }
1160
1161 /**
1162 * Only use from Skylark. Returns the implicit output artifact for a given output path.
1163 */
1164 public Artifact getImplicitOutputArtifact(String path) {
Lukacs Berki4a89a9b2015-07-29 06:54:07 +00001165 return getPackageRelativeArtifact(path, getBinOrGenfilesDirectory());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001166 }
1167
1168 /**
1169 * Convenience method to return a host configured target for the "compiler"
1170 * attribute. Allows caller to decide whether a warning should be printed if
1171 * the "compiler" attribute is not set to the default value.
1172 *
1173 * @param warnIfNotDefault if true, print a warning if the value for the
1174 * "compiler" attribute is set to something other than the default
1175 * @return a ConfiguredTarget using the host configuration for the "compiler"
1176 * attribute
1177 */
1178 public final FilesToRunProvider getCompiler(boolean warnIfNotDefault) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001179 Label label = attributes().get("compiler", BuildType.LABEL);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001180 if (warnIfNotDefault && !label.equals(getRule().getAttrDefaultValue("compiler"))) {
1181 attributeWarning("compiler", "setting the compiler is strongly discouraged");
1182 }
1183 return getExecutablePrerequisite("compiler", Mode.HOST);
1184 }
1185
1186 /**
1187 * Returns the (unmodifiable, ordered) list of artifacts which are the outputs
1188 * of this target.
1189 *
1190 * <p>Each element in this list is associated with a single output, either
1191 * declared implicitly (via setImplicitOutputsFunction()) or explicitly
1192 * (listed in the 'outs' attribute of our rule).
1193 */
1194 public final ImmutableList<Artifact> getOutputArtifacts() {
1195 ImmutableList.Builder<Artifact> artifacts = ImmutableList.builder();
1196 for (OutputFile out : getRule().getOutputFiles()) {
1197 artifacts.add(createOutputArtifact(out));
1198 }
1199 return artifacts.build();
1200 }
1201
1202 /**
1203 * Like getFilesToBuild(), except that it also includes the runfiles middleman, if any.
1204 * Middlemen are expanded in the SpawnStrategy or by the Distributor.
1205 */
1206 public static ImmutableList<Artifact> getFilesToRun(
1207 RunfilesSupport runfilesSupport, NestedSet<Artifact> filesToBuild) {
1208 if (runfilesSupport == null) {
1209 return ImmutableList.copyOf(filesToBuild);
1210 } else {
1211 ImmutableList.Builder<Artifact> allFilesToBuild = ImmutableList.builder();
1212 allFilesToBuild.addAll(filesToBuild);
1213 allFilesToBuild.add(runfilesSupport.getRunfilesMiddleman());
1214 return allFilesToBuild.build();
1215 }
1216 }
1217
1218 /**
1219 * Like {@link #getOutputArtifacts()} but for a singular output item.
1220 * Reports an error if the "out" attribute is not a singleton.
1221 *
1222 * @return null if the output list is empty, the artifact for the first item
1223 * of the output list otherwise
1224 */
1225 public Artifact getOutputArtifact() {
1226 List<Artifact> outs = getOutputArtifacts();
1227 if (outs.size() != 1) {
1228 attributeError("out", "exactly one output file required");
1229 if (outs.isEmpty()) {
1230 return null;
1231 }
1232 }
1233 return outs.get(0);
1234 }
1235
1236 /**
1237 * Returns an artifact with a given file extension. All other path components
1238 * are the same as in {@code pathFragment}.
1239 */
1240 public final Artifact getRelatedArtifact(PathFragment pathFragment, String extension) {
1241 PathFragment file = FileSystemUtils.replaceExtension(pathFragment, extension);
Lukacs Berki4a89a9b2015-07-29 06:54:07 +00001242 return getDerivedArtifact(file, getConfiguration().getBinDirectory());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001243 }
1244
1245 /**
1246 * Returns true if runfiles support should create the runfiles tree, or
1247 * false if it should just create the manifest.
1248 */
1249 public boolean shouldCreateRunfilesSymlinks() {
1250 // TODO(bazel-team): Ideally we wouldn't need such logic, and we'd
1251 // always use the BuildConfiguration#buildRunfiles() to determine
1252 // whether to build the runfiles. The problem is that certain build
1253 // steps actually consume their runfiles. These include:
1254 // a. par files consumes the runfiles directory
1255 // We should modify autopar to take a list of files instead.
1256 // of the runfiles directory.
1257 // b. host tools could potentially use data files, but currently don't
1258 // (they're run from the execution root, not a runfiles tree).
1259 // Currently hostConfiguration.buildRunfiles() returns true.
1260 if (TargetUtils.isTestRule(getTarget())) {
1261 // Tests are only executed during testing (duh),
1262 // and their runfiles are generated lazily on local
1263 // execution (see LocalTestStrategy). Therefore, it
1264 // is safe not to build their runfiles.
1265 return getConfiguration().buildRunfiles();
1266 } else {
1267 return true;
1268 }
1269 }
1270
1271 /**
1272 * @return true if {@code rule} is visible from {@code prerequisite}.
1273 *
1274 * <p>This only computes the logic as implemented by the visibility system. The final decision
1275 * whether a dependency is allowed is made by
1276 * {@link ConfiguredRuleClassProvider.PrerequisiteValidator}.
1277 */
1278 public static boolean isVisible(Rule rule, TransitiveInfoCollection prerequisite) {
1279 // Check visibility attribute
1280 for (PackageSpecification specification :
1281 prerequisite.getProvider(VisibilityProvider.class).getVisibility()) {
Lukacs Berki485eb962016-01-13 10:47:29 +00001282 if (specification.containsPackage(rule.getLabel().getPackageIdentifier())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001283 return true;
1284 }
1285 }
1286
1287 return false;
1288 }
1289
1290 /**
1291 * @return the set of features applicable for the current rule's package.
1292 */
1293 public ImmutableSet<String> getFeatures() {
1294 return features;
1295 }
1296
Florian Weikertb8a6a942015-09-25 12:36:08 +00001297 @Override
1298 public String toString() {
1299 return "RuleContext(" + getLabel() + ", " + getConfiguration() + ")";
1300 }
1301
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001302 /**
1303 * Builder class for a RuleContext.
1304 */
Florian Weikertb8a6a942015-09-25 12:36:08 +00001305 public static final class Builder implements RuleErrorConsumer {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001306 private final AnalysisEnvironment env;
1307 private final Rule rule;
Michael Staibb51251e2015-09-29 23:31:51 +00001308 private final ConfigurationFragmentPolicy configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +00001309 private Class<? extends BuildConfiguration.Fragment> universalFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001310 private final BuildConfiguration configuration;
Greg Estren9eb1cf02015-06-26 22:18:35 +00001311 private final BuildConfiguration hostConfiguration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001312 private final PrerequisiteValidator prerequisiteValidator;
Michael Staib8824d5e2016-01-20 21:37:05 +00001313 @Nullable private final String aspectName;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001314 private final ErrorReporter reporter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001315 private ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap;
1316 private Set<ConfigMatchingProvider> configConditions;
1317 private NestedSet<PackageSpecification> visibility;
Dmitry Lomovace678e2015-12-16 15:10:20 +00001318 private ImmutableMap<String, Attribute> aspectAttributes;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001319
Michael Staib8824d5e2016-01-20 21:37:05 +00001320 Builder(
1321 AnalysisEnvironment env,
1322 Rule rule,
1323 @Nullable String aspectName,
1324 BuildConfiguration configuration,
Greg Estren9eb1cf02015-06-26 22:18:35 +00001325 BuildConfiguration hostConfiguration,
Michael Staib8824d5e2016-01-20 21:37:05 +00001326 PrerequisiteValidator prerequisiteValidator,
1327 ConfigurationFragmentPolicy configurationFragmentPolicy) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001328 this.env = Preconditions.checkNotNull(env);
1329 this.rule = Preconditions.checkNotNull(rule);
Michael Staib8824d5e2016-01-20 21:37:05 +00001330 this.aspectName = aspectName;
1331 this.configurationFragmentPolicy = Preconditions.checkNotNull(configurationFragmentPolicy);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001332 this.configuration = Preconditions.checkNotNull(configuration);
Greg Estren9eb1cf02015-06-26 22:18:35 +00001333 this.hostConfiguration = Preconditions.checkNotNull(hostConfiguration);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001334 this.prerequisiteValidator = prerequisiteValidator;
Michael Staib8824d5e2016-01-20 21:37:05 +00001335 reporter = new ErrorReporter(env, rule, getRuleClassNameForLogging());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001336 }
1337
1338 RuleContext build() {
1339 Preconditions.checkNotNull(prerequisiteMap);
1340 Preconditions.checkNotNull(configConditions);
1341 Preconditions.checkNotNull(visibility);
1342 ListMultimap<String, ConfiguredTarget> targetMap = createTargetMap();
1343 ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap =
1344 createFilesetEntryMap(rule, configConditions);
Michael Staib8824d5e2016-01-20 21:37:05 +00001345 return new RuleContext(
1346 this,
1347 targetMap,
1348 filesetEntryMap,
1349 configConditions,
1350 universalFragment,
1351 getRuleClassNameForLogging(),
Carmi Grushko06f65f72015-11-02 22:42:24 +00001352 aspectAttributes != null ? aspectAttributes : ImmutableMap.<String, Attribute>of());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001353 }
1354
1355 Builder setVisibility(NestedSet<PackageSpecification> visibility) {
1356 this.visibility = visibility;
1357 return this;
1358 }
1359
1360 /**
1361 * Sets the prerequisites and checks their visibility. It also generates appropriate error or
1362 * warning messages and sets the error flag as appropriate.
1363 */
1364 Builder setPrerequisites(ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap) {
1365 this.prerequisiteMap = Preconditions.checkNotNull(prerequisiteMap);
1366 return this;
1367 }
1368
1369 /**
Carmi Grushko06f65f72015-11-02 22:42:24 +00001370 * Adds attributes which are defined by an Aspect (and not by RuleClass).
1371 */
1372 Builder setAspectAttributes(Map<String, Attribute> aspectAttributes) {
Dmitry Lomovace678e2015-12-16 15:10:20 +00001373 this.aspectAttributes = ImmutableMap.copyOf(aspectAttributes);
Carmi Grushko06f65f72015-11-02 22:42:24 +00001374 return this;
1375 }
1376
1377 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001378 * Sets the configuration conditions needed to determine which paths to follow for this
1379 * rule's configurable attributes.
1380 */
1381 Builder setConfigConditions(Set<ConfigMatchingProvider> configConditions) {
1382 this.configConditions = Preconditions.checkNotNull(configConditions);
1383 return this;
1384 }
1385
Greg Estrenc5a352f2015-11-13 17:25:36 +00001386 /**
1387 * Sets the fragment that can be legally accessed even when not explicitly declared.
1388 */
1389 Builder setUniversalFragment(Class<? extends BuildConfiguration.Fragment> fragment) {
1390 // TODO(bazel-team): Add this directly to ConfigurationFragmentPolicy, so we
1391 // don't need separate logic specifically for checking this fragment. The challenge is
1392 // that we need RuleClassProvider to figure out what this fragment is, and not every
1393 // call state that creates ConfigurationFragmentPolicy has access to that.
1394 this.universalFragment = fragment;
1395 return this;
1396 }
1397
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001398 private boolean validateFilesetEntry(FilesetEntry filesetEntry, ConfiguredTarget src) {
1399 if (src.getProvider(FilesetProvider.class) != null) {
1400 return true;
1401 }
1402 if (filesetEntry.isSourceFileset()) {
1403 return true;
1404 }
1405
1406 Target srcTarget = src.getTarget();
1407 if (!(srcTarget instanceof FileTarget)) {
1408 attributeError("entries", String.format(
1409 "Invalid 'srcdir' target '%s'. Must be another Fileset or package",
1410 srcTarget.getLabel()));
1411 return false;
1412 }
1413
1414 if (srcTarget instanceof OutputFile) {
1415 attributeWarning("entries", String.format("'srcdir' target '%s' is not an input file. "
1416 + "This forces the Fileset to be executed unconditionally",
1417 srcTarget.getLabel()));
1418 }
1419
1420 return true;
1421 }
1422
1423 /**
1424 * Determines and returns a map from attribute name to list of configured fileset entries, based
1425 * on a PrerequisiteMap instance.
1426 */
1427 private ListMultimap<String, ConfiguredFilesetEntry> createFilesetEntryMap(
1428 final Rule rule, Set<ConfigMatchingProvider> configConditions) {
1429 final ImmutableSortedKeyListMultimap.Builder<String, ConfiguredFilesetEntry> mapBuilder =
1430 ImmutableSortedKeyListMultimap.builder();
1431 for (Attribute attr : rule.getAttributes()) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001432 if (attr.getType() != BuildType.FILESET_ENTRY_LIST) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001433 continue;
1434 }
1435 String attributeName = attr.getName();
1436 Map<Label, ConfiguredTarget> ctMap = new HashMap<>();
1437 for (ConfiguredTarget prerequisite : prerequisiteMap.get(attr)) {
1438 ctMap.put(prerequisite.getLabel(), prerequisite);
1439 }
1440 List<FilesetEntry> entries = ConfiguredAttributeMapper.of(rule, configConditions)
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001441 .get(attributeName, BuildType.FILESET_ENTRY_LIST);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001442 for (FilesetEntry entry : entries) {
1443 if (entry.getFiles() == null) {
1444 Label label = entry.getSrcLabel();
1445 ConfiguredTarget src = ctMap.get(label);
1446 if (!validateFilesetEntry(entry, src)) {
1447 continue;
1448 }
1449
1450 mapBuilder.put(attributeName, new ConfiguredFilesetEntry(entry, src));
1451 } else {
1452 ImmutableList.Builder<TransitiveInfoCollection> files = ImmutableList.builder();
1453 for (Label file : entry.getFiles()) {
1454 files.add(ctMap.get(file));
1455 }
1456 mapBuilder.put(attributeName, new ConfiguredFilesetEntry(entry, files.build()));
1457 }
1458 }
1459 }
1460 return mapBuilder.build();
1461 }
1462
1463 /**
1464 * Determines and returns a map from attribute name to list of configured targets.
1465 */
1466 private ImmutableSortedKeyListMultimap<String, ConfiguredTarget> createTargetMap() {
1467 ImmutableSortedKeyListMultimap.Builder<String, ConfiguredTarget> mapBuilder =
1468 ImmutableSortedKeyListMultimap.builder();
1469
1470 for (Map.Entry<Attribute, Collection<ConfiguredTarget>> entry :
1471 prerequisiteMap.asMap().entrySet()) {
1472 Attribute attribute = entry.getKey();
1473 if (attribute == null) {
1474 continue;
1475 }
1476 if (attribute.isSilentRuleClassFilter()) {
1477 Predicate<RuleClass> filter = attribute.getAllowedRuleClassesPredicate();
1478 for (ConfiguredTarget configuredTarget : entry.getValue()) {
1479 Target prerequisiteTarget = configuredTarget.getTarget();
1480 if ((prerequisiteTarget instanceof Rule)
1481 && filter.apply(((Rule) prerequisiteTarget).getRuleClassObject())) {
1482 validateDirectPrerequisite(attribute, configuredTarget);
1483 mapBuilder.put(attribute.getName(), configuredTarget);
1484 }
1485 }
1486 } else {
1487 for (ConfiguredTarget configuredTarget : entry.getValue()) {
1488 validateDirectPrerequisite(attribute, configuredTarget);
1489 mapBuilder.put(attribute.getName(), configuredTarget);
1490 }
1491 }
1492 }
1493
1494 // Handle abi_deps+deps error.
1495 Attribute abiDepsAttr = rule.getAttributeDefinition("abi_deps");
1496 if ((abiDepsAttr != null) && rule.isAttributeValueExplicitlySpecified("abi_deps")
1497 && rule.isAttributeValueExplicitlySpecified("deps")) {
1498 attributeError("deps", "Only one of deps and abi_deps should be provided");
1499 }
1500 return mapBuilder.build();
1501 }
1502
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001503 public void reportError(Location location, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001504 reporter.reportError(location, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001505 }
1506
Florian Weikertb8a6a942015-09-25 12:36:08 +00001507 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001508 public void ruleError(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001509 reporter.ruleError(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001510 }
1511
Florian Weikertb8a6a942015-09-25 12:36:08 +00001512 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001513 public void attributeError(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001514 reporter.attributeError(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001515 }
1516
1517 public void reportWarning(Location location, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001518 reporter.reportWarning(location, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001519 }
1520
Florian Weikertb8a6a942015-09-25 12:36:08 +00001521 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001522 public void ruleWarning(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001523 reporter.ruleWarning(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001524 }
1525
Florian Weikertb8a6a942015-09-25 12:36:08 +00001526 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001527 public void attributeWarning(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001528 reporter.attributeWarning(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001529 }
1530
1531 private void reportBadPrerequisite(Attribute attribute, String targetKind,
1532 Label prerequisiteLabel, String reason, boolean isWarning) {
1533 String msgPrefix = targetKind != null ? targetKind + " " : "";
1534 String msgReason = reason != null ? " (" + reason + ")" : "";
1535 if (isWarning) {
1536 attributeWarning(attribute.getName(), String.format(
1537 "%s'%s' is unexpected here%s; continuing anyway",
1538 msgPrefix, prerequisiteLabel, msgReason));
1539 } else {
1540 attributeError(attribute.getName(), String.format(
1541 "%s'%s' is misplaced here%s", msgPrefix, prerequisiteLabel, msgReason));
1542 }
1543 }
1544
1545 private void validateDirectPrerequisiteType(ConfiguredTarget prerequisite,
1546 Attribute attribute) {
1547 Target prerequisiteTarget = prerequisite.getTarget();
1548 Label prerequisiteLabel = prerequisiteTarget.getLabel();
1549
1550 if (prerequisiteTarget instanceof Rule) {
1551 Rule prerequisiteRule = (Rule) prerequisiteTarget;
1552
1553 String reason = attribute.getValidityPredicate().checkValid(rule, prerequisiteRule);
1554 if (reason != null) {
1555 reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(),
1556 prerequisiteLabel, reason, false);
1557 }
1558 }
1559
1560 if (attribute.isStrictLabelCheckingEnabled()) {
1561 if (prerequisiteTarget instanceof Rule) {
1562 RuleClass ruleClass = ((Rule) prerequisiteTarget).getRuleClassObject();
1563 if (!attribute.getAllowedRuleClassesPredicate().apply(ruleClass)) {
1564 boolean allowedWithWarning = attribute.getAllowedRuleClassesWarningPredicate()
1565 .apply(ruleClass);
1566 reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(), prerequisiteLabel,
Ulf Adams07dba942015-03-05 14:47:37 +00001567 "expected " + attribute.getAllowedRuleClassesPredicate(), allowedWithWarning);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001568 }
1569 } else if (prerequisiteTarget instanceof FileTarget) {
1570 if (!attribute.getAllowedFileTypesPredicate()
1571 .apply(((FileTarget) prerequisiteTarget).getFilename())) {
1572 if (prerequisiteTarget instanceof InputFile
1573 && !((InputFile) prerequisiteTarget).getPath().exists()) {
1574 // Misplaced labels, no corresponding target exists
1575 if (attribute.getAllowedFileTypesPredicate().isNone()
1576 && !((InputFile) prerequisiteTarget).getFilename().contains(".")) {
1577 // There are no allowed files in the attribute but it's not a valid rule,
1578 // and the filename doesn't contain a dot --> probably a misspelled rule
1579 attributeError(attribute.getName(),
1580 "rule '" + prerequisiteLabel + "' does not exist");
1581 } else {
1582 attributeError(attribute.getName(),
1583 "target '" + prerequisiteLabel + "' does not exist");
1584 }
1585 } else {
1586 // The file exists but has a bad extension
1587 reportBadPrerequisite(attribute, "file", prerequisiteLabel,
Ulf Adams07dba942015-03-05 14:47:37 +00001588 "expected " + attribute.getAllowedFileTypesPredicate(), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001589 }
1590 }
1591 }
1592 }
1593 }
1594
1595 public Rule getRule() {
1596 return rule;
1597 }
1598
Michael Staib8824d5e2016-01-20 21:37:05 +00001599 /**
1600 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
1601 */
1602 public String getRuleClassNameForLogging() {
1603 return aspectName != null
1604 ? aspectName + " aspect on " + rule.getRuleClass()
1605 : rule.getRuleClass();
1606 }
1607
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001608 public BuildConfiguration getConfiguration() {
1609 return configuration;
1610 }
1611
1612 /**
1613 * @return true if {@code rule} is visible from {@code prerequisite}.
1614 *
1615 * <p>This only computes the logic as implemented by the visibility system. The final decision
1616 * whether a dependency is allowed is made by
1617 * {@link ConfiguredRuleClassProvider.PrerequisiteValidator}, who is supposed to call this
1618 * method to determine whether a dependency is allowed as per visibility rules.
1619 */
1620 public boolean isVisible(TransitiveInfoCollection prerequisite) {
1621 return RuleContext.isVisible(rule, prerequisite);
1622 }
1623
1624 private void validateDirectPrerequisiteFileTypes(ConfiguredTarget prerequisite,
1625 Attribute attribute) {
1626 if (attribute.isSkipAnalysisTimeFileTypeCheck()) {
1627 return;
1628 }
1629 FileTypeSet allowedFileTypes = attribute.getAllowedFileTypesPredicate();
Ulf Adams788fd1a2015-03-12 13:54:09 +00001630 if (allowedFileTypes == null) {
1631 // It's not a label or label_list attribute.
1632 return;
1633 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001634 if (allowedFileTypes == FileTypeSet.ANY_FILE && !attribute.isNonEmpty()
1635 && !attribute.isSingleArtifact()) {
1636 return;
1637 }
1638
1639 // If we allow any file we still need to check if there are actually files generated
1640 // Note that this check only runs for ANY_FILE predicates if the attribute is NON_EMPTY
1641 // or SINGLE_ARTIFACT
1642 // If we performed this check when allowedFileTypes == NO_FILE this would
1643 // always throw an error in those cases
1644 if (allowedFileTypes != FileTypeSet.NO_FILE) {
1645 Iterable<Artifact> artifacts = prerequisite.getProvider(FileProvider.class)
1646 .getFilesToBuild();
1647 if (attribute.isSingleArtifact() && Iterables.size(artifacts) != 1) {
1648 attributeError(attribute.getName(),
1649 "'" + prerequisite.getLabel() + "' must produce a single file");
1650 return;
1651 }
1652 for (Artifact sourceArtifact : artifacts) {
1653 if (allowedFileTypes.apply(sourceArtifact.getFilename())) {
1654 return;
1655 }
1656 }
1657 attributeError(attribute.getName(), "'" + prerequisite.getLabel()
Michael Staib8824d5e2016-01-20 21:37:05 +00001658 + "' does not produce any " + getRuleClassNameForLogging() + " " + attribute.getName()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001659 + " files (expected " + allowedFileTypes + ")");
1660 }
1661 }
1662
1663 private void validateMandatoryProviders(ConfiguredTarget prerequisite, Attribute attribute) {
Yun Peng5c34e962016-02-22 15:13:19 +00001664 List<ImmutableSet<String>> mandatoryProvidersList = attribute.getMandatoryProvidersList();
1665 if (mandatoryProvidersList.isEmpty()) {
1666 return;
1667 }
1668 List<List<String>> missingProvidersList = new ArrayList<>();
1669 for (ImmutableSet<String> providers : mandatoryProvidersList) {
1670 List<String> missing = new ArrayList<>();
1671 for (String provider : providers) {
1672 if (prerequisite.get(provider) == null) {
1673 missing.add(provider);
1674 }
1675 }
1676 if (missing.isEmpty()) {
1677 return;
1678 } else {
1679 missingProvidersList.add(missing);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001680 }
1681 }
Yun Peng5c34e962016-02-22 15:13:19 +00001682 StringBuilder missingProviders = new StringBuilder();
1683 Joiner joinProvider = Joiner.on("', '");
1684 for (List<String> providers : missingProvidersList) {
1685 if (missingProviders.length() > 0) {
1686 missingProviders.append(" or ");
1687 }
1688 missingProviders.append((providers.size() > 1) ? "[" : "")
1689 .append("'");
1690 joinProvider.appendTo(missingProviders, providers);
1691 missingProviders.append("'")
1692 .append((providers.size() > 1) ? "]" : "");
1693 }
1694 attributeError(
1695 attribute.getName(),
1696 "'" + prerequisite.getLabel() + "' does not have mandatory provider "
1697 + missingProviders);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001698 }
1699
1700 private void validateDirectPrerequisite(Attribute attribute, ConfiguredTarget prerequisite) {
1701 validateDirectPrerequisiteType(prerequisite, attribute);
1702 validateDirectPrerequisiteFileTypes(prerequisite, attribute);
1703 validateMandatoryProviders(prerequisite, attribute);
Greg Estren875c7a72015-09-24 19:57:54 +00001704 if (attribute.performPrereqValidatorCheck()) {
Ulf Adams0b638972015-09-08 13:25:35 +00001705 prerequisiteValidator.validate(this, prerequisite, attribute);
1706 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001707 }
1708 }
1709
Florian Weikertb8a6a942015-09-25 12:36:08 +00001710 /**
1711 * Helper class for reporting errors and warnings.
1712 */
1713 public static final class ErrorReporter implements RuleErrorConsumer {
1714 private final AnalysisEnvironment env;
1715 private final Rule rule;
Michael Staib8824d5e2016-01-20 21:37:05 +00001716 private final String ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001717
Michael Staib8824d5e2016-01-20 21:37:05 +00001718 ErrorReporter(AnalysisEnvironment env, Rule rule, String ruleClassNameForLogging) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001719 this.env = env;
1720 this.rule = rule;
Michael Staib8824d5e2016-01-20 21:37:05 +00001721 this.ruleClassNameForLogging = ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001722 }
1723
1724 public void reportError(Location location, String message) {
1725 env.getEventHandler().handle(Event.error(location, message));
1726 }
1727
1728 @Override
1729 public void ruleError(String message) {
1730 reportError(rule.getLocation(), prefixRuleMessage(message));
1731 }
1732
1733 @Override
1734 public void attributeError(String attrName, String message) {
Florian Weikert5c679342015-11-04 16:53:59 +00001735 reportError(rule.getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
Florian Weikertb8a6a942015-09-25 12:36:08 +00001736 }
1737
1738 public void reportWarning(Location location, String message) {
1739 env.getEventHandler().handle(Event.warn(location, message));
1740 }
1741
1742 @Override
1743 public void ruleWarning(String message) {
1744 env.getEventHandler().handle(Event.warn(rule.getLocation(), prefixRuleMessage(message)));
1745 }
1746
1747 @Override
1748 public void attributeWarning(String attrName, String message) {
Florian Weikert5c679342015-11-04 16:53:59 +00001749 reportWarning(
1750 rule.getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
Florian Weikertb8a6a942015-09-25 12:36:08 +00001751 }
1752
1753 private String prefixRuleMessage(String message) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001754 return String.format(
1755 "in %s rule %s: %s", getRuleClassNameForLogging(), rule.getLabel(), message);
Florian Weikertb8a6a942015-09-25 12:36:08 +00001756 }
1757
1758 private String maskInternalAttributeNames(String name) {
1759 return Attribute.isImplicit(name) ? "(an implicit dependency)" : name;
1760 }
1761
1762 /**
1763 * Prefixes the given message with details about the rule and appends details about the macro
1764 * that created this rule, if applicable.
1765 */
1766 private String completeAttributeMessage(String attrName, String message) {
1767 // Appends a note to the given message if the offending rule was created by a macro.
1768 String macroMessageAppendix =
Florian Weikert5c679342015-11-04 16:53:59 +00001769 rule.wasCreatedByMacro()
Florian Weikertb8a6a942015-09-25 12:36:08 +00001770 ? String.format(
1771 ". Since this rule was created by the macro '%s', the error might have been "
1772 + "caused by the macro implementation in %s",
Florian Weikert5c679342015-11-04 16:53:59 +00001773 getGeneratorFunction(), rule.getAttributeLocationWithoutMacro(attrName))
Florian Weikertb8a6a942015-09-25 12:36:08 +00001774 : "";
1775
1776 return String.format("in %s attribute of %s rule %s: %s%s",
Michael Staib8824d5e2016-01-20 21:37:05 +00001777 maskInternalAttributeNames(attrName), getRuleClassNameForLogging(), rule.getLabel(),
1778 message, macroMessageAppendix);
1779 }
1780
1781 /**
1782 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
1783 */
1784 private String getRuleClassNameForLogging() {
1785 return ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001786 }
1787
Florian Weikertb8a6a942015-09-25 12:36:08 +00001788 private String getGeneratorFunction() {
1789 return (String) rule.getAttributeContainer().getAttr("generator_function");
1790 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001791 }
1792}