blob: 9c0c47426d47bbf8223363d1bae16994e6e4467d [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
Janak Ramakrishnan7857c792016-03-21 08:35:35 +000017import com.google.common.annotations.VisibleForTesting;
Yun Peng5c34e962016-02-22 15:13:19 +000018import com.google.common.base.Joiner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010019import com.google.common.base.Predicate;
Yun Pengefd7ca12016-03-03 13:14:38 +000020import com.google.common.base.Predicates;
Cal Peyserf296e872016-05-03 17:36:54 +000021import com.google.common.collect.ImmutableBiMap;
Florian Weikert082c0542015-08-06 10:24:29 +000022import com.google.common.collect.ImmutableCollection;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.common.collect.ImmutableList;
24import com.google.common.collect.ImmutableListMultimap;
25import com.google.common.collect.ImmutableMap;
26import com.google.common.collect.ImmutableSet;
Manuel Klimek6d9fb362015-04-30 12:50:55 +000027import com.google.common.collect.ImmutableSortedSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.common.collect.Iterables;
29import com.google.common.collect.ListMultimap;
30import com.google.common.collect.Multimaps;
31import com.google.common.collect.Sets;
32import com.google.devtools.build.lib.actions.Action;
Rumou Duan33bab462016-04-25 17:55:12 +000033import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import com.google.devtools.build.lib.actions.ActionOwner;
35import com.google.devtools.build.lib.actions.ActionRegistry;
36import com.google.devtools.build.lib.actions.Artifact;
37import com.google.devtools.build.lib.actions.ArtifactOwner;
38import com.google.devtools.build.lib.actions.Root;
39import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider.PrerequisiteValidator;
40import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
41import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
Ulf Adamsc272e0f2015-04-22 19:56:21 +000042import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
44import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
45import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
Florian Weikert3f8aac92015-09-07 12:06:02 +000046import com.google.devtools.build.lib.analysis.config.FragmentCollection;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000047import com.google.devtools.build.lib.cmdline.Label;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap;
49import com.google.devtools.build.lib.collect.nestedset.NestedSet;
50import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
51import com.google.devtools.build.lib.events.Event;
52import com.google.devtools.build.lib.events.Location;
53import com.google.devtools.build.lib.packages.Attribute;
54import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
55import com.google.devtools.build.lib.packages.Attribute.SplitTransition;
56import com.google.devtools.build.lib.packages.AttributeMap;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000057import com.google.devtools.build.lib.packages.BuildType;
Michael Staibb51251e2015-09-29 23:31:51 +000058import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010059import com.google.devtools.build.lib.packages.FileTarget;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000060import com.google.devtools.build.lib.packages.FilesetEntry;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010061import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
62import com.google.devtools.build.lib.packages.InputFile;
63import com.google.devtools.build.lib.packages.OutputFile;
64import com.google.devtools.build.lib.packages.PackageSpecification;
65import com.google.devtools.build.lib.packages.RawAttributeMapper;
66import com.google.devtools.build.lib.packages.Rule;
67import com.google.devtools.build.lib.packages.RuleClass;
Chris Parsons4dfb22c2016-05-23 17:39:42 +000068import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import com.google.devtools.build.lib.packages.RuleErrorConsumer;
70import com.google.devtools.build.lib.packages.Target;
71import com.google.devtools.build.lib.packages.TargetUtils;
Lukacs Berki7894c182016-05-10 12:07:01 +000072import com.google.devtools.build.lib.rules.AliasProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import com.google.devtools.build.lib.rules.fileset.FilesetProvider;
74import com.google.devtools.build.lib.shell.ShellUtils;
75import com.google.devtools.build.lib.syntax.EvalException;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000076import com.google.devtools.build.lib.syntax.Type;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010077import com.google.devtools.build.lib.util.FileTypeSet;
Mark Schaller6df81792015-12-10 18:47:47 +000078import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010079import com.google.devtools.build.lib.vfs.FileSystemUtils;
80import com.google.devtools.build.lib.vfs.PathFragment;
81
82import java.util.ArrayList;
83import java.util.Collection;
84import java.util.HashMap;
85import java.util.HashSet;
86import java.util.List;
87import java.util.Map;
88import java.util.Set;
89
90import javax.annotation.Nullable;
91
92/**
Lukacs Berki2300cd62016-05-19 11:06:37 +000093 * The totality of data available during the analysis of a rule.
Janak Ramakrishnan81c5bd82016-03-22 20:07:43 +000094 *
95 * <p>These objects should not outlast the analysis phase. Do not pass them to {@link Action}
96 * objects or other persistent objects. There are internal tests to ensure that RuleContext objects
97 * are not persisted that check the name of this class, so update those tests if you change this
98 * class's name.
Lukacs Berki2300cd62016-05-19 11:06:37 +000099 *
100 * @see com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 */
102public final class RuleContext extends TargetContext
103 implements ActionConstructionContext, ActionRegistry, RuleErrorConsumer {
104
105 /**
106 * The configured version of FilesetEntry.
107 */
108 @Immutable
109 public static final class ConfiguredFilesetEntry {
110 private final FilesetEntry entry;
111 private final TransitiveInfoCollection src;
112 private final ImmutableList<TransitiveInfoCollection> files;
113
114 ConfiguredFilesetEntry(FilesetEntry entry, TransitiveInfoCollection src) {
115 this.entry = entry;
116 this.src = src;
117 this.files = null;
118 }
119
120 ConfiguredFilesetEntry(FilesetEntry entry, ImmutableList<TransitiveInfoCollection> files) {
121 this.entry = entry;
122 this.src = null;
123 this.files = files;
124 }
125
126 public FilesetEntry getEntry() {
127 return entry;
128 }
129
130 public TransitiveInfoCollection getSrc() {
131 return src;
132 }
133
134 /**
135 * Targets from FilesetEntry.files, or null if the user omitted it.
136 */
137 @Nullable
Ulf Adams10993fe2016-04-19 12:55:12 +0000138 public ImmutableList<TransitiveInfoCollection> getFiles() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100139 return files;
140 }
141 }
142
Janak Ramakrishnan7857c792016-03-21 08:35:35 +0000143 private static final String HOST_CONFIGURATION_PROGRESS_TAG = "for host";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144
145 private final Rule rule;
146 private final ListMultimap<String, ConfiguredTarget> targetMap;
147 private final ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap;
Lukacs Berki7894c182016-05-10 12:07:01 +0000148 private final ImmutableMap<Label, ConfigMatchingProvider> configConditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149 private final AttributeMap attributes;
150 private final ImmutableSet<String> features;
Michael Staib8824d5e2016-01-20 21:37:05 +0000151 private final String ruleClassNameForLogging;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000152 private final ImmutableMap<String, Attribute> aspectAttributes;
Greg Estren9eb1cf02015-06-26 22:18:35 +0000153 private final BuildConfiguration hostConfiguration;
Michael Staibb51251e2015-09-29 23:31:51 +0000154 private final ConfigurationFragmentPolicy configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +0000155 private final Class<? extends BuildConfiguration.Fragment> universalFragment;
Florian Weikertb8a6a942015-09-25 12:36:08 +0000156 private final ErrorReporter reporter;
Cal Peyserf296e872016-05-03 17:36:54 +0000157 private final ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>>
158 skylarkProviderRegistry;
159
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160 private ActionOwner actionOwner;
161
162 /* lazily computed cache for Make variables, computed from the above. See get... method */
163 private transient ConfigurationMakeVariableContext configurationMakeVariableContext = null;
164
Dmitry Lomovace678e2015-12-16 15:10:20 +0000165 private RuleContext(
166 Builder builder,
Nathan Harmatafcb17112016-04-13 16:56:58 +0000167 AttributeMap attributes,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000168 ListMultimap<String, ConfiguredTarget> targetMap,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap,
Lukacs Berki7894c182016-05-10 12:07:01 +0000170 ImmutableMap<Label, ConfigMatchingProvider> configConditions,
Greg Estrenc5a352f2015-11-13 17:25:36 +0000171 Class<? extends BuildConfiguration.Fragment> universalFragment,
Michael Staib8824d5e2016-01-20 21:37:05 +0000172 String ruleClassNameForLogging,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000173 ImmutableMap<String, Attribute> aspectAttributes) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100174 super(builder.env, builder.rule, builder.configuration, builder.prerequisiteMap.get(null),
175 builder.visibility);
176 this.rule = builder.rule;
Michael Staibb51251e2015-09-29 23:31:51 +0000177 this.configurationFragmentPolicy = builder.configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +0000178 this.universalFragment = universalFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100179 this.targetMap = targetMap;
180 this.filesetEntryMap = filesetEntryMap;
181 this.configConditions = configConditions;
Nathan Harmatafcb17112016-04-13 16:56:58 +0000182 this.attributes = attributes;
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000183 this.features = getEnabledFeatures();
Michael Staib8824d5e2016-01-20 21:37:05 +0000184 this.ruleClassNameForLogging = ruleClassNameForLogging;
Carmi Grushko06f65f72015-11-02 22:42:24 +0000185 this.aspectAttributes = aspectAttributes;
Cal Peyserf296e872016-05-03 17:36:54 +0000186 this.skylarkProviderRegistry = builder.skylarkProviderRegistry;
Greg Estren9eb1cf02015-06-26 22:18:35 +0000187 this.hostConfiguration = builder.hostConfiguration;
Florian Weikertb8a6a942015-09-25 12:36:08 +0000188 reporter = builder.reporter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100189 }
190
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000191 private ImmutableSet<String> getEnabledFeatures() {
192 Set<String> globallyEnabled = new HashSet<>();
193 Set<String> globallyDisabled = new HashSet<>();
194 parseFeatures(getConfiguration().getDefaultFeatures(), globallyEnabled, globallyDisabled);
Googler21da7262015-09-19 00:29:50 +0000195 for (ImmutableMap.Entry<Class<? extends Fragment>, Fragment> entry :
196 getConfiguration().getAllFragments().entrySet()) {
Michael Staibff66c192016-01-14 22:40:37 +0000197 if (isLegalFragment(entry.getKey())) {
Googler21da7262015-09-19 00:29:50 +0000198 globallyEnabled.addAll(entry.getValue().configurationEnabledFeatures(this));
199 }
200 }
Manuel Klimek6d9fb362015-04-30 12:50:55 +0000201 Set<String> packageEnabled = new HashSet<>();
202 Set<String> packageDisabled = new HashSet<>();
203 parseFeatures(getRule().getPackage().getFeatures(), packageEnabled, packageDisabled);
204 Set<String> packageFeatures =
205 Sets.difference(Sets.union(globallyEnabled, packageEnabled), packageDisabled);
206 Set<String> ruleFeatures = packageFeatures;
207 if (attributes().has("features", Type.STRING_LIST)) {
208 Set<String> ruleEnabled = new HashSet<>();
209 Set<String> ruleDisabled = new HashSet<>();
210 parseFeatures(attributes().get("features", Type.STRING_LIST), ruleEnabled, ruleDisabled);
211 ruleFeatures = Sets.difference(Sets.union(packageFeatures, ruleEnabled), ruleDisabled);
212 }
213 return ImmutableSortedSet.copyOf(Sets.difference(ruleFeatures, globallyDisabled));
214 }
215
216 private void parseFeatures(Iterable<String> features, Set<String> enabled, Set<String> disabled) {
217 for (String feature : features) {
218 if (feature.startsWith("-")) {
219 disabled.add(feature.substring(1));
220 } else if (feature.equals("no_layering_check")) {
221 // TODO(bazel-team): Remove once we do not have BUILD files left that contain
222 // 'no_layering_check'.
223 disabled.add(feature.substring(3));
224 } else {
225 enabled.add(feature);
226 }
227 }
228 }
229
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 @Override
231 public Rule getRule() {
232 return rule;
233 }
234
235 /**
Michael Staib8824d5e2016-01-20 21:37:05 +0000236 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
237 */
238 public String getRuleClassNameForLogging() {
239 return ruleClassNameForLogging;
240 }
241
242 /**
Kristina Chodorow91876f02015-02-27 17:14:12 +0000243 * Returns the workspace name for the rule.
244 */
245 public String getWorkspaceName() {
246 return rule.getWorkspaceName();
247 }
248
249 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100250 * The configuration conditions that trigger this rule's configurable attributes.
251 */
Lukacs Berki7894c182016-05-10 12:07:01 +0000252 ImmutableMap<Label, ConfigMatchingProvider> getConfigConditions() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100253 return configConditions;
254 }
255
256 /**
Lukacs Berki4313d372015-06-29 07:28:44 +0000257 * Returns the host configuration for this rule.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100258 */
259 public BuildConfiguration getHostConfiguration() {
Greg Estren9eb1cf02015-06-26 22:18:35 +0000260 return hostConfiguration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100261 }
262
263 /**
Dmitry Lomovace678e2015-12-16 15:10:20 +0000264 * Attributes from aspects.
265 */
266 public ImmutableMap<String, Attribute> getAspectAttributes() {
267 return aspectAttributes;
268 }
269
270 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100271 * Accessor for the Rule's attribute values.
272 */
273 public AttributeMap attributes() {
274 return attributes;
275 }
276
277 /**
Cal Peyserf296e872016-05-03 17:36:54 +0000278 * Returns a map that indicates which providers should be exported to skylark under the key
279 * (map key). These provider types will also be exportable by skylark rules under (map key).
280 */
281 public ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>>
282 getSkylarkProviderRegistry() {
283 return skylarkProviderRegistry;
284 }
285
286 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100287 * Returns whether this instance is known to have errors at this point during analysis. Do not
288 * call this method after the initializationHook has returned.
289 */
290 public boolean hasErrors() {
291 return getAnalysisEnvironment().hasErrors();
292 }
Chris Parsons4dfb22c2016-05-23 17:39:42 +0000293
294 /**
295 * No-op if {@link #hasErrors} is false, throws {@link RuleErrorException} if it is true.
296 * This provides a convenience to early-exit of configured target creation if there are errors.
297 */
298 public void assertNoErrors() throws RuleErrorException {
299 if (hasErrors()) {
300 throw new RuleErrorException();
301 }
302 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100303
304 /**
305 * Returns an immutable map from attribute name to list of configured targets for that attribute.
306 */
307 public ListMultimap<String, ? extends TransitiveInfoCollection> getConfiguredTargetMap() {
308 return targetMap;
309 }
310
311 /**
312 * Returns an immutable map from attribute name to list of fileset entries.
313 */
314 public ListMultimap<String, ConfiguredFilesetEntry> getFilesetEntryMap() {
315 return filesetEntryMap;
316 }
317
318 @Override
319 public ActionOwner getActionOwner() {
320 if (actionOwner == null) {
Janak Ramakrishnan7857c792016-03-21 08:35:35 +0000321 actionOwner = createActionOwner(rule, getConfiguration());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100322 }
323 return actionOwner;
324 }
325
326 /**
327 * Returns a configuration fragment for this this target.
328 */
329 @Nullable
Florian Weikert3f8aac92015-09-07 12:06:02 +0000330 public <T extends Fragment> T getFragment(Class<T> fragment, ConfigurationTransition config) {
Florian Weikertd2a24612015-09-07 14:35:23 +0000331 return getFragment(fragment, fragment.getSimpleName(), "", config);
332 }
333
334 @Nullable
335 protected <T extends Fragment> T getFragment(Class<T> fragment, String name,
336 String additionalErrorMessage, ConfigurationTransition config) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100337 // TODO(bazel-team): The fragments can also be accessed directly through BuildConfiguration.
338 // Can we lock that down somehow?
Florian Weikert3f8aac92015-09-07 12:06:02 +0000339 Preconditions.checkArgument(isLegalFragment(fragment, config),
Florian Weikertd2a24612015-09-07 14:35:23 +0000340 "%s has to declare '%s' as a required fragment "
341 + "in %s configuration in order to access it.%s",
Michael Staib8824d5e2016-01-20 21:37:05 +0000342 getRuleClassNameForLogging(), name, FragmentCollection.getConfigurationName(config),
Florian Weikertd2a24612015-09-07 14:35:23 +0000343 additionalErrorMessage);
Florian Weikert1c2eeac2015-10-28 10:00:53 +0000344 return getConfiguration(config).getFragment(fragment);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345 }
346
Florian Weikert082c0542015-08-06 10:24:29 +0000347 @Nullable
Florian Weikert3f8aac92015-09-07 12:06:02 +0000348 public <T extends Fragment> T getFragment(Class<T> fragment) {
349 // NONE means target configuration.
350 return getFragment(fragment, ConfigurationTransition.NONE);
Florian Weikert082c0542015-08-06 10:24:29 +0000351 }
352
Florian Weikert3f8aac92015-09-07 12:06:02 +0000353 @Nullable
354 public Fragment getSkylarkFragment(String name, ConfigurationTransition config) {
Florian Weikert1c2eeac2015-10-28 10:00:53 +0000355 Class<? extends Fragment> fragmentClass =
356 getConfiguration(config).getSkylarkFragmentByName(name);
Florian Weikertd2a24612015-09-07 14:35:23 +0000357 if (fragmentClass == null) {
358 return null;
359 }
360 return getFragment(fragmentClass, name,
361 String.format(
362 " Please update the '%1$sfragments' argument of the rule definition "
363 + "(for example: %1$sfragments = [\"%2$s\"])",
364 (config == ConfigurationTransition.HOST) ? "host_" : "", name),
365 config);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000366 }
367
368 public ImmutableCollection<String> getSkylarkFragmentNames(ConfigurationTransition config) {
369 return getConfiguration(config).getSkylarkFragmentNames();
370 }
371
372 public <T extends Fragment> boolean isLegalFragment(
373 Class<T> fragment, ConfigurationTransition config) {
Greg Estrenc5a352f2015-11-13 17:25:36 +0000374 return fragment == universalFragment
375 || configurationFragmentPolicy.isLegalConfigurationFragment(fragment, config);
Florian Weikert082c0542015-08-06 10:24:29 +0000376 }
377
Ulf Adamsea11fc52015-08-04 14:23:58 +0000378 public <T extends Fragment> boolean isLegalFragment(Class<T> fragment) {
Florian Weikert3f8aac92015-09-07 12:06:02 +0000379 // NONE means target configuration.
380 return isLegalFragment(fragment, ConfigurationTransition.NONE);
381 }
Florian Weikertd2a24612015-09-07 14:35:23 +0000382
Florian Weikert3f8aac92015-09-07 12:06:02 +0000383 protected BuildConfiguration getConfiguration(ConfigurationTransition config) {
384 return config.equals(ConfigurationTransition.HOST) ? hostConfiguration : getConfiguration();
Ulf Adamsea11fc52015-08-04 14:23:58 +0000385 }
386
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100387 @Override
388 public ArtifactOwner getOwner() {
389 return getAnalysisEnvironment().getOwner();
390 }
391
Ulf Adamsc272e0f2015-04-22 19:56:21 +0000392 public ImmutableList<Artifact> getBuildInfo(BuildInfoKey key) {
393 return getAnalysisEnvironment().getBuildInfo(this, key);
394 }
395
Janak Ramakrishnan7857c792016-03-21 08:35:35 +0000396 @VisibleForTesting
397 public static ActionOwner createActionOwner(Rule rule, BuildConfiguration configuration) {
398 return new ActionOwner(
399 rule.getLabel(),
400 rule.getLocation(),
401 configuration.getMnemonic(),
402 rule.getTargetKind(),
403 configuration.checksum(),
404 configuration.isHostConfiguration() ? HOST_CONFIGURATION_PROGRESS_TAG : null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100405 }
406
407 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000408 public void registerAction(ActionAnalysisMetadata... action) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100409 getAnalysisEnvironment().registerAction(action);
410 }
411
412 /**
413 * Convenience function for subclasses to report non-attribute-specific
414 * errors in the current rule.
415 */
416 @Override
417 public void ruleError(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000418 reporter.ruleError(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100419 }
Chris Parsons4dfb22c2016-05-23 17:39:42 +0000420
421 /**
422 * Convenience function to report non-attribute-specific errors in the current rule and then
423 * throw a {@link RuleErrorException}, immediately exiting the build invocation. Alternatively,
424 * invoke {@link #ruleError} instead to collect additional error information before ending the
425 * invocation.
426 */
427 public void throwWithRuleError(String message) throws RuleErrorException {
428 reporter.ruleError(message);
429 throw new RuleErrorException();
430 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100431
432 /**
433 * Convenience function for subclasses to report non-attribute-specific
434 * warnings in the current rule.
435 */
436 @Override
437 public void ruleWarning(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000438 reporter.ruleWarning(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100439 }
440
441 /**
442 * Convenience function for subclasses to report attribute-specific errors in
443 * the current rule.
444 *
445 * <p>If the name of the attribute starts with <code>$</code>
446 * it is replaced with a string <code>(an implicit dependency)</code>.
447 */
448 @Override
449 public void attributeError(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000450 reporter.attributeError(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100451 }
452
453 /**
Chris Parsons4dfb22c2016-05-23 17:39:42 +0000454 * Convenience function to report attribute-specific errors in the current rule, and then throw a
455 * {@link RuleErrorException}, immediately exiting the build invocation. Alternatively, invoke
456 * {@link #attributeError} instead to collect additional error information before ending the
457 * invocation.
458 *
459 * <p>If the name of the attribute starts with <code>$</code>
460 * it is replaced with a string <code>(an implicit dependency)</code>.
461 */
Chris Parsonsf6a45fd2016-06-15 19:32:34 +0000462 public RuleErrorException throwWithAttributeError(String attrName, String message)
463 throws RuleErrorException {
Chris Parsons4dfb22c2016-05-23 17:39:42 +0000464 reporter.attributeError(attrName, message);
465 throw new RuleErrorException();
466 }
467
468 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100469 * Like attributeError, but does not mark the configured target as errored.
470 *
471 * <p>If the name of the attribute starts with <code>$</code>
472 * it is replaced with a string <code>(an implicit dependency)</code>.
473 */
474 @Override
475 public void attributeWarning(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +0000476 reporter.attributeWarning(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100477 }
478
479 /**
480 * Returns an artifact beneath the root of either the "bin" or "genfiles"
481 * tree, whose path is based on the name of this target and the current
482 * configuration. The choice of which tree to use is based on the rule with
483 * which this target (which must be an OutputFile or a Rule) is associated.
484 */
485 public Artifact createOutputArtifact() {
486 return internalCreateOutputArtifact(getTarget());
487 }
488
489 /**
490 * Returns the output artifact of an {@link OutputFile} of this target.
491 *
492 * @see #createOutputArtifact()
493 */
494 public Artifact createOutputArtifact(OutputFile out) {
495 return internalCreateOutputArtifact(out);
496 }
497
498 /**
499 * Implementation for {@link #createOutputArtifact()} and
500 * {@link #createOutputArtifact(OutputFile)}. This is private so that
501 * {@link #createOutputArtifact(OutputFile)} can have a more specific
502 * signature.
503 */
504 private Artifact internalCreateOutputArtifact(Target target) {
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000505 Preconditions.checkState(
506 target.getLabel().getPackageIdentifier().equals(getLabel().getPackageIdentifier()),
507 "Creating output artifact for target '%s' in different package than the rule '%s' "
508 + "being analyzed", target.getLabel(), getLabel());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100509 Root root = getBinOrGenfilesDirectory();
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000510 return getPackageRelativeArtifact(target.getName(), root);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100511 }
512
513 /**
514 * Returns the root of either the "bin" or "genfiles"
515 * tree, based on this target and the current configuration.
516 * The choice of which tree to use is based on the rule with
517 * which this target (which must be an OutputFile or a Rule) is associated.
518 */
519 public Root getBinOrGenfilesDirectory() {
520 return rule.hasBinaryOutput()
521 ? getConfiguration().getBinDirectory()
522 : getConfiguration().getGenfilesDirectory();
523 }
524
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000525 /**
526 * Creates an artifact in a directory that is unique to the package that contains the rule,
527 * thus guaranteeing that it never clashes with artifacts created by rules in other packages.
528 */
529 public Artifact getPackageRelativeArtifact(String relative, Root root) {
530 return getPackageRelativeArtifact(new PathFragment(relative), root);
531 }
532
533 /**
Lukacs Berki21a04f22015-08-20 13:31:24 +0000534 * Returns an artifact that can be an output of shared actions. Only use when there is no other
535 * option.
536 *
537 * <p>This artifact can be created anywhere in the output tree, which, in addition to making
538 * sharing possible, opens up the possibility of action conflicts and makes it impossible to
539 * infer the label of the rule creating the artifact from the path of the artifact.
540 */
541 public Artifact getShareableArtifact(PathFragment rootRelativePath, Root root) {
542 return getAnalysisEnvironment().getDerivedArtifact(rootRelativePath, root);
543 }
544
545 /**
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000546 * Creates an artifact in a directory that is unique to the package that contains the rule,
547 * thus guaranteeing that it never clashes with artifacts created by rules in other packages.
548 */
549 public Artifact getPackageRelativeArtifact(PathFragment relative, Root root) {
550 return getDerivedArtifact(getPackageDirectory().getRelative(relative), root);
551 }
552
553 /**
554 * Returns the root-relative path fragment under which output artifacts of this rule should go.
555 *
556 * <p>Note that:
557 * <ul>
558 * <li>This doesn't guarantee that there are no clashes with rules in the same package.
559 * <li>If possible, {@link #getPackageRelativeArtifact(PathFragment, Root)} should be used
560 * instead of this method.
561 * </ul>
562 *
563 * Ideally, user-visible artifacts should all have corresponding output file targets, all others
564 * should go into a rule-specific directory.
565 * {@link #getUniqueDirectoryArtifact(String, PathFragment, Root)}) ensures that this is the case.
566 */
567 public PathFragment getPackageDirectory() {
568 return getLabel().getPackageIdentifier().getPathFragment();
569 }
570
571 /**
572 * Creates an artifact under a given root with the given root-relative path.
573 *
574 * <p>Verifies that it is in the root-relative directory corresponding to the package of the rule,
575 * thus ensuring that it doesn't clash with other artifacts generated by other rules using this
576 * method.
577 */
Ulf Adams3ab82f72015-09-04 12:10:53 +0000578 @Override
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000579 public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
580 Preconditions.checkState(rootRelativePath.startsWith(getPackageDirectory()),
581 "Output artifact '%s' not under package directory '%s' for target '%s'",
582 rootRelativePath, getPackageDirectory(), getLabel());
583 return getAnalysisEnvironment().getDerivedArtifact(rootRelativePath, root);
584 }
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000585
586 /**
587 * Creates a TreeArtifact under a given root with the given root-relative path.
588 *
589 * <p>Verifies that it is in the root-relative directory corresponding to the package of the rule,
590 * thus ensuring that it doesn't clash with other artifacts generated by other rules using this
591 * method.
592 */
593 public Artifact getTreeArtifact(PathFragment rootRelativePath, Root root) {
594 Preconditions.checkState(rootRelativePath.startsWith(getPackageDirectory()),
595 "Output artifact '%s' not under package directory '%s' for target '%s'",
596 rootRelativePath, getPackageDirectory(), getLabel());
597 return getAnalysisEnvironment().getTreeArtifact(rootRelativePath, root);
598 }
599
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000600 /**
601 * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
602 * clashes with artifacts created by other rules.
603 */
604 public Artifact getUniqueDirectoryArtifact(
605 String uniqueDirectory, String relative, Root root) {
606 return getUniqueDirectoryArtifact(uniqueDirectory, new PathFragment(relative), root);
607 }
608
Lukacs Berkid6e64242015-07-30 17:06:23 +0000609 /**
610 * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
611 * clashes with artifacts created by other rules.
612 */
Lukacs Berki4a89a9b2015-07-29 06:54:07 +0000613 public Artifact getUniqueDirectoryArtifact(
614 String uniqueDirectory, PathFragment relative, Root root) {
615 return getDerivedArtifact(getUniqueDirectory(uniqueDirectory).getRelative(relative), root);
616 }
617
Googler03083852015-12-06 18:31:53 +0000618 /**
619 * Returns the Attribute associated with this name, if it's a valid attribute for this rule,
620 * or is associated with an attached aspect. Otherwise returns null.
621 */
622 @Nullable
623 public Attribute getAttribute(String attributeName) {
Marian Loburca3cf532015-02-27 09:14:50 +0000624 Attribute result = getRule().getAttributeDefinition(attributeName);
625 if (result != null) {
626 return result;
627 }
Carmi Grushko06f65f72015-11-02 22:42:24 +0000628 return aspectAttributes.get(attributeName);
Marian Loburca3cf532015-02-27 09:14:50 +0000629 }
630
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100631 /**
632 * Returns the list of transitive info collections that feed into this target through the
633 * specified attribute. Note that you need to specify the correct mode for the attribute,
634 * otherwise an assertion will be raised.
635 */
636 public List<? extends TransitiveInfoCollection> getPrerequisites(String attributeName,
637 Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +0000638 Attribute attributeDefinition = getAttribute(attributeName);
Chris Parsons2e62c0d2016-04-19 22:13:59 +0000639 if ((mode == Mode.TARGET) && (attributeDefinition.hasSplitConfigurationTransition())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100640 // TODO(bazel-team): If you request a split-configured attribute in the target configuration,
641 // we return only the list of configured targets for the first architecture; this is for
642 // backwards compatibility with existing code in cases where the call to getPrerequisites is
643 // deeply nested and we can't easily inject the behavior we want. However, we should fix all
644 // such call sites.
645 checkAttribute(attributeName, Mode.SPLIT);
646 Map<String, ? extends List<? extends TransitiveInfoCollection>> map =
647 getSplitPrerequisites(attributeName, /*requireSplit=*/false);
648 return map.isEmpty()
649 ? ImmutableList.<TransitiveInfoCollection>of()
650 : map.entrySet().iterator().next().getValue();
651 }
652
653 checkAttribute(attributeName, mode);
654 return targetMap.get(attributeName);
655 }
656
657 /**
658 * Returns the a prerequisites keyed by the CPU of their configurations; this method throws an
659 * exception if the split transition is not active.
660 */
661 public Map<String, ? extends List<? extends TransitiveInfoCollection>>
662 getSplitPrerequisites(String attributeName) {
663 return getSplitPrerequisites(attributeName, /*requireSplit*/true);
664 }
665
666 private Map<String, ? extends List<? extends TransitiveInfoCollection>>
667 getSplitPrerequisites(String attributeName, boolean requireSplit) {
668 checkAttribute(attributeName, Mode.SPLIT);
669
Marian Loburca3cf532015-02-27 09:14:50 +0000670 Attribute attributeDefinition = getAttribute(attributeName);
Chris Parsons2e62c0d2016-04-19 22:13:59 +0000671 SplitTransition<?> transition = attributeDefinition.getSplitTransition(rule);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100672 List<BuildConfiguration> configurations =
673 getConfiguration().getTransitions().getSplitConfigurations(transition);
674 if (configurations.size() == 1) {
675 // There are two cases here:
676 // 1. Splitting is enabled, but only one target cpu.
677 // 2. Splitting is disabled, and no --cpu value was provided on the command line.
678 // In the first case, the cpu value is non-null, but in the second case it is null. We only
679 // allow that to proceed if the caller specified that he is going to ignore the cpu value
680 // anyway.
681 String cpu = configurations.get(0).getCpu();
682 if (cpu == null) {
683 Preconditions.checkState(!requireSplit);
684 cpu = "DO_NOT_USE";
685 }
686 return ImmutableMap.of(cpu, targetMap.get(attributeName));
687 }
688
689 Set<String> cpus = new HashSet<>();
690 for (BuildConfiguration config : configurations) {
691 // This method should only be called when the split config is enabled on the command line, in
692 // which case this cpu can't be null.
693 Preconditions.checkNotNull(config.getCpu());
694 cpus.add(config.getCpu());
695 }
696
697 // Use an ImmutableListMultimap.Builder here to preserve ordering.
698 ImmutableListMultimap.Builder<String, TransitiveInfoCollection> result =
699 ImmutableListMultimap.builder();
700 for (TransitiveInfoCollection t : targetMap.get(attributeName)) {
701 if (t.getConfiguration() != null) {
702 result.put(t.getConfiguration().getCpu(), t);
703 } else {
704 // Source files don't have a configuration, so we add them to all architecture entries.
705 for (String cpu : cpus) {
706 result.put(cpu, t);
707 }
708 }
709 }
710 return Multimaps.asMap(result.build());
711 }
712
713 /**
714 * Returns the specified provider of the prerequisite referenced by the attribute in the
715 * argument. Note that you need to specify the correct mode for the attribute, otherwise an
716 * assertion will be raised. If the attribute is empty of it does not support the specified
717 * provider, returns null.
718 */
719 public <C extends TransitiveInfoProvider> C getPrerequisite(
720 String attributeName, Mode mode, Class<C> provider) {
721 TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
722 return prerequisite == null ? null : prerequisite.getProvider(provider);
723 }
724
725 /**
726 * Returns the transitive info collection that feeds into this target through the specified
727 * attribute. Note that you need to specify the correct mode for the attribute, otherwise an
728 * assertion will be raised. Returns null if the attribute is empty.
729 */
730 public TransitiveInfoCollection getPrerequisite(String attributeName, Mode mode) {
731 checkAttribute(attributeName, mode);
732 List<? extends TransitiveInfoCollection> elements = targetMap.get(attributeName);
733 if (elements.size() > 1) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000734 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
735 + " produces more than one prerequisite");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100736 }
737 return elements.isEmpty() ? null : elements.get(0);
738 }
739
740 /**
Chris Parsons4aa7c9d2016-04-07 19:29:02 +0000741 * For a given attribute, returns all {@link TransitiveInfoProvider}s provided by targets
742 * of that attribute. Each {@link TransitiveInfoProvider} is keyed by the
743 * {@link BuildConfiguration} under which the provider was created.
744 */
745 public <C extends TransitiveInfoProvider> ImmutableListMultimap<BuildConfiguration, C>
746 getPrerequisitesByConfiguration(String attributeName, Mode mode, final Class<C> classType) {
747 AnalysisUtils.checkProvider(classType);
748 List<? extends TransitiveInfoCollection> transitiveInfoCollections =
749 getPrerequisites(attributeName, mode);
Chris Parsons0d7f0412016-04-29 20:35:44 +0000750
Chris Parsons4aa7c9d2016-04-07 19:29:02 +0000751 ImmutableListMultimap.Builder<BuildConfiguration, C> result =
752 ImmutableListMultimap.builder();
753 for (TransitiveInfoCollection prerequisite : transitiveInfoCollections) {
754 C prerequisiteProvider = prerequisite.getProvider(classType);
755 if (prerequisiteProvider != null) {
756 result.put(prerequisite.getConfiguration(), prerequisiteProvider);
757 }
758 }
759 return result.build();
760 }
761
762 /**
Chris Parsons0d7f0412016-04-29 20:35:44 +0000763 * For a given attribute, returns all {@link TransitiveInfoCollection}s provided by targets
764 * of that attribute. Each {@link TransitiveInfoCollection} is keyed by the
765 * {@link BuildConfiguration} under which the collection was created.
766 */
767 public ImmutableListMultimap<BuildConfiguration, TransitiveInfoCollection>
768 getPrerequisitesByConfiguration(String attributeName, Mode mode) {
769 List<? extends TransitiveInfoCollection> transitiveInfoCollections =
770 getPrerequisites(attributeName, mode);
771
772 ImmutableListMultimap.Builder<BuildConfiguration, TransitiveInfoCollection> result =
773 ImmutableListMultimap.builder();
774 for (TransitiveInfoCollection prerequisite : transitiveInfoCollections) {
775 result.put(prerequisite.getConfiguration(), prerequisite);
776 }
777 return result.build();
778 }
779
780 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100781 * Returns all the providers of the specified type that are listed under the specified attribute
782 * of this target in the BUILD file.
783 */
784 public <C extends TransitiveInfoProvider> Iterable<C> getPrerequisites(String attributeName,
785 Mode mode, final Class<C> classType) {
786 AnalysisUtils.checkProvider(classType);
787 return AnalysisUtils.getProviders(getPrerequisites(attributeName, mode), classType);
788 }
789
790 /**
791 * Returns all the providers of the specified type that are listed under the specified attribute
792 * of this target in the BUILD file, and that contain the specified provider.
793 */
794 public <C extends TransitiveInfoProvider> Iterable<? extends TransitiveInfoCollection>
795 getPrerequisitesIf(String attributeName, Mode mode, final Class<C> classType) {
796 AnalysisUtils.checkProvider(classType);
797 return AnalysisUtils.filterByProvider(getPrerequisites(attributeName, mode), classType);
798 }
799
800 /**
801 * Returns the prerequisite referred to by the specified attribute. Also checks whether
802 * the attribute is marked as executable and that the target referred to can actually be
803 * executed.
804 *
805 * <p>The {@code mode} argument must match the configuration transition specified in the
806 * definition of the attribute.
807 *
808 * @param attributeName the name of the attribute
809 * @param mode the configuration transition of the attribute
810 *
811 * @return the {@link FilesToRunProvider} interface of the prerequisite.
812 */
Carmi Grushko802f39e2016-04-06 20:03:56 +0000813 @Nullable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100814 public FilesToRunProvider getExecutablePrerequisite(String attributeName, Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +0000815 Attribute ruleDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100816
817 if (ruleDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000818 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100819 + " is not defined");
820 }
821 if (!ruleDefinition.isExecutable()) {
Michael Staib8824d5e2016-01-20 21:37:05 +0000822 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100823 + " is not configured to be executable");
824 }
825
826 TransitiveInfoCollection prerequisite = getPrerequisite(attributeName, mode);
827 if (prerequisite == null) {
828 return null;
829 }
830
831 FilesToRunProvider result = prerequisite.getProvider(FilesToRunProvider.class);
832 if (result == null || result.getExecutable() == null) {
833 attributeError(
834 attributeName, prerequisite.getLabel() + " does not refer to a valid executable target");
835 }
836 return result;
837 }
838
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000839 /** Indicates whether a string list attribute should be tokenized. */
840 public enum Tokenize {
841 YES,
842 NO
843 }
844
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100845 /**
Ulf Adamse685ef32015-07-29 15:28:01 +0000846 * Gets an attribute of type STRING_LIST expanding Make variables, $(location) tags into the
847 * dependency location (see {@link LocationExpander} for details) and tokenizes the result.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100848 *
849 * @param attributeName the name of the attribute to process
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000850 * @return a list of strings containing the expanded and tokenized values for the attribute
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100851 */
Ulf Adams10993fe2016-04-19 12:55:12 +0000852 public ImmutableList<String> getTokenizedStringListAttr(String attributeName) {
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000853 return getExpandedStringListAttr(attributeName, Tokenize.YES);
854 }
855
856 /**
857 * Gets an attribute of type STRING_LIST expanding Make variables and $(location) tags,
858 * and optionally tokenizes the result.
859 *
860 * @param attributeName the name of the attribute to process
861 * @return a list of strings containing the processed values for the attribute
862 */
Ulf Adams10993fe2016-04-19 12:55:12 +0000863 public ImmutableList<String> getExpandedStringListAttr(String attributeName, Tokenize tokenize) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100864 if (!getRule().isAttrDefined(attributeName, Type.STRING_LIST)) {
865 // TODO(bazel-team): This should be an error.
866 return ImmutableList.of();
867 }
868 List<String> original = attributes().get(attributeName, Type.STRING_LIST);
869 if (original.isEmpty()) {
870 return ImmutableList.of();
871 }
872 List<String> tokens = new ArrayList<>();
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000873 LocationExpander locationExpander =
Ulf Adamse685ef32015-07-29 15:28:01 +0000874 new LocationExpander(this, LocationExpander.Options.ALLOW_DATA);
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000875
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100876 for (String token : original) {
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000877 expandValue(tokens, attributeName, token, locationExpander, tokenize);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100878 }
879 return ImmutableList.copyOf(tokens);
880 }
881
882 /**
883 * Expands make variables in value and tokenizes the result into tokens.
884 *
885 * <p>This methods should be called only during initialization.
886 */
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000887 public void tokenizeAndExpandMakeVars(List<String> tokens, String attributeName, String value) {
888 tokenizeAndExpandMakeVars(tokens, attributeName, value, null);
889 }
890
891 /**
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000892 * Expands make variables and $(location) tags in value and tokenizes the result into tokens.
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000893 *
894 * <p>This methods should be called only during initialization.
895 */
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000896 public void tokenizeAndExpandMakeVars(
897 List<String> tokens,
898 String attributeName,
899 String value,
900 @Nullable LocationExpander locationExpander) {
901 expandValue(tokens, attributeName, value, locationExpander, Tokenize.YES);
902 }
903
904 /**
905 * Expands make variables and $(location) tags in value, and optionally tokenizes the result.
906 *
907 * <p>This methods should be called only during initialization.
908 */
909 public void expandValue(
910 List<String> tokens,
911 String attributeName,
912 String value,
913 @Nullable LocationExpander locationExpander,
914 Tokenize tokenize) {
915 if (locationExpander != null) {
916 value = locationExpander.expandAttribute(attributeName, value);
917 }
918 value = expandMakeVariables(attributeName, value);
919 if (tokenize == Tokenize.YES) {
920 try {
921 ShellUtils.tokenize(tokens, value);
922 } catch (ShellUtils.TokenizationException e) {
923 attributeError(attributeName, e.getMessage());
Damien Martin-Guillerezbb46c872015-03-06 12:04:35 +0000924 }
Liam Miller-Cushonce6a7b22016-02-03 18:12:36 +0000925 } else {
926 tokens.add(value);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100927 }
928 }
929
930 /**
931 * Return a context that maps Make variable names (string) to values (string).
932 *
933 * @return a ConfigurationMakeVariableContext.
934 **/
935 public ConfigurationMakeVariableContext getConfigurationMakeVariableContext() {
936 if (configurationMakeVariableContext == null) {
937 configurationMakeVariableContext = new ConfigurationMakeVariableContext(
938 getRule().getPackage(), getConfiguration());
939 }
940 return configurationMakeVariableContext;
941 }
942
943 /**
944 * Returns the string "expression" after expanding all embedded references to
945 * "Make" variables. If any errors are encountered, they are reported, and
946 * "expression" is returned unchanged.
947 *
948 * @param attributeName the name of the attribute from which "expression" comes;
949 * used for error reporting.
950 * @param expression the string to expand.
951 * @return the expansion of "expression".
952 */
953 public String expandMakeVariables(String attributeName, String expression) {
954 return expandMakeVariables(attributeName, expression, getConfigurationMakeVariableContext());
955 }
956
957 /**
958 * Returns the string "expression" after expanding all embedded references to
959 * "Make" variables. If any errors are encountered, they are reported, and
960 * "expression" is returned unchanged.
961 *
962 * @param attributeName the name of the attribute from which "expression" comes;
963 * used for error reporting.
964 * @param expression the string to expand.
965 * @param context the ConfigurationMakeVariableContext which can have a customized
966 * lookupMakeVariable(String) method.
967 * @return the expansion of "expression".
968 */
969 public String expandMakeVariables(String attributeName, String expression,
970 ConfigurationMakeVariableContext context) {
971 try {
972 return MakeVariableExpander.expand(expression, context);
973 } catch (MakeVariableExpander.ExpansionException e) {
974 attributeError(attributeName, e.getMessage());
975 return expression;
976 }
977 }
978
979 /**
980 * Gets the value of the STRING_LIST attribute expanding all make variables.
981 */
982 public List<String> expandedMakeVariablesList(String attrName) {
983 List<String> variables = new ArrayList<>();
984 for (String variable : attributes().get(attrName, Type.STRING_LIST)) {
985 variables.add(expandMakeVariables(attrName, variable));
986 }
987 return variables;
988 }
989
990 /**
991 * If the string consists of a single variable, returns the expansion of
992 * that variable. Otherwise, returns null. Syntax errors are reported.
993 *
994 * @param attrName the name of the attribute from which "expression" comes;
995 * used for error reporting.
996 * @param expression the string to expand.
997 * @return the expansion of "expression", or null.
998 */
999 public String expandSingleMakeVariable(String attrName, String expression) {
1000 try {
1001 return MakeVariableExpander.expandSingleVariable(expression,
1002 new ConfigurationMakeVariableContext(getRule().getPackage(), getConfiguration()));
1003 } catch (MakeVariableExpander.ExpansionException e) {
1004 attributeError(attrName, e.getMessage());
1005 return expression;
1006 }
1007 }
1008
1009 private void checkAttribute(String attributeName, Mode mode) {
Marian Loburca3cf532015-02-27 09:14:50 +00001010 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001011 if (attributeDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001012 throw new IllegalStateException(getRule().getLocation() + ": " + getRuleClassNameForLogging()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001013 + " attribute " + attributeName + " is not defined");
1014 }
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001015 if (!(attributeDefinition.getType() == BuildType.LABEL
1016 || attributeDefinition.getType() == BuildType.LABEL_LIST)) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001017 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001018 + " is not a label type attribute");
1019 }
1020 if (mode == Mode.HOST) {
1021 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.HOST) {
1022 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001023 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001024 + " is not configured for the host configuration");
1025 }
1026 } else if (mode == Mode.TARGET) {
1027 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.NONE) {
1028 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001029 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001030 + " is not configured for the target configuration");
1031 }
1032 } else if (mode == Mode.DATA) {
1033 if (attributeDefinition.getConfigurationTransition() != ConfigurationTransition.DATA) {
1034 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001035 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001036 + " is not configured for the data configuration");
1037 }
1038 } else if (mode == Mode.SPLIT) {
Chris Parsons2e62c0d2016-04-19 22:13:59 +00001039 if (!(attributeDefinition.hasSplitConfigurationTransition())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001040 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001041 + getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001042 + " is not configured for a split transition");
1043 }
1044 }
1045 }
1046
1047 /**
1048 * Returns the Mode for which the attribute is configured.
1049 * This is intended for Skylark, where the Mode is implicitly chosen.
1050 */
1051 public Mode getAttributeMode(String attributeName) {
Marian Loburca3cf532015-02-27 09:14:50 +00001052 Attribute attributeDefinition = getAttribute(attributeName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001053 if (attributeDefinition == null) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001054 throw new IllegalStateException(getRule().getLocation() + ": " + getRuleClassNameForLogging()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001055 + " attribute " + attributeName + " is not defined");
1056 }
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001057 if (!(attributeDefinition.getType() == BuildType.LABEL
1058 || attributeDefinition.getType() == BuildType.LABEL_LIST)) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001059 throw new IllegalStateException(getRuleClassNameForLogging() + " attribute " + attributeName
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001060 + " is not a label type attribute");
1061 }
1062 if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.HOST) {
1063 return Mode.HOST;
1064 } else if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.NONE) {
1065 return Mode.TARGET;
1066 } else if (attributeDefinition.getConfigurationTransition() == ConfigurationTransition.DATA) {
1067 return Mode.DATA;
Chris Parsons2e62c0d2016-04-19 22:13:59 +00001068 } else if (attributeDefinition.hasSplitConfigurationTransition()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001069 return Mode.SPLIT;
1070 }
1071 throw new IllegalStateException(getRule().getLocation() + ": "
Michael Staib8824d5e2016-01-20 21:37:05 +00001072 + getRuleClassNameForLogging() + " attribute " + attributeName + " is not configured");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001073 }
1074
1075 /**
1076 * For the specified attribute "attributeName" (which must be of type
1077 * list(label)), resolve all the labels into ConfiguredTargets (for the
1078 * configuration appropriate to the attribute) and return their build
1079 * artifacts as a {@link PrerequisiteArtifacts} instance.
1080 *
1081 * @param attributeName the name of the attribute to traverse
1082 */
1083 public PrerequisiteArtifacts getPrerequisiteArtifacts(String attributeName, Mode mode) {
1084 return PrerequisiteArtifacts.get(this, attributeName, mode);
1085 }
1086
1087 /**
1088 * For the specified attribute "attributeName" (which must be of type label),
1089 * resolves the ConfiguredTarget and returns its single build artifact.
1090 *
1091 * <p>If the attribute is optional, has no default and was not specified, then
1092 * null will be returned. Note also that null is returned (and an attribute
1093 * error is raised) if there wasn't exactly one build artifact for the target.
1094 */
1095 public Artifact getPrerequisiteArtifact(String attributeName, Mode mode) {
1096 TransitiveInfoCollection target = getPrerequisite(attributeName, mode);
1097 return transitiveInfoCollectionToArtifact(attributeName, target);
1098 }
1099
1100 /**
1101 * Equivalent to getPrerequisiteArtifact(), but also asserts that
1102 * host-configuration is appropriate for the specified attribute.
1103 */
1104 public Artifact getHostPrerequisiteArtifact(String attributeName) {
1105 TransitiveInfoCollection target = getPrerequisite(attributeName, Mode.HOST);
1106 return transitiveInfoCollectionToArtifact(attributeName, target);
1107 }
1108
1109 private Artifact transitiveInfoCollectionToArtifact(
1110 String attributeName, TransitiveInfoCollection target) {
1111 if (target != null) {
1112 Iterable<Artifact> artifacts = target.getProvider(FileProvider.class).getFilesToBuild();
1113 if (Iterables.size(artifacts) == 1) {
1114 return Iterables.getOnlyElement(artifacts);
1115 } else {
1116 attributeError(attributeName, target.getLabel() + " expected a single artifact");
1117 }
1118 }
1119 return null;
1120 }
1121
1122 /**
1123 * Returns the sole file in the "srcs" attribute. Reports an error and
1124 * (possibly) returns null if "srcs" does not identify a single file of the
1125 * expected type.
1126 */
1127 public Artifact getSingleSource(String fileTypeName) {
1128 List<Artifact> srcs = PrerequisiteArtifacts.get(this, "srcs", Mode.TARGET).list();
1129 switch (srcs.size()) {
1130 case 0 : // error already issued by getSrc()
1131 return null;
1132 case 1 : // ok
1133 return Iterables.getOnlyElement(srcs);
1134 default :
1135 attributeError("srcs", "only a single " + fileTypeName + " is allowed here");
1136 return srcs.get(0);
1137 }
1138 }
1139
1140 public Artifact getSingleSource() {
Michael Staib8824d5e2016-01-20 21:37:05 +00001141 return getSingleSource(getRuleClassNameForLogging() + " source file");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001142 }
1143
1144 /**
1145 * Returns a path fragment qualified by the rule name and unique fragment to
1146 * disambiguate artifacts produced from the source file appearing in
1147 * multiple rules.
1148 *
1149 * <p>For example "pkg/dir/name" -> "pkg/&lt;fragment>/rule/dir/name.
1150 */
1151 public final PathFragment getUniqueDirectory(String fragment) {
1152 return AnalysisUtils.getUniqueDirectory(getLabel(), new PathFragment(fragment));
1153 }
1154
1155 /**
1156 * Check that all targets that were specified as sources are from the same
1157 * package as this rule. Output a warning or an error for every target that is
1158 * imported from a different package.
1159 */
1160 public void checkSrcsSamePackage(boolean onlyWarn) {
1161 PathFragment packageName = getLabel().getPackageFragment();
1162 for (Artifact srcItem : PrerequisiteArtifacts.get(this, "srcs", Mode.TARGET).list()) {
1163 if (!srcItem.isSourceArtifact()) {
1164 // In theory, we should not do this check. However, in practice, we
1165 // have a couple of rules that do not obey the "srcs must contain
1166 // files and only files" rule. Thus, we are stuck with this hack here :(
1167 continue;
1168 }
1169 Label associatedLabel = srcItem.getOwner();
1170 PathFragment itemPackageName = associatedLabel.getPackageFragment();
1171 if (!itemPackageName.equals(packageName)) {
1172 String message = "please do not import '" + associatedLabel + "' directly. "
1173 + "You should either move the file to this package or depend on "
1174 + "an appropriate rule there";
1175 if (onlyWarn) {
1176 attributeWarning("srcs", message);
1177 } else {
1178 attributeError("srcs", message);
1179 }
1180 }
1181 }
1182 }
1183
1184
1185 /**
1186 * Returns the label to which the {@code NODEP_LABEL} attribute
1187 * {@code attrName} refers, checking that it is a valid label, and that it is
1188 * referring to a local target. Reports a warning otherwise.
1189 */
1190 public Label getLocalNodepLabelAttribute(String attrName) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001191 Label label = attributes().get(attrName, BuildType.NODEP_LABEL);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001192 if (label == null) {
1193 return null;
1194 }
1195
1196 if (!getTarget().getLabel().getPackageFragment().equals(label.getPackageFragment())) {
1197 attributeWarning(attrName, "does not reference a local rule");
1198 }
1199
1200 return label;
1201 }
1202
1203 /**
1204 * Returns the implicit output artifact for a given template function. If multiple or no artifacts
1205 * can be found as a result of the template, an exception is thrown.
1206 */
Florian Weikert4b67d4f2015-09-14 13:35:34 +00001207 public Artifact getImplicitOutputArtifact(ImplicitOutputsFunction function)
1208 throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001209 Iterable<String> result;
1210 try {
1211 result = function.getImplicitOutputs(RawAttributeMapper.of(rule));
1212 } catch (EvalException e) {
1213 // It's ok as long as we don't use this method from Skylark.
1214 throw new IllegalStateException(e);
1215 }
1216 return getImplicitOutputArtifact(Iterables.getOnlyElement(result));
1217 }
1218
1219 /**
1220 * Only use from Skylark. Returns the implicit output artifact for a given output path.
1221 */
1222 public Artifact getImplicitOutputArtifact(String path) {
Lukacs Berki4a89a9b2015-07-29 06:54:07 +00001223 return getPackageRelativeArtifact(path, getBinOrGenfilesDirectory());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001224 }
1225
1226 /**
1227 * Convenience method to return a host configured target for the "compiler"
1228 * attribute. Allows caller to decide whether a warning should be printed if
1229 * the "compiler" attribute is not set to the default value.
1230 *
1231 * @param warnIfNotDefault if true, print a warning if the value for the
1232 * "compiler" attribute is set to something other than the default
1233 * @return a ConfiguredTarget using the host configuration for the "compiler"
1234 * attribute
1235 */
1236 public final FilesToRunProvider getCompiler(boolean warnIfNotDefault) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001237 Label label = attributes().get("compiler", BuildType.LABEL);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001238 if (warnIfNotDefault && !label.equals(getRule().getAttrDefaultValue("compiler"))) {
1239 attributeWarning("compiler", "setting the compiler is strongly discouraged");
1240 }
1241 return getExecutablePrerequisite("compiler", Mode.HOST);
1242 }
1243
1244 /**
1245 * Returns the (unmodifiable, ordered) list of artifacts which are the outputs
1246 * of this target.
1247 *
1248 * <p>Each element in this list is associated with a single output, either
1249 * declared implicitly (via setImplicitOutputsFunction()) or explicitly
1250 * (listed in the 'outs' attribute of our rule).
1251 */
1252 public final ImmutableList<Artifact> getOutputArtifacts() {
1253 ImmutableList.Builder<Artifact> artifacts = ImmutableList.builder();
1254 for (OutputFile out : getRule().getOutputFiles()) {
1255 artifacts.add(createOutputArtifact(out));
1256 }
1257 return artifacts.build();
1258 }
1259
1260 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001261 * Like {@link #getOutputArtifacts()} but for a singular output item.
1262 * Reports an error if the "out" attribute is not a singleton.
1263 *
1264 * @return null if the output list is empty, the artifact for the first item
1265 * of the output list otherwise
1266 */
1267 public Artifact getOutputArtifact() {
1268 List<Artifact> outs = getOutputArtifacts();
1269 if (outs.size() != 1) {
1270 attributeError("out", "exactly one output file required");
1271 if (outs.isEmpty()) {
1272 return null;
1273 }
1274 }
1275 return outs.get(0);
1276 }
1277
1278 /**
1279 * Returns an artifact with a given file extension. All other path components
1280 * are the same as in {@code pathFragment}.
1281 */
1282 public final Artifact getRelatedArtifact(PathFragment pathFragment, String extension) {
1283 PathFragment file = FileSystemUtils.replaceExtension(pathFragment, extension);
Lukacs Berki4a89a9b2015-07-29 06:54:07 +00001284 return getDerivedArtifact(file, getConfiguration().getBinDirectory());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001285 }
1286
1287 /**
Googlerf812a2f2016-06-13 16:14:10 +00001288 * Returns true if the target for this context is a test target.
1289 */
1290 public boolean isTestTarget() {
1291 return TargetUtils.isTestRule(getTarget());
1292 }
1293
1294 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001295 * Returns true if runfiles support should create the runfiles tree, or
1296 * false if it should just create the manifest.
1297 */
1298 public boolean shouldCreateRunfilesSymlinks() {
1299 // TODO(bazel-team): Ideally we wouldn't need such logic, and we'd
1300 // always use the BuildConfiguration#buildRunfiles() to determine
1301 // whether to build the runfiles. The problem is that certain build
1302 // steps actually consume their runfiles. These include:
1303 // a. par files consumes the runfiles directory
1304 // We should modify autopar to take a list of files instead.
1305 // of the runfiles directory.
1306 // b. host tools could potentially use data files, but currently don't
1307 // (they're run from the execution root, not a runfiles tree).
1308 // Currently hostConfiguration.buildRunfiles() returns true.
Googlerf812a2f2016-06-13 16:14:10 +00001309 if (isTestTarget()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001310 // Tests are only executed during testing (duh),
1311 // and their runfiles are generated lazily on local
1312 // execution (see LocalTestStrategy). Therefore, it
1313 // is safe not to build their runfiles.
1314 return getConfiguration().buildRunfiles();
1315 } else {
1316 return true;
1317 }
1318 }
1319
1320 /**
1321 * @return true if {@code rule} is visible from {@code prerequisite}.
1322 *
1323 * <p>This only computes the logic as implemented by the visibility system. The final decision
1324 * whether a dependency is allowed is made by
1325 * {@link ConfiguredRuleClassProvider.PrerequisiteValidator}.
1326 */
1327 public static boolean isVisible(Rule rule, TransitiveInfoCollection prerequisite) {
1328 // Check visibility attribute
1329 for (PackageSpecification specification :
1330 prerequisite.getProvider(VisibilityProvider.class).getVisibility()) {
Lukacs Berki485eb962016-01-13 10:47:29 +00001331 if (specification.containsPackage(rule.getLabel().getPackageIdentifier())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001332 return true;
1333 }
1334 }
1335
1336 return false;
1337 }
1338
1339 /**
1340 * @return the set of features applicable for the current rule's package.
1341 */
1342 public ImmutableSet<String> getFeatures() {
1343 return features;
1344 }
1345
Florian Weikertb8a6a942015-09-25 12:36:08 +00001346 @Override
1347 public String toString() {
1348 return "RuleContext(" + getLabel() + ", " + getConfiguration() + ")";
1349 }
1350
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001351 /**
1352 * Builder class for a RuleContext.
1353 */
Florian Weikertb8a6a942015-09-25 12:36:08 +00001354 public static final class Builder implements RuleErrorConsumer {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001355 private final AnalysisEnvironment env;
1356 private final Rule rule;
Michael Staibb51251e2015-09-29 23:31:51 +00001357 private final ConfigurationFragmentPolicy configurationFragmentPolicy;
Greg Estrenc5a352f2015-11-13 17:25:36 +00001358 private Class<? extends BuildConfiguration.Fragment> universalFragment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001359 private final BuildConfiguration configuration;
Greg Estren9eb1cf02015-06-26 22:18:35 +00001360 private final BuildConfiguration hostConfiguration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001361 private final PrerequisiteValidator prerequisiteValidator;
Michael Staib8824d5e2016-01-20 21:37:05 +00001362 @Nullable private final String aspectName;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001363 private final ErrorReporter reporter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001364 private ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap;
Lukacs Berki7894c182016-05-10 12:07:01 +00001365 private ImmutableMap<Label, ConfigMatchingProvider> configConditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001366 private NestedSet<PackageSpecification> visibility;
Dmitry Lomovace678e2015-12-16 15:10:20 +00001367 private ImmutableMap<String, Attribute> aspectAttributes;
Cal Peyserf296e872016-05-03 17:36:54 +00001368 private ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>> skylarkProviderRegistry;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001369
Michael Staib8824d5e2016-01-20 21:37:05 +00001370 Builder(
1371 AnalysisEnvironment env,
1372 Rule rule,
1373 @Nullable String aspectName,
1374 BuildConfiguration configuration,
Greg Estren9eb1cf02015-06-26 22:18:35 +00001375 BuildConfiguration hostConfiguration,
Michael Staib8824d5e2016-01-20 21:37:05 +00001376 PrerequisiteValidator prerequisiteValidator,
1377 ConfigurationFragmentPolicy configurationFragmentPolicy) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001378 this.env = Preconditions.checkNotNull(env);
1379 this.rule = Preconditions.checkNotNull(rule);
Michael Staib8824d5e2016-01-20 21:37:05 +00001380 this.aspectName = aspectName;
1381 this.configurationFragmentPolicy = Preconditions.checkNotNull(configurationFragmentPolicy);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001382 this.configuration = Preconditions.checkNotNull(configuration);
Greg Estren9eb1cf02015-06-26 22:18:35 +00001383 this.hostConfiguration = Preconditions.checkNotNull(hostConfiguration);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001384 this.prerequisiteValidator = prerequisiteValidator;
Michael Staib8824d5e2016-01-20 21:37:05 +00001385 reporter = new ErrorReporter(env, rule, getRuleClassNameForLogging());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001386 }
1387
1388 RuleContext build() {
1389 Preconditions.checkNotNull(prerequisiteMap);
1390 Preconditions.checkNotNull(configConditions);
1391 Preconditions.checkNotNull(visibility);
Nathan Harmatafcb17112016-04-13 16:56:58 +00001392 AttributeMap attributes = ConfiguredAttributeMapper.of(rule, configConditions);
1393 validateAttributes(attributes);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001394 ListMultimap<String, ConfiguredTarget> targetMap = createTargetMap();
1395 ListMultimap<String, ConfiguredFilesetEntry> filesetEntryMap =
1396 createFilesetEntryMap(rule, configConditions);
Michael Staib8824d5e2016-01-20 21:37:05 +00001397 return new RuleContext(
1398 this,
Nathan Harmatafcb17112016-04-13 16:56:58 +00001399 attributes,
Michael Staib8824d5e2016-01-20 21:37:05 +00001400 targetMap,
1401 filesetEntryMap,
1402 configConditions,
1403 universalFragment,
1404 getRuleClassNameForLogging(),
Carmi Grushko06f65f72015-11-02 22:42:24 +00001405 aspectAttributes != null ? aspectAttributes : ImmutableMap.<String, Attribute>of());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001406 }
1407
Nathan Harmatafcb17112016-04-13 16:56:58 +00001408 private void validateAttributes(AttributeMap attributes) {
1409 rule.getRuleClassObject().checkAttributesNonEmpty(rule, reporter, attributes);
1410 }
1411
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001412 Builder setVisibility(NestedSet<PackageSpecification> visibility) {
1413 this.visibility = visibility;
1414 return this;
1415 }
1416
1417 /**
1418 * Sets the prerequisites and checks their visibility. It also generates appropriate error or
1419 * warning messages and sets the error flag as appropriate.
1420 */
1421 Builder setPrerequisites(ListMultimap<Attribute, ConfiguredTarget> prerequisiteMap) {
1422 this.prerequisiteMap = Preconditions.checkNotNull(prerequisiteMap);
1423 return this;
1424 }
1425
1426 /**
Carmi Grushko06f65f72015-11-02 22:42:24 +00001427 * Adds attributes which are defined by an Aspect (and not by RuleClass).
1428 */
1429 Builder setAspectAttributes(Map<String, Attribute> aspectAttributes) {
Dmitry Lomovace678e2015-12-16 15:10:20 +00001430 this.aspectAttributes = ImmutableMap.copyOf(aspectAttributes);
Carmi Grushko06f65f72015-11-02 22:42:24 +00001431 return this;
1432 }
1433
1434 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001435 * Sets the configuration conditions needed to determine which paths to follow for this
1436 * rule's configurable attributes.
1437 */
Lukacs Berki7894c182016-05-10 12:07:01 +00001438 Builder setConfigConditions(ImmutableMap<Label, ConfigMatchingProvider> configConditions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001439 this.configConditions = Preconditions.checkNotNull(configConditions);
1440 return this;
1441 }
1442
Greg Estrenc5a352f2015-11-13 17:25:36 +00001443 /**
1444 * Sets the fragment that can be legally accessed even when not explicitly declared.
1445 */
1446 Builder setUniversalFragment(Class<? extends BuildConfiguration.Fragment> fragment) {
1447 // TODO(bazel-team): Add this directly to ConfigurationFragmentPolicy, so we
1448 // don't need separate logic specifically for checking this fragment. The challenge is
1449 // that we need RuleClassProvider to figure out what this fragment is, and not every
1450 // call state that creates ConfigurationFragmentPolicy has access to that.
1451 this.universalFragment = fragment;
1452 return this;
1453 }
1454
Cal Peyserf296e872016-05-03 17:36:54 +00001455 /**
1456 * Sets a map that indicates which providers should be exported to skylark under the key
1457 * (map key). These provider types will also be exportable by skylark rules under (map key).
1458 */
1459 Builder setSkylarkProvidersRegistry(
1460 ImmutableBiMap<String, Class<? extends TransitiveInfoProvider>> skylarkProviderRegistry) {
1461 this.skylarkProviderRegistry = skylarkProviderRegistry;
1462 return this;
1463 }
1464
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001465 private boolean validateFilesetEntry(FilesetEntry filesetEntry, ConfiguredTarget src) {
1466 if (src.getProvider(FilesetProvider.class) != null) {
1467 return true;
1468 }
1469 if (filesetEntry.isSourceFileset()) {
1470 return true;
1471 }
1472
1473 Target srcTarget = src.getTarget();
1474 if (!(srcTarget instanceof FileTarget)) {
1475 attributeError("entries", String.format(
1476 "Invalid 'srcdir' target '%s'. Must be another Fileset or package",
1477 srcTarget.getLabel()));
1478 return false;
1479 }
1480
1481 if (srcTarget instanceof OutputFile) {
1482 attributeWarning("entries", String.format("'srcdir' target '%s' is not an input file. "
1483 + "This forces the Fileset to be executed unconditionally",
1484 srcTarget.getLabel()));
1485 }
1486
1487 return true;
1488 }
1489
1490 /**
1491 * Determines and returns a map from attribute name to list of configured fileset entries, based
1492 * on a PrerequisiteMap instance.
1493 */
1494 private ListMultimap<String, ConfiguredFilesetEntry> createFilesetEntryMap(
Lukacs Berki7894c182016-05-10 12:07:01 +00001495 final Rule rule, ImmutableMap<Label, ConfigMatchingProvider> configConditions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001496 final ImmutableSortedKeyListMultimap.Builder<String, ConfiguredFilesetEntry> mapBuilder =
1497 ImmutableSortedKeyListMultimap.builder();
1498 for (Attribute attr : rule.getAttributes()) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001499 if (attr.getType() != BuildType.FILESET_ENTRY_LIST) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001500 continue;
1501 }
1502 String attributeName = attr.getName();
1503 Map<Label, ConfiguredTarget> ctMap = new HashMap<>();
1504 for (ConfiguredTarget prerequisite : prerequisiteMap.get(attr)) {
1505 ctMap.put(prerequisite.getLabel(), prerequisite);
1506 }
1507 List<FilesetEntry> entries = ConfiguredAttributeMapper.of(rule, configConditions)
Lukacs Berkiffa73ad2015-09-18 11:40:12 +00001508 .get(attributeName, BuildType.FILESET_ENTRY_LIST);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001509 for (FilesetEntry entry : entries) {
1510 if (entry.getFiles() == null) {
1511 Label label = entry.getSrcLabel();
1512 ConfiguredTarget src = ctMap.get(label);
1513 if (!validateFilesetEntry(entry, src)) {
1514 continue;
1515 }
1516
1517 mapBuilder.put(attributeName, new ConfiguredFilesetEntry(entry, src));
1518 } else {
1519 ImmutableList.Builder<TransitiveInfoCollection> files = ImmutableList.builder();
1520 for (Label file : entry.getFiles()) {
1521 files.add(ctMap.get(file));
1522 }
1523 mapBuilder.put(attributeName, new ConfiguredFilesetEntry(entry, files.build()));
1524 }
1525 }
1526 }
1527 return mapBuilder.build();
1528 }
1529
1530 /**
1531 * Determines and returns a map from attribute name to list of configured targets.
1532 */
1533 private ImmutableSortedKeyListMultimap<String, ConfiguredTarget> createTargetMap() {
1534 ImmutableSortedKeyListMultimap.Builder<String, ConfiguredTarget> mapBuilder =
1535 ImmutableSortedKeyListMultimap.builder();
1536
1537 for (Map.Entry<Attribute, Collection<ConfiguredTarget>> entry :
1538 prerequisiteMap.asMap().entrySet()) {
1539 Attribute attribute = entry.getKey();
1540 if (attribute == null) {
1541 continue;
1542 }
1543 if (attribute.isSilentRuleClassFilter()) {
1544 Predicate<RuleClass> filter = attribute.getAllowedRuleClassesPredicate();
1545 for (ConfiguredTarget configuredTarget : entry.getValue()) {
1546 Target prerequisiteTarget = configuredTarget.getTarget();
1547 if ((prerequisiteTarget instanceof Rule)
1548 && filter.apply(((Rule) prerequisiteTarget).getRuleClassObject())) {
1549 validateDirectPrerequisite(attribute, configuredTarget);
1550 mapBuilder.put(attribute.getName(), configuredTarget);
1551 }
1552 }
1553 } else {
1554 for (ConfiguredTarget configuredTarget : entry.getValue()) {
1555 validateDirectPrerequisite(attribute, configuredTarget);
1556 mapBuilder.put(attribute.getName(), configuredTarget);
1557 }
1558 }
1559 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001560 return mapBuilder.build();
1561 }
1562
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001563 public void reportError(Location location, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001564 reporter.reportError(location, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001565 }
1566
Florian Weikertb8a6a942015-09-25 12:36:08 +00001567 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001568 public void ruleError(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001569 reporter.ruleError(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001570 }
1571
Florian Weikertb8a6a942015-09-25 12:36:08 +00001572 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001573 public void attributeError(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001574 reporter.attributeError(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001575 }
1576
1577 public void reportWarning(Location location, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001578 reporter.reportWarning(location, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001579 }
1580
Florian Weikertb8a6a942015-09-25 12:36:08 +00001581 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001582 public void ruleWarning(String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001583 reporter.ruleWarning(message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001584 }
1585
Florian Weikertb8a6a942015-09-25 12:36:08 +00001586 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001587 public void attributeWarning(String attrName, String message) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001588 reporter.attributeWarning(attrName, message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001589 }
1590
1591 private void reportBadPrerequisite(Attribute attribute, String targetKind,
Lukacs Berki7894c182016-05-10 12:07:01 +00001592 ConfiguredTarget prerequisite, String reason, boolean isWarning) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001593 String msgPrefix = targetKind != null ? targetKind + " " : "";
1594 String msgReason = reason != null ? " (" + reason + ")" : "";
1595 if (isWarning) {
1596 attributeWarning(attribute.getName(), String.format(
Lukacs Berki7894c182016-05-10 12:07:01 +00001597 "%s'%s'%s is unexpected here%s; continuing anyway",
1598 msgPrefix, prerequisite.getLabel(), AliasProvider.printVisibilityChain(prerequisite),
1599 msgReason));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001600 } else {
1601 attributeError(attribute.getName(), String.format(
Lukacs Berki7894c182016-05-10 12:07:01 +00001602 "%s'%s'%s is misplaced here%s", msgPrefix, prerequisite.getLabel(),
1603 AliasProvider.printVisibilityChain(prerequisite), msgReason));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001604 }
1605 }
1606
1607 private void validateDirectPrerequisiteType(ConfiguredTarget prerequisite,
1608 Attribute attribute) {
1609 Target prerequisiteTarget = prerequisite.getTarget();
1610 Label prerequisiteLabel = prerequisiteTarget.getLabel();
1611
1612 if (prerequisiteTarget instanceof Rule) {
1613 Rule prerequisiteRule = (Rule) prerequisiteTarget;
1614
1615 String reason = attribute.getValidityPredicate().checkValid(rule, prerequisiteRule);
1616 if (reason != null) {
1617 reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(),
Lukacs Berki7894c182016-05-10 12:07:01 +00001618 prerequisite, reason, false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001619 }
1620 }
1621
Yun Pengefd7ca12016-03-03 13:14:38 +00001622 if (prerequisiteTarget instanceof Rule) {
1623 validateRuleDependency(prerequisite, attribute);
1624 } else if (prerequisiteTarget instanceof FileTarget) {
1625 if (attribute.isStrictLabelCheckingEnabled()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001626 if (!attribute.getAllowedFileTypesPredicate()
1627 .apply(((FileTarget) prerequisiteTarget).getFilename())) {
1628 if (prerequisiteTarget instanceof InputFile
1629 && !((InputFile) prerequisiteTarget).getPath().exists()) {
1630 // Misplaced labels, no corresponding target exists
1631 if (attribute.getAllowedFileTypesPredicate().isNone()
1632 && !((InputFile) prerequisiteTarget).getFilename().contains(".")) {
1633 // There are no allowed files in the attribute but it's not a valid rule,
1634 // and the filename doesn't contain a dot --> probably a misspelled rule
1635 attributeError(attribute.getName(),
1636 "rule '" + prerequisiteLabel + "' does not exist");
1637 } else {
1638 attributeError(attribute.getName(),
1639 "target '" + prerequisiteLabel + "' does not exist");
1640 }
1641 } else {
1642 // The file exists but has a bad extension
Lukacs Berki7894c182016-05-10 12:07:01 +00001643 reportBadPrerequisite(attribute, "file", prerequisite,
Ulf Adams07dba942015-03-05 14:47:37 +00001644 "expected " + attribute.getAllowedFileTypesPredicate(), false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001645 }
1646 }
1647 }
1648 }
1649 }
1650
1651 public Rule getRule() {
1652 return rule;
1653 }
1654
Michael Staib8824d5e2016-01-20 21:37:05 +00001655 /**
1656 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
1657 */
1658 public String getRuleClassNameForLogging() {
1659 return aspectName != null
1660 ? aspectName + " aspect on " + rule.getRuleClass()
1661 : rule.getRuleClass();
1662 }
1663
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001664 public BuildConfiguration getConfiguration() {
1665 return configuration;
1666 }
1667
1668 /**
1669 * @return true if {@code rule} is visible from {@code prerequisite}.
1670 *
1671 * <p>This only computes the logic as implemented by the visibility system. The final decision
1672 * whether a dependency is allowed is made by
1673 * {@link ConfiguredRuleClassProvider.PrerequisiteValidator}, who is supposed to call this
1674 * method to determine whether a dependency is allowed as per visibility rules.
1675 */
1676 public boolean isVisible(TransitiveInfoCollection prerequisite) {
1677 return RuleContext.isVisible(rule, prerequisite);
1678 }
1679
1680 private void validateDirectPrerequisiteFileTypes(ConfiguredTarget prerequisite,
1681 Attribute attribute) {
1682 if (attribute.isSkipAnalysisTimeFileTypeCheck()) {
1683 return;
1684 }
1685 FileTypeSet allowedFileTypes = attribute.getAllowedFileTypesPredicate();
Ulf Adams788fd1a2015-03-12 13:54:09 +00001686 if (allowedFileTypes == null) {
1687 // It's not a label or label_list attribute.
1688 return;
1689 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001690 if (allowedFileTypes == FileTypeSet.ANY_FILE && !attribute.isNonEmpty()
1691 && !attribute.isSingleArtifact()) {
1692 return;
1693 }
1694
1695 // If we allow any file we still need to check if there are actually files generated
1696 // Note that this check only runs for ANY_FILE predicates if the attribute is NON_EMPTY
1697 // or SINGLE_ARTIFACT
1698 // If we performed this check when allowedFileTypes == NO_FILE this would
1699 // always throw an error in those cases
1700 if (allowedFileTypes != FileTypeSet.NO_FILE) {
1701 Iterable<Artifact> artifacts = prerequisite.getProvider(FileProvider.class)
1702 .getFilesToBuild();
1703 if (attribute.isSingleArtifact() && Iterables.size(artifacts) != 1) {
1704 attributeError(attribute.getName(),
1705 "'" + prerequisite.getLabel() + "' must produce a single file");
1706 return;
1707 }
1708 for (Artifact sourceArtifact : artifacts) {
1709 if (allowedFileTypes.apply(sourceArtifact.getFilename())) {
1710 return;
1711 }
1712 }
1713 attributeError(attribute.getName(), "'" + prerequisite.getLabel()
Michael Staib8824d5e2016-01-20 21:37:05 +00001714 + "' does not produce any " + getRuleClassNameForLogging() + " " + attribute.getName()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001715 + " files (expected " + allowedFileTypes + ")");
1716 }
1717 }
1718
Yun Pengefd7ca12016-03-03 13:14:38 +00001719 private String getMissingMandatoryProviders(ConfiguredTarget prerequisite, Attribute attribute){
Yun Peng5c34e962016-02-22 15:13:19 +00001720 List<ImmutableSet<String>> mandatoryProvidersList = attribute.getMandatoryProvidersList();
1721 if (mandatoryProvidersList.isEmpty()) {
Yun Pengefd7ca12016-03-03 13:14:38 +00001722 return null;
Yun Peng5c34e962016-02-22 15:13:19 +00001723 }
1724 List<List<String>> missingProvidersList = new ArrayList<>();
1725 for (ImmutableSet<String> providers : mandatoryProvidersList) {
1726 List<String> missing = new ArrayList<>();
1727 for (String provider : providers) {
1728 if (prerequisite.get(provider) == null) {
1729 missing.add(provider);
1730 }
1731 }
1732 if (missing.isEmpty()) {
Yun Pengefd7ca12016-03-03 13:14:38 +00001733 return null;
Yun Peng5c34e962016-02-22 15:13:19 +00001734 } else {
1735 missingProvidersList.add(missing);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001736 }
1737 }
Yun Peng5c34e962016-02-22 15:13:19 +00001738 StringBuilder missingProviders = new StringBuilder();
1739 Joiner joinProvider = Joiner.on("', '");
1740 for (List<String> providers : missingProvidersList) {
1741 if (missingProviders.length() > 0) {
1742 missingProviders.append(" or ");
1743 }
1744 missingProviders.append((providers.size() > 1) ? "[" : "")
1745 .append("'");
1746 joinProvider.appendTo(missingProviders, providers);
1747 missingProviders.append("'")
1748 .append((providers.size() > 1) ? "]" : "");
1749 }
Yun Pengefd7ca12016-03-03 13:14:38 +00001750 return missingProviders.toString();
1751 }
1752
Liam Miller-Cushonbbae4d92016-05-03 18:23:03 +00001753 private String getMissingMandatoryNativeProviders(
1754 ConfiguredTarget prerequisite, Attribute attribute) {
1755 List<Class<? extends TransitiveInfoProvider>> mandatoryProvidersList =
1756 attribute.getMandatoryNativeProviders();
1757 if (mandatoryProvidersList.isEmpty()) {
1758 return null;
1759 }
1760 List<Class<? extends TransitiveInfoProvider>> missing = new ArrayList<>();
1761 for (Class<? extends TransitiveInfoProvider> provider : mandatoryProvidersList) {
1762 if (prerequisite.getProvider(provider) == null) {
1763 missing.add(provider);
1764 }
1765 }
1766 if (missing.isEmpty()) {
1767 return null;
1768 }
1769 StringBuilder sb = new StringBuilder();
1770 for (Class<? extends TransitiveInfoProvider> provider : missing) {
1771 if (sb.length() > 0) {
1772 sb.append(", ");
1773 }
1774 sb.append(provider.getSimpleName());
1775 }
1776 return sb.toString();
1777 }
1778
Yun Pengefd7ca12016-03-03 13:14:38 +00001779 /**
1780 * Because some rules still have to use allowedRuleClasses to do rule dependency validation.
1781 * We implemented the allowedRuleClasses OR mandatoryProvidersList mechanism. Either condition
1782 * is satisfied, we consider the dependency valid.
1783 */
1784 private void validateRuleDependency(ConfiguredTarget prerequisite, Attribute attribute) {
1785 Target prerequisiteTarget = prerequisite.getTarget();
Yun Pengefd7ca12016-03-03 13:14:38 +00001786 RuleClass ruleClass = ((Rule) prerequisiteTarget).getRuleClassObject();
1787 Boolean allowed = null;
Yun Pengefd7ca12016-03-03 13:14:38 +00001788
1789 if (attribute.getAllowedRuleClassesPredicate() != Predicates.<RuleClass>alwaysTrue()) {
1790 allowed = attribute.getAllowedRuleClassesPredicate().apply(ruleClass);
1791 if (allowed) {
1792 return;
1793 }
1794 }
1795
1796 if (attribute.getAllowedRuleClassesWarningPredicate()
Yun Pengda9410c2016-03-18 21:14:51 +00001797 != Predicates.<RuleClass>alwaysTrue()) {
Greg Estren080ac252016-05-12 20:57:13 +00001798 Predicate<RuleClass> warningPredicate = attribute.getAllowedRuleClassesWarningPredicate();
1799 if (warningPredicate.apply(ruleClass)) {
Lukacs Berki7894c182016-05-10 12:07:01 +00001800 reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(), prerequisite,
Greg Estren080ac252016-05-12 20:57:13 +00001801 "expected " + warningPredicate, true);
Yun Pengefd7ca12016-03-03 13:14:38 +00001802 return;
1803 }
1804 }
1805
Liam Miller-Cushonbbae4d92016-05-03 18:23:03 +00001806 if (!attribute.getMandatoryNativeProviders().isEmpty()) {
1807 String missing = getMissingMandatoryNativeProviders(prerequisite, attribute);
1808 if (missing != null) {
1809 attributeError(
1810 attribute.getName(),
1811 "'" + prerequisite.getLabel() + "' does not have mandatory providers: " + missing);
1812 return;
1813 }
1814 }
1815
Yun Pengefd7ca12016-03-03 13:14:38 +00001816 if (!attribute.getMandatoryProvidersList().isEmpty()) {
1817 String missingMandatoryProviders = getMissingMandatoryProviders(prerequisite, attribute);
1818 if (missingMandatoryProviders != null) {
1819 attributeError(
Yun Pengda9410c2016-03-18 21:14:51 +00001820 attribute.getName(),
1821 "'" + prerequisite.getLabel() + "' does not have mandatory provider "
1822 + missingMandatoryProviders);
Yun Pengefd7ca12016-03-03 13:14:38 +00001823 }
1824 } else if (Boolean.FALSE.equals(allowed)) {
Lukacs Berki7894c182016-05-10 12:07:01 +00001825 reportBadPrerequisite(attribute, prerequisiteTarget.getTargetKind(), prerequisite,
Yun Pengda9410c2016-03-18 21:14:51 +00001826 "expected " + attribute.getAllowedRuleClassesPredicate(), false);
Yun Pengefd7ca12016-03-03 13:14:38 +00001827 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001828 }
1829
1830 private void validateDirectPrerequisite(Attribute attribute, ConfiguredTarget prerequisite) {
1831 validateDirectPrerequisiteType(prerequisite, attribute);
1832 validateDirectPrerequisiteFileTypes(prerequisite, attribute);
Greg Estren875c7a72015-09-24 19:57:54 +00001833 if (attribute.performPrereqValidatorCheck()) {
Ulf Adams0b638972015-09-08 13:25:35 +00001834 prerequisiteValidator.validate(this, prerequisite, attribute);
1835 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001836 }
1837 }
1838
Florian Weikertb8a6a942015-09-25 12:36:08 +00001839 /**
1840 * Helper class for reporting errors and warnings.
1841 */
1842 public static final class ErrorReporter implements RuleErrorConsumer {
1843 private final AnalysisEnvironment env;
1844 private final Rule rule;
Michael Staib8824d5e2016-01-20 21:37:05 +00001845 private final String ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001846
Michael Staib8824d5e2016-01-20 21:37:05 +00001847 ErrorReporter(AnalysisEnvironment env, Rule rule, String ruleClassNameForLogging) {
Florian Weikertb8a6a942015-09-25 12:36:08 +00001848 this.env = env;
1849 this.rule = rule;
Michael Staib8824d5e2016-01-20 21:37:05 +00001850 this.ruleClassNameForLogging = ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001851 }
1852
1853 public void reportError(Location location, String message) {
1854 env.getEventHandler().handle(Event.error(location, message));
1855 }
1856
1857 @Override
1858 public void ruleError(String message) {
1859 reportError(rule.getLocation(), prefixRuleMessage(message));
1860 }
1861
1862 @Override
1863 public void attributeError(String attrName, String message) {
Florian Weikert5c679342015-11-04 16:53:59 +00001864 reportError(rule.getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
Florian Weikertb8a6a942015-09-25 12:36:08 +00001865 }
1866
1867 public void reportWarning(Location location, String message) {
1868 env.getEventHandler().handle(Event.warn(location, message));
1869 }
1870
1871 @Override
1872 public void ruleWarning(String message) {
1873 env.getEventHandler().handle(Event.warn(rule.getLocation(), prefixRuleMessage(message)));
1874 }
1875
1876 @Override
1877 public void attributeWarning(String attrName, String message) {
Florian Weikert5c679342015-11-04 16:53:59 +00001878 reportWarning(
1879 rule.getAttributeLocation(attrName), completeAttributeMessage(attrName, message));
Florian Weikertb8a6a942015-09-25 12:36:08 +00001880 }
1881
1882 private String prefixRuleMessage(String message) {
Michael Staib8824d5e2016-01-20 21:37:05 +00001883 return String.format(
1884 "in %s rule %s: %s", getRuleClassNameForLogging(), rule.getLabel(), message);
Florian Weikertb8a6a942015-09-25 12:36:08 +00001885 }
1886
1887 private String maskInternalAttributeNames(String name) {
1888 return Attribute.isImplicit(name) ? "(an implicit dependency)" : name;
1889 }
1890
1891 /**
1892 * Prefixes the given message with details about the rule and appends details about the macro
1893 * that created this rule, if applicable.
1894 */
1895 private String completeAttributeMessage(String attrName, String message) {
1896 // Appends a note to the given message if the offending rule was created by a macro.
1897 String macroMessageAppendix =
Florian Weikert5c679342015-11-04 16:53:59 +00001898 rule.wasCreatedByMacro()
Florian Weikertb8a6a942015-09-25 12:36:08 +00001899 ? String.format(
1900 ". Since this rule was created by the macro '%s', the error might have been "
1901 + "caused by the macro implementation in %s",
Florian Weikert5c679342015-11-04 16:53:59 +00001902 getGeneratorFunction(), rule.getAttributeLocationWithoutMacro(attrName))
Florian Weikertb8a6a942015-09-25 12:36:08 +00001903 : "";
1904
1905 return String.format("in %s attribute of %s rule %s: %s%s",
Michael Staib8824d5e2016-01-20 21:37:05 +00001906 maskInternalAttributeNames(attrName), getRuleClassNameForLogging(), rule.getLabel(),
1907 message, macroMessageAppendix);
1908 }
1909
1910 /**
1911 * Returns a rule class name suitable for log messages, including an aspect name if applicable.
1912 */
1913 private String getRuleClassNameForLogging() {
1914 return ruleClassNameForLogging;
Florian Weikertb8a6a942015-09-25 12:36:08 +00001915 }
1916
Florian Weikertb8a6a942015-09-25 12:36:08 +00001917 private String getGeneratorFunction() {
1918 return (String) rule.getAttributeContainer().getAttr("generator_function");
1919 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001920 }
1921}