blob: 1d6329a6a1aa3a017741cdc6be858a7de283048f [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.
14package com.google.devtools.build.lib.skyframe;
15
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010016import com.google.common.base.Function;
Greg Estren00049432015-08-25 16:43:47 +000017import com.google.common.base.Verify;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ArrayListMultimap;
Greg Estren00049432015-08-25 16:43:47 +000019import com.google.common.collect.ImmutableList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Iterables;
Greg Estren123de312015-11-17 19:47:58 +000022import com.google.common.collect.LinkedListMultimap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.common.collect.ListMultimap;
Ulf Adams7cb66de2016-01-14 08:46:43 +000024import com.google.common.collect.Maps;
Greg Estren00049432015-08-25 16:43:47 +000025import com.google.common.collect.Multimap;
Janak Ramakrishnanb3a6ca72015-03-27 20:07:28 +000026import com.google.devtools.build.lib.actions.Action;
27import com.google.devtools.build.lib.actions.Actions;
28import com.google.devtools.build.lib.actions.Artifact;
29import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment;
Dmitry Lomovb487ac62015-11-09 13:09:12 +000031import com.google.devtools.build.lib.analysis.ConfiguredAspect;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import com.google.devtools.build.lib.analysis.ConfiguredTarget;
33import com.google.devtools.build.lib.analysis.DependencyResolver.Dependency;
34import com.google.devtools.build.lib.analysis.LabelAndConfiguration;
35import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
36import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
37import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
38import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
Greg Estren00049432015-08-25 16:43:47 +000039import com.google.devtools.build.lib.analysis.config.BuildOptions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
Greg Estren00049432015-08-25 16:43:47 +000041import com.google.devtools.build.lib.analysis.config.HostTransition;
42import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
43import com.google.devtools.build.lib.analysis.config.PatchTransition;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000044import com.google.devtools.build.lib.cmdline.Label;
Marian Loburc62faba2015-09-09 10:08:06 +000045import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
Ulf Adams7cb66de2016-01-14 08:46:43 +000046import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047import com.google.devtools.build.lib.events.Event;
48import com.google.devtools.build.lib.events.StoredEventHandler;
Dmitry Lomovb487ac62015-11-09 13:09:12 +000049import com.google.devtools.build.lib.packages.Aspect;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import com.google.devtools.build.lib.packages.AspectDefinition;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051import com.google.devtools.build.lib.packages.Attribute;
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +000052import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000053import com.google.devtools.build.lib.packages.BuildType;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.packages.NoSuchTargetException;
55import com.google.devtools.build.lib.packages.NoSuchThingException;
Marian Loburc62faba2015-09-09 10:08:06 +000056import com.google.devtools.build.lib.packages.Package;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057import com.google.devtools.build.lib.packages.RawAttributeMapper;
58import com.google.devtools.build.lib.packages.Rule;
Greg Estren00049432015-08-25 16:43:47 +000059import com.google.devtools.build.lib.packages.RuleClassProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.lib.packages.Target;
61import com.google.devtools.build.lib.packages.TargetUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010062import com.google.devtools.build.lib.skyframe.AspectFunction.AspectCreationException;
63import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider;
64import com.google.devtools.build.lib.syntax.EvalException;
Mark Schaller6df81792015-12-10 18:47:47 +000065import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010066import com.google.devtools.build.skyframe.SkyFunction;
67import com.google.devtools.build.skyframe.SkyFunctionException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.skyframe.SkyKey;
69import com.google.devtools.build.skyframe.SkyValue;
Greg Estren00049432015-08-25 16:43:47 +000070import com.google.devtools.build.skyframe.ValueOrException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071import com.google.devtools.build.skyframe.ValueOrException3;
72
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import java.util.Collection;
74import java.util.HashMap;
75import java.util.HashSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076import java.util.Map;
Greg Estren00049432015-08-25 16:43:47 +000077import java.util.Objects;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010078import java.util.Set;
79
80import javax.annotation.Nullable;
81
82/**
83 * SkyFunction for {@link ConfiguredTargetValue}s.
84 */
85final class ConfiguredTargetFunction implements SkyFunction {
86
87 /**
88 * Exception class that signals an error during the evaluation of a dependency.
89 */
90 public static class DependencyEvaluationException extends Exception {
Ulf Adams25f03d82016-01-25 10:31:46 +000091 public DependencyEvaluationException(InvalidConfigurationException cause) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092 super(cause);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093 }
94
Ulf Adams25f03d82016-01-25 10:31:46 +000095 public DependencyEvaluationException(ConfiguredValueCreationException cause) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 super(cause);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097 }
98
99 @Override
Ulf Adams25f03d82016-01-25 10:31:46 +0000100 public synchronized Exception getCause() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100101 return (Exception) super.getCause();
102 }
103 }
104
105 private static final Function<Dependency, SkyKey> TO_KEYS =
106 new Function<Dependency, SkyKey>() {
107 @Override
108 public SkyKey apply(Dependency input) {
109 return ConfiguredTargetValue.key(input.getLabel(), input.getConfiguration());
110 }
111 };
112
113 private final BuildViewProvider buildViewProvider;
Greg Estren00049432015-08-25 16:43:47 +0000114 private final RuleClassProvider ruleClassProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100115
Greg Estren00049432015-08-25 16:43:47 +0000116 ConfiguredTargetFunction(BuildViewProvider buildViewProvider,
117 RuleClassProvider ruleClassProvider) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 this.buildViewProvider = buildViewProvider;
Greg Estren00049432015-08-25 16:43:47 +0000119 this.ruleClassProvider = ruleClassProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 }
121
122 @Override
123 public SkyValue compute(SkyKey key, Environment env) throws ConfiguredTargetFunctionException,
124 InterruptedException {
125 SkyframeBuildView view = buildViewProvider.getSkyframeBuildView();
Marian Loburc62faba2015-09-09 10:08:06 +0000126 NestedSetBuilder<Package> transitivePackages = NestedSetBuilder.stableOrder();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 ConfiguredTargetKey configuredTargetKey = (ConfiguredTargetKey) key.argument();
128 LabelAndConfiguration lc = LabelAndConfiguration.of(
129 configuredTargetKey.getLabel(), configuredTargetKey.getConfiguration());
130
131 BuildConfiguration configuration = lc.getConfiguration();
132
133 PackageValue packageValue =
134 (PackageValue) env.getValue(PackageValue.key(lc.getLabel().getPackageIdentifier()));
135 if (packageValue == null) {
136 return null;
137 }
138
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000139 Package pkg = packageValue.getPackage();
140 if (pkg.containsErrors()) {
141 throw new ConfiguredTargetFunctionException(
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000142 new BuildFileContainsErrorsException(lc.getLabel().getPackageIdentifier()));
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000143 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144 Target target;
145 try {
146 target = packageValue.getPackage().getTarget(lc.getLabel().getName());
Ulf Adams96683612016-01-25 09:04:54 +0000147 } catch (NoSuchTargetException e) {
148 throw new ConfiguredTargetFunctionException(e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100149 }
Marian Loburc62faba2015-09-09 10:08:06 +0000150 transitivePackages.add(packageValue.getPackage());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100151 // TODO(bazel-team): This is problematic - we create the right key, but then end up with a value
152 // that doesn't match; we can even have the same value multiple times. However, I think it's
153 // only triggered in tests (i.e., in normal operation, the configuration passed in is already
154 // null).
Greg Estrena6c88962015-09-28 19:35:18 +0000155 if (!target.isConfigurable()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100156 configuration = null;
157 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158
159 SkyframeDependencyResolver resolver = view.createDependencyResolver(env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100160
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000161 TargetAndConfiguration ctgValue =
162 new TargetAndConfiguration(target, configuration);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163 try {
164 // Get the configuration targets that trigger this rule's configurable attributes.
165 Set<ConfigMatchingProvider> configConditions =
Marian Loburc62faba2015-09-09 10:08:06 +0000166 getConfigConditions(ctgValue.getTarget(), env, resolver, ctgValue, transitivePackages);
Greg Estren00049432015-08-25 16:43:47 +0000167 if (env.valuesMissing()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100168 return null;
169 }
170
Dmitry Lomov6231d082015-11-02 17:17:20 +0000171 ListMultimap<Attribute, ConfiguredTarget> depValueMap =
172 computeDependencies(
173 env,
174 resolver,
175 ctgValue,
176 null,
177 configConditions,
178 ruleClassProvider,
179 view.getHostConfiguration(configuration),
180 transitivePackages);
Greg Estren00049432015-08-25 16:43:47 +0000181 ConfiguredTargetValue ans = createConfiguredTarget(
Marian Loburc62faba2015-09-09 10:08:06 +0000182 view, env, target, configuration, depValueMap, configConditions, transitivePackages);
Greg Estren00049432015-08-25 16:43:47 +0000183 return ans;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 } catch (DependencyEvaluationException e) {
Ulf Adams25f03d82016-01-25 10:31:46 +0000185 if (e.getCause() instanceof ConfiguredValueCreationException) {
186 throw new ConfiguredTargetFunctionException(
187 (ConfiguredValueCreationException) e.getCause());
188 } else {
189 // Cast to InvalidConfigurationException as a consistency check. If you add any
190 // DependencyEvaluationException constructors, you may need to change this code, too.
191 InvalidConfigurationException cause = (InvalidConfigurationException) e.getCause();
192 throw new ConfiguredTargetFunctionException(
193 new ConfiguredValueCreationException(cause.getMessage(), target.getLabel()));
194 }
Marian Loburfc567b32015-09-14 08:44:25 +0000195 } catch (AspectCreationException e) {
Ulf Adams25f03d82016-01-25 10:31:46 +0000196 // getAnalysisRootCause may be null if the analysis of the aspect itself failed.
197 Label rootCause = target.getLabel();
198 if (e.getAnalysisRootCause() != null) {
199 rootCause = e.getAnalysisRootCause();
200 }
Marian Loburfc567b32015-09-14 08:44:25 +0000201 throw new ConfiguredTargetFunctionException(
Ulf Adams25f03d82016-01-25 10:31:46 +0000202 new ConfiguredValueCreationException(e.getMessage(), rootCause));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100203 }
204 }
205
206 /**
207 * Computes the direct dependencies of a node in the configured target graph (a configured
208 * target or an aspect).
209 *
210 * <p>Returns null if Skyframe hasn't evaluated the required dependencies yet. In this case, the
211 * caller should also return null to Skyframe.
Dmitry Lomov6231d082015-11-02 17:17:20 +0000212 * @param env the Skyframe environment
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100213 * @param resolver The dependency resolver
214 * @param ctgValue The label and the configuration of the node
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000215 * @param aspect
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100216 * @param configConditions the configuration conditions for evaluating the attributes of the node
Greg Estren00049432015-08-25 16:43:47 +0000217 * @param ruleClassProvider rule class provider for determining the right configuration fragments
218 * to apply to deps
219 * @param hostConfiguration the host configuration. There's a noticeable performance hit from
220 * instantiating this on demand for every dependency that wants it, so it's best to compute
221 * the host configuration as early as possible and pass this reference to all consumers
Dmitry Lomov6231d082015-11-02 17:17:20 +0000222 * */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100223 @Nullable
224 static ListMultimap<Attribute, ConfiguredTarget> computeDependencies(
Dmitry Lomov6231d082015-11-02 17:17:20 +0000225 Environment env,
226 SkyframeDependencyResolver resolver,
227 TargetAndConfiguration ctgValue,
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000228 Aspect aspect,
Dmitry Lomov6231d082015-11-02 17:17:20 +0000229 Set<ConfigMatchingProvider> configConditions,
230 RuleClassProvider ruleClassProvider,
231 BuildConfiguration hostConfiguration,
232 NestedSetBuilder<Package> transitivePackages)
Florian Weikert4b67d4f2015-09-14 13:35:34 +0000233 throws DependencyEvaluationException, AspectCreationException, InterruptedException {
Greg Estren00049432015-08-25 16:43:47 +0000234 // Create the map from attributes to list of (target, configuration) pairs.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100235 ListMultimap<Attribute, Dependency> depValueNames;
236 try {
Dmitry Lomov6231d082015-11-02 17:17:20 +0000237 depValueNames =
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000238 resolver.dependentNodeMap(ctgValue, hostConfiguration, aspect, configConditions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 } catch (EvalException e) {
Ulf Adams25f03d82016-01-25 10:31:46 +0000240 // EvalException can only be thrown by computed Skylark attributes in the current rule.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100241 env.getListener().handle(Event.error(e.getLocation(), e.getMessage()));
Ulf Adams25f03d82016-01-25 10:31:46 +0000242 throw new DependencyEvaluationException(
243 new ConfiguredValueCreationException(e.print(), ctgValue.getLabel()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100244 }
245
Greg Estren00049432015-08-25 16:43:47 +0000246 // Trim each dep's configuration so it only includes the fragments needed by its transitive
247 // closure (only dynamic configurations support this).
248 if (ctgValue.getConfiguration() != null
249 && ctgValue.getConfiguration().useDynamicConfigurations()) {
250 depValueNames = trimConfigurations(env, ctgValue, depValueNames, hostConfiguration,
251 ruleClassProvider);
252 if (depValueNames == null) {
253 return null;
254 }
255 }
256
257 // Resolve configured target dependencies and handle errors.
Marian Loburc62faba2015-09-09 10:08:06 +0000258 Map<SkyKey, ConfiguredTarget> depValues = resolveConfiguredTargetDependencies(env,
Ulf Adams7cb66de2016-01-14 08:46:43 +0000259 depValueNames.values(), transitivePackages);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100260 if (depValues == null) {
261 return null;
262 }
263
Greg Estren00049432015-08-25 16:43:47 +0000264 // Resolve required aspects.
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000265 ListMultimap<SkyKey, ConfiguredAspect> depAspects =
266 resolveAspectDependencies(env, depValues, depValueNames.values(), transitivePackages);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 if (depAspects == null) {
268 return null;
269 }
270
Greg Estren00049432015-08-25 16:43:47 +0000271 // Merge the dependent configured targets and aspects into a single map.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100272 return mergeAspects(depValueNames, depValues, depAspects);
273 }
274
275 /**
Greg Estren00049432015-08-25 16:43:47 +0000276 * Helper class for {@link #trimConfigurations} - encapsulates a set of config fragments and
277 * a dynamic transition. This can be used to determine the exact build options needed to
278 * set a dynamic configuration.
279 */
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000280 @Immutable
Greg Estren00049432015-08-25 16:43:47 +0000281 private static final class FragmentsAndTransition {
282 // Treat this as immutable. The only reason this isn't an ImmutableSet is because it
283 // gets bound to a NestedSet.toSet() reference, which returns a Set interface.
284 final Set<Class<? extends BuildConfiguration.Fragment>> fragments;
285 final Attribute.Transition transition;
286 private final int hashCode;
287
288 FragmentsAndTransition(Set<Class<? extends BuildConfiguration.Fragment>> fragments,
289 Attribute.Transition transition) {
290 this.fragments = fragments;
291 this.transition = transition;
292 hashCode = Objects.hash(this.fragments, this.transition);
293 }
294
295 @Override
296 public boolean equals(Object o) {
297 if (o == this) {
298 return true;
299 } else if (o == null) {
300 return false;
301 } else {
302 FragmentsAndTransition other = (FragmentsAndTransition) o;
303 return other.transition.equals(transition) && other.fragments.equals(fragments);
304 }
305 }
306
307 @Override
308 public int hashCode() {
309 return hashCode;
310 }
311 }
312
313 /**
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000314 * Helper class for {@link #trimConfigurations} - encapsulates an <attribute, label> pair that
315 * can be used to map from an input dependency to a trimmed dependency.
316 */
317 @Immutable
318 private static final class AttributeAndLabel {
319 final Attribute attribute;
320 final Label label;
321 final int hashCode;
322
323 AttributeAndLabel(Attribute attribute, Label label) {
324 this.attribute = attribute;
325 this.label = label;
326 this.hashCode = Objects.hash(this.attribute, this.label);
327 }
328
329 @Override
330 public boolean equals(Object o) {
331 if (!(o instanceof AttributeAndLabel)) {
332 return false;
333 }
334 AttributeAndLabel other = (AttributeAndLabel) o;
335 return Objects.equals(other.attribute, attribute) && other.label.equals(label);
336 }
337
338 @Override
339 public int hashCode() {
340 return hashCode;
341 }
342 }
343
344 /**
345 * Variation of {@link Map#put} that triggers an exception if another value already exists.
346 */
347 private static <K, V> void putOnlyEntry(Map<K, V> map, K key, V value) {
348 Verify.verify(map.put(key, value) == null,
349 "couldn't insert %s: map already has key %s", value.toString(), key.toString());
350 }
351
352 /**
Greg Estren00049432015-08-25 16:43:47 +0000353 * Creates a dynamic configuration for each dep that's custom-fitted specifically for that dep.
354 *
355 * <p>More specifically: given a set of {@link Dependency} instances holding dynamic config
356 * transition requests (e.g. {@link Dependency#hasStaticConfiguration()} == false}), returns
357 * equivalent dependencies containing dynamically created configurations that a) apply those
358 * transitions and b) only contain the fragments needed by the dep and everything in its
359 * transitive closure.
360 *
361 * <p>This method is heavily performance-optimized. Because it, in aggregate, reads over every
362 * edge in the configured target graph, small inefficiencies can have observable impact on
363 * build analysis time. Keep this in mind when making modifications and performance-test any
364 * changes you make.
365 */
366 @Nullable
367 static ListMultimap<Attribute, Dependency> trimConfigurations(Environment env,
368 TargetAndConfiguration ctgValue, ListMultimap<Attribute, Dependency> originalDeps,
369 BuildConfiguration hostConfiguration, RuleClassProvider ruleClassProvider)
370 throws DependencyEvaluationException {
371
372 // Maps each Skyframe-evaluated BuildConfiguration to the dependencies that need that
373 // configuration. For cases where Skyframe isn't needed to get the configuration (e.g. when
374 // we just re-used the original rule's configuration), we should skip this outright.
Greg Estren123de312015-11-17 19:47:58 +0000375 Multimap<SkyKey, Map.Entry<Attribute, Dependency>> keysToEntries = LinkedListMultimap.create();
Greg Estren00049432015-08-25 16:43:47 +0000376
377 // Stores the result of applying a dynamic transition to the current configuration using a
378 // particular subset of fragments. By caching this, we save from redundantly computing the
379 // same transition for every dependency edge that requests that transition. This can have
380 // real effect on analysis time for commonly triggered transitions.
381 Map<FragmentsAndTransition, BuildOptions> transitionsMap = new HashMap<>();
382
383 // The fragments used by the current target's configuration.
384 Set<Class<? extends BuildConfiguration.Fragment>> ctgFragments =
385 ctgValue.getConfiguration().fragmentClasses();
386 BuildOptions ctgOptions = ctgValue.getConfiguration().getOptions();
387
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000388 // Stores the trimmed versions of each dependency. This method must preserve the original label
389 // ordering of each attribute. For example, if originalDeps.get("data") is [":a", ":b"], the
390 // trimmed variant must also be [":a", ":b"] in the same order. Because we may not actualize
391 // the results in order (some results need Skyframe-evaluated configurations while others can
392 // be computed trivially), we dump them all into this map, then as a final step iterate through
393 // the original list and pluck out values from here for the final value.
394 Map<AttributeAndLabel, Dependency> trimmedDeps = new HashMap<>();
Greg Estren00049432015-08-25 16:43:47 +0000395
396 for (Map.Entry<Attribute, Dependency> depsEntry : originalDeps.entries()) {
397 Dependency dep = depsEntry.getValue();
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000398 AttributeAndLabel attributeAndLabel =
399 new AttributeAndLabel(depsEntry.getKey(), dep.getLabel());
Greg Estren00049432015-08-25 16:43:47 +0000400
401 if (dep.hasStaticConfiguration()) {
402 // Certain targets (like output files) trivially pass their configurations to their deps.
403 // So no need to transform them in any way.
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000404 putOnlyEntry(trimmedDeps, attributeAndLabel,
Greg Estren00049432015-08-25 16:43:47 +0000405 new Dependency(dep.getLabel(), dep.getConfiguration(), dep.getAspects()));
406 continue;
407 } else if (dep.getTransition() == Attribute.ConfigurationTransition.NULL) {
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000408 putOnlyEntry(trimmedDeps, attributeAndLabel,
Greg Estren00049432015-08-25 16:43:47 +0000409 new Dependency(dep.getLabel(), (BuildConfiguration) null, dep.getAspects()));
410 continue;
411 }
412
413 // Figure out the required fragments for this dep and its transitive closure.
414 SkyKey fragmentsKey = TransitiveTargetValue.key(dep.getLabel());
415 TransitiveTargetValue transitiveDepInfo = (TransitiveTargetValue) env.getValue(fragmentsKey);
416 if (transitiveDepInfo == null) {
417 // This should only be possible for tests. In actual runs, this was already called
418 // as a routine part of the loading phase.
419 // TODO(bazel-team): check this only occurs in a test context.
420 return null;
421 }
422 Set<Class<? extends BuildConfiguration.Fragment>> depFragments =
423 transitiveDepInfo.getTransitiveConfigFragments().toSet();
424
425 boolean sameFragments = depFragments.equals(ctgFragments);
426 Attribute.Transition transition = dep.getTransition();
427
428 if (sameFragments) {
429 if (transition == Attribute.ConfigurationTransition.NONE) {
430 // The dep uses the same exact configuration.
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000431 putOnlyEntry(trimmedDeps, attributeAndLabel,
Greg Estren00049432015-08-25 16:43:47 +0000432 new Dependency(dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects()));
433 continue;
434 } else if (transition == HostTransition.INSTANCE) {
435 // The current rule's host configuration can also be used for the dep. We short-circuit
436 // the standard transition logic for host transitions because these transitions are
437 // uniquely frequent. It's possible, e.g., for every node in the configured target graph
438 // to incur multiple host transitions. So we aggressively optimize to avoid hurting
439 // analysis time.
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000440 putOnlyEntry(trimmedDeps, attributeAndLabel,
Greg Estren00049432015-08-25 16:43:47 +0000441 new Dependency(dep.getLabel(), hostConfiguration, dep.getAspects()));
442 continue;
443 }
444 }
445
446 // Apply the transition or use the cached result if it was already applied.
447 FragmentsAndTransition transitionKey = new FragmentsAndTransition(depFragments, transition);
448 BuildOptions toOptions = transitionsMap.get(transitionKey);
449 if (toOptions == null) {
450 Verify.verify(transition == Attribute.ConfigurationTransition.NONE
451 || transition instanceof PatchTransition);
452 BuildOptions fromOptions = ctgOptions;
Greg Estren00049432015-08-25 16:43:47 +0000453 // TODO(bazel-team): safety-check that the below call never mutates fromOptions.
454 toOptions = transition == Attribute.ConfigurationTransition.NONE
455 ? fromOptions
456 : ((PatchTransition) transition).apply(fromOptions);
Greg Estren3f241a42015-11-11 22:34:05 +0000457 if (!sameFragments) {
458 // TODO(bazel-team): pre-compute getOptionsClasses in the constructor.
459 toOptions = toOptions.trim(BuildConfiguration.getOptionsClasses(
460 transitiveDepInfo.getTransitiveConfigFragments(), ruleClassProvider));
461 }
Greg Estren00049432015-08-25 16:43:47 +0000462 transitionsMap.put(transitionKey, toOptions);
463 }
464
465 // If the transition doesn't change the configuration, trivially re-use the original
466 // configuration.
467 if (sameFragments && toOptions.equals(ctgOptions)) {
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000468 putOnlyEntry(trimmedDeps, attributeAndLabel,
Greg Estren00049432015-08-25 16:43:47 +0000469 new Dependency(dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects()));
470 continue;
471 }
472
473 // If we get here, we have to get the configuration from Skyframe.
474 keysToEntries.put(BuildConfigurationValue.key(depFragments, toOptions), depsEntry);
475 }
476
477 // Get all BuildConfigurations we need to get from Skyframe.
478 Map<SkyKey, ValueOrException<InvalidConfigurationException>> depConfigValues =
479 env.getValuesOrThrow(keysToEntries.keySet(), InvalidConfigurationException.class);
480 if (env.valuesMissing()) {
481 return null;
482 }
483
484 // Now fill in the remaining unresolved deps with the now-resolved configurations.
485 try {
486 for (Map.Entry<SkyKey, ValueOrException<InvalidConfigurationException>> entry :
487 depConfigValues.entrySet()) {
488 SkyKey key = entry.getKey();
489 BuildConfigurationValue trimmedConfig = (BuildConfigurationValue) entry.getValue().get();
490 for (Map.Entry<Attribute, Dependency> info : keysToEntries.get(key)) {
Greg Estren00049432015-08-25 16:43:47 +0000491 Dependency originalDep = info.getValue();
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000492 putOnlyEntry(trimmedDeps, new AttributeAndLabel(info.getKey(), originalDep.getLabel()),
Greg Estren00049432015-08-25 16:43:47 +0000493 new Dependency(originalDep.getLabel(), trimmedConfig.getConfiguration(),
494 originalDep.getAspects()));
495 }
496 }
497 } catch (InvalidConfigurationException e) {
498 throw new DependencyEvaluationException(e);
499 }
500
Greg Estrene5c9dbe02015-11-18 20:47:43 +0000501 // Re-assemble the output map with the same value ordering (e.g. each attribute's dep labels
502 // appear in the same order) as the input.
503 ListMultimap<Attribute, Dependency> result = ArrayListMultimap.create();
504 for (Map.Entry<Attribute, Dependency> depsEntry : originalDeps.entries()) {
505 Dependency trimmedDep = Verify.verifyNotNull(
506 trimmedDeps.get(
507 new AttributeAndLabel(depsEntry.getKey(), depsEntry.getValue().getLabel())));
508 result.put(depsEntry.getKey(), trimmedDep);
509 }
510 return result;
Greg Estren00049432015-08-25 16:43:47 +0000511 }
512
513 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100514 * Merges the each direct dependency configured target with the aspects associated with it.
515 *
516 * <p>Note that the combination of a configured target and its associated aspects are not
517 * represented by a Skyframe node. This is because there can possibly be many different
518 * combinations of aspects for a particular configured target, so it would result in a
519 * combinatiorial explosion of Skyframe nodes.
520 */
521 private static ListMultimap<Attribute, ConfiguredTarget> mergeAspects(
522 ListMultimap<Attribute, Dependency> depValueNames,
523 Map<SkyKey, ConfiguredTarget> depConfiguredTargetMap,
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000524 ListMultimap<SkyKey, ConfiguredAspect> depAspectMap) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100525 ListMultimap<Attribute, ConfiguredTarget> result = ArrayListMultimap.create();
526
527 for (Map.Entry<Attribute, Dependency> entry : depValueNames.entries()) {
528 Dependency dep = entry.getValue();
529 SkyKey depKey = TO_KEYS.apply(dep);
530 ConfiguredTarget depConfiguredTarget = depConfiguredTargetMap.get(depKey);
Lukacs Berki047b7422015-07-22 08:32:37 +0000531 result.put(entry.getKey(),
532 RuleConfiguredTarget.mergeAspects(depConfiguredTarget, depAspectMap.get(depKey)));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100533 }
534
535 return result;
536 }
537
538 /**
539 * Given a list of {@link Dependency} objects, returns a multimap from the {@link SkyKey} of the
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000540 * dependency to the {@link ConfiguredAspect} instances that should be merged into it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100541 *
542 * <p>Returns null if the required aspects are not computed yet.
543 */
544 @Nullable
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000545 private static ListMultimap<SkyKey, ConfiguredAspect> resolveAspectDependencies(
546 Environment env,
547 Map<SkyKey, ConfiguredTarget> configuredTargetMap,
548 Iterable<Dependency> deps,
Marian Loburc62faba2015-09-09 10:08:06 +0000549 NestedSetBuilder<Package> transitivePackages)
Marian Loburfc567b32015-09-14 08:44:25 +0000550 throws AspectCreationException {
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000551 ListMultimap<SkyKey, ConfiguredAspect> result = ArrayListMultimap.create();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100552 Set<SkyKey> aspectKeys = new HashSet<>();
553 for (Dependency dep : deps) {
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000554 for (Aspect depAspect : dep.getAspects()) {
Marian Lobur702cad72015-09-02 09:53:58 +0000555 aspectKeys.add(createAspectKey(dep.getLabel(), dep.getConfiguration(), depAspect));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100556 }
557 }
558
559 Map<SkyKey, ValueOrException3<
560 AspectCreationException, NoSuchThingException, ConfiguredValueCreationException>>
561 depAspects = env.getValuesOrThrow(aspectKeys, AspectCreationException.class,
562 NoSuchThingException.class, ConfiguredValueCreationException.class);
563
564 for (Dependency dep : deps) {
565 SkyKey depKey = TO_KEYS.apply(dep);
Marian Lobur2099da02015-05-12 14:42:01 +0000566 // If the same target was declared in different attributes of rule, we should not process it
567 // twice.
568 if (result.containsKey(depKey)) {
569 continue;
570 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100571 ConfiguredTarget depConfiguredTarget = configuredTargetMap.get(depKey);
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000572 for (Aspect depAspect : dep.getAspects()) {
Dmitry Lomov6231d082015-11-02 17:17:20 +0000573 if (!aspectMatchesConfiguredTarget(depConfiguredTarget, depAspect)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100574 continue;
575 }
576
Marian Lobur702cad72015-09-02 09:53:58 +0000577 SkyKey aspectKey = createAspectKey(dep.getLabel(), dep.getConfiguration(), depAspect);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100578 AspectValue aspectValue = null;
579 try {
580 aspectValue = (AspectValue) depAspects.get(aspectKey).get();
581 } catch (ConfiguredValueCreationException e) {
582 // The configured target should have been created in resolveConfiguredTargetDependencies()
583 throw new IllegalStateException(e);
Marian Loburfc567b32015-09-14 08:44:25 +0000584 } catch (NoSuchThingException e) {
Marian Loburfc567b32015-09-14 08:44:25 +0000585 throw new AspectCreationException(
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000586 String.format(
587 "Evaluation of aspect %s on %s failed: %s",
Dmitry Lomov6231d082015-11-02 17:17:20 +0000588 depAspect.getDefinition().getName(),
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000589 dep.getLabel(),
590 e.toString()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100591 }
592
593 if (aspectValue == null) {
594 // Dependent aspect has either not been computed yet or is in error.
595 return null;
596 }
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000597 result.put(depKey, aspectValue.getConfiguredAspect());
Marian Loburc62faba2015-09-09 10:08:06 +0000598 transitivePackages.addTransitive(aspectValue.getTransitivePackages());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 }
600 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100601 return result;
602 }
603
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000604 public static SkyKey createAspectKey(
605 Label label, BuildConfiguration buildConfiguration, Aspect depAspect) {
Marian Lobur702cad72015-09-02 09:53:58 +0000606 return AspectValue.key(label,
607 buildConfiguration,
Dmitry Lomov8ff06452015-10-21 19:16:30 +0000608 depAspect.getAspectClass(),
Marian Lobur702cad72015-09-02 09:53:58 +0000609 depAspect.getParameters());
610 }
611
Dmitry Lomovb487ac62015-11-09 13:09:12 +0000612 private static boolean aspectMatchesConfiguredTarget(ConfiguredTarget dep, Aspect aspectClass) {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000613 AspectDefinition aspectDefinition = aspectClass.getDefinition();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100614 for (Class<?> provider : aspectDefinition.getRequiredProviders()) {
Dmitry Lomove2033b12015-08-19 16:57:49 +0000615 if (dep.getProvider(provider.asSubclass(TransitiveInfoProvider.class)) == null) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100616 return false;
617 }
618 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100619 return true;
620 }
621
622 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100623 * Returns the set of {@link ConfigMatchingProvider}s that key the configurable attributes
624 * used by this rule.
625 *
626 * <p>>If the configured targets supplying those providers aren't yet resolved by the
627 * dependency resolver, returns null.
628 */
629 @Nullable
630 static Set<ConfigMatchingProvider> getConfigConditions(Target target, Environment env,
Marian Loburc62faba2015-09-09 10:08:06 +0000631 SkyframeDependencyResolver resolver, TargetAndConfiguration ctgValue,
632 NestedSetBuilder<Package> transitivePackages)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100633 throws DependencyEvaluationException {
634 if (!(target instanceof Rule)) {
635 return ImmutableSet.of();
636 }
637
638 ImmutableSet.Builder<ConfigMatchingProvider> configConditions = ImmutableSet.builder();
639
640 // Collect the labels of the configured targets we need to resolve.
641 ListMultimap<Attribute, LabelAndConfiguration> configLabelMap = ArrayListMultimap.create();
642 RawAttributeMapper attributeMap = RawAttributeMapper.of(((Rule) target));
643 for (Attribute a : ((Rule) target).getAttributes()) {
644 for (Label configLabel : attributeMap.getConfigurabilityKeys(a.getName(), a.getType())) {
Lukacs Berkiffa73ad2015-09-18 11:40:12 +0000645 if (!BuildType.Selector.isReservedLabel(configLabel)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100646 configLabelMap.put(a, LabelAndConfiguration.of(
Lukacs Berkifc2a01b2016-01-14 11:56:32 +0000647 target.getLabel().resolveRepositoryRelative(configLabel),
648 ctgValue.getConfiguration()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100649 }
650 }
651 }
652 if (configLabelMap.isEmpty()) {
653 return ImmutableSet.of();
654 }
655
656 // Collect the corresponding Skyframe configured target values. Abort early if they haven't
657 // been computed yet.
Marian Lobur702cad72015-09-02 09:53:58 +0000658 Collection<Dependency> configValueNames = resolver.resolveRuleLabels(ctgValue, configLabelMap);
Greg Estren00049432015-08-25 16:43:47 +0000659
660 // No need to get new configs from Skyframe - config_setting rules always use the current
661 // target's config.
662 // TODO(bazel-team): remove the need for this special transformation. We can probably do this by
663 // simply passing this through trimConfigurations.
664 BuildConfiguration targetConfig = ctgValue.getConfiguration();
665 if (targetConfig != null && targetConfig.useDynamicConfigurations()) {
666 ImmutableList.Builder<Dependency> staticConfigs = ImmutableList.builder();
667 for (Dependency dep : configValueNames) {
668 staticConfigs.add(new Dependency(dep.getLabel(), targetConfig, dep.getAspects()));
669 }
670 configValueNames = staticConfigs.build();
671 }
672
Ulf Adams7cb66de2016-01-14 08:46:43 +0000673 Map<SkyKey, ConfiguredTarget> configValues = resolveConfiguredTargetDependencies(
674 env, configValueNames, transitivePackages);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100675 if (configValues == null) {
676 return null;
677 }
678
679 // Get the configured targets as ConfigMatchingProvider interfaces.
680 for (Dependency entry : configValueNames) {
681 ConfiguredTarget value = configValues.get(TO_KEYS.apply(entry));
682 // The code above guarantees that value is non-null here.
683 ConfigMatchingProvider provider = value.getProvider(ConfigMatchingProvider.class);
684 if (provider != null) {
685 configConditions.add(provider);
686 } else {
687 // Not a valid provider for configuration conditions.
688 String message =
689 entry.getLabel() + " is not a valid configuration key for " + target.getLabel();
690 env.getListener().handle(Event.error(TargetUtils.getLocationMaybe(target), message));
Ulf Adams25f03d82016-01-25 10:31:46 +0000691 throw new DependencyEvaluationException(new ConfiguredValueCreationException(
692 message, target.getLabel()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100693 }
694 }
695
696 return configConditions.build();
697 }
698
699 /***
Ulf Adams7cb66de2016-01-14 08:46:43 +0000700 * Resolves the targets referenced in depValueNames and returns their ConfiguredTarget instances.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100701 *
702 * <p>Returns null if not all instances are available yet.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100703 */
704 @Nullable
705 private static Map<SkyKey, ConfiguredTarget> resolveConfiguredTargetDependencies(
Ulf Adams25f03d82016-01-25 10:31:46 +0000706 Environment env, Collection<Dependency> deps, NestedSetBuilder<Package> transitivePackages)
707 throws DependencyEvaluationException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100708 boolean ok = !env.valuesMissing();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100709 Iterable<SkyKey> depKeys = Iterables.transform(deps, TO_KEYS);
Ulf Adams25f03d82016-01-25 10:31:46 +0000710 Map<SkyKey, ValueOrException<ConfiguredValueCreationException>> depValues =
711 env.getValuesOrThrow(depKeys, ConfiguredValueCreationException.class);
Ulf Adams7cb66de2016-01-14 08:46:43 +0000712 Map<SkyKey, ConfiguredTarget> result = Maps.newHashMapWithExpectedSize(depValues.size());
Ulf Adams25f03d82016-01-25 10:31:46 +0000713 for (Map.Entry<SkyKey, ValueOrException<ConfiguredValueCreationException>> entry :
714 depValues.entrySet()) {
715 ConfiguredTargetValue depValue;
716 try {
717 depValue = (ConfiguredTargetValue) entry.getValue().get();
718 } catch (ConfiguredValueCreationException e) {
719 throw new DependencyEvaluationException(e);
720 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100721 if (depValue == null) {
722 ok = false;
723 } else {
Ulf Adams7cb66de2016-01-14 08:46:43 +0000724 result.put(entry.getKey(), depValue.getConfiguredTarget());
Marian Loburc62faba2015-09-09 10:08:06 +0000725 transitivePackages.addTransitive(depValue.getTransitivePackages());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100726 }
727 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100728 if (!ok) {
729 return null;
730 } else {
Ulf Adams7cb66de2016-01-14 08:46:43 +0000731 return result;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100732 }
733 }
734
735
736 @Override
737 public String extractTag(SkyKey skyKey) {
738 return Label.print(((ConfiguredTargetKey) skyKey.argument()).getLabel());
739 }
740
741 @Nullable
742 private ConfiguredTargetValue createConfiguredTarget(SkyframeBuildView view,
743 Environment env, Target target, BuildConfiguration configuration,
Greg Estren00049432015-08-25 16:43:47 +0000744 ListMultimap<Attribute, ConfiguredTarget> depValueMap,
Marian Loburc62faba2015-09-09 10:08:06 +0000745 Set<ConfigMatchingProvider> configConditions,
746 NestedSetBuilder<Package> transitivePackages)
Greg Estren9eb1cf02015-06-26 22:18:35 +0000747 throws ConfiguredTargetFunctionException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100748 StoredEventHandler events = new StoredEventHandler();
749 BuildConfiguration ownerConfig = (configuration == null)
750 ? null : configuration.getArtifactOwnerConfiguration();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100751 CachingAnalysisEnvironment analysisEnvironment = view.createAnalysisEnvironment(
752 new ConfiguredTargetKey(target.getLabel(), ownerConfig), false,
Ulf Adamsbfb76a92015-04-22 20:44:35 +0000753 events, env, configuration);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100754 if (env.valuesMissing()) {
755 return null;
756 }
757
758 ConfiguredTarget configuredTarget = view.createConfiguredTarget(target, configuration,
Greg Estren00049432015-08-25 16:43:47 +0000759 analysisEnvironment, depValueMap, configConditions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100760
761 events.replayOn(env.getListener());
762 if (events.hasErrors()) {
763 analysisEnvironment.disable(target);
764 throw new ConfiguredTargetFunctionException(new ConfiguredValueCreationException(
Ulf Adams25f03d82016-01-25 10:31:46 +0000765 "Analysis of target '" + target.getLabel() + "' failed; build aborted",
766 target.getLabel()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100767 }
768 Preconditions.checkState(!analysisEnvironment.hasErrors(),
769 "Analysis environment hasError() but no errors reported");
770 if (env.valuesMissing()) {
771 return null;
772 }
773
774 analysisEnvironment.disable(target);
775 Preconditions.checkNotNull(configuredTarget, target);
776
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000777 Map<Artifact, Action> generatingActions;
778 // Check for conflicting actions within this configured target (that indicates a bug in the
779 // rule implementation).
Janak Ramakrishnanb3a6ca72015-03-27 20:07:28 +0000780 try {
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000781 generatingActions = filterSharedActionsAndThrowIfConflict(analysisEnvironment.getRegisteredActions());
Janak Ramakrishnanb3a6ca72015-03-27 20:07:28 +0000782 } catch (ActionConflictException e) {
783 throw new ConfiguredTargetFunctionException(e);
784 }
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000785 return new ConfiguredTargetValue(
786 configuredTarget, generatingActions, transitivePackages.build());
Janak Ramakrishnanb3a6ca72015-03-27 20:07:28 +0000787 }
788
789 static Map<Artifact, Action> filterSharedActionsAndThrowIfConflict(Iterable<Action> actions)
790 throws ActionConflictException {
791 Map<Artifact, Action> generatingActions = new HashMap<>();
792 for (Action action : actions) {
793 for (Artifact artifact : action.getOutputs()) {
794 Action previousAction = generatingActions.put(artifact, action);
795 if (previousAction != null && previousAction != action
796 && !Actions.canBeShared(previousAction, action)) {
797 throw new ActionConflictException(artifact, previousAction, action);
798 }
799 }
800 }
801 return generatingActions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100802 }
803
804 /**
805 * An exception indicating that there was a problem during the construction of
806 * a ConfiguredTargetValue.
807 */
808 public static final class ConfiguredValueCreationException extends Exception {
Ulf Adams25f03d82016-01-25 10:31:46 +0000809 // TODO(ulfjack): Collect all analysis root causes, not just the first one.
810 private final Label analysisRootCause;
811
812 public ConfiguredValueCreationException(String message, Label currentTarget) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100813 super(message);
Ulf Adams25f03d82016-01-25 10:31:46 +0000814 this.analysisRootCause = Preconditions.checkNotNull(currentTarget);
815 }
816
817 public Label getAnalysisRootCause() {
818 return analysisRootCause;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100819 }
820 }
821
822 /**
823 * Used to declare all the exception types that can be wrapped in the exception thrown by
824 * {@link ConfiguredTargetFunction#compute}.
825 */
826 public static final class ConfiguredTargetFunctionException extends SkyFunctionException {
Ulf Adamsd55d7af2016-01-19 11:03:22 +0000827 public ConfiguredTargetFunctionException(NoSuchThingException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100828 super(e, Transience.PERSISTENT);
829 }
830
Ulf Adams25f03d82016-01-25 10:31:46 +0000831 private ConfiguredTargetFunctionException(ConfiguredValueCreationException e) {
832 super(e, Transience.PERSISTENT);
Ulf Adams3ab82f72015-09-04 12:10:53 +0000833 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100834
Janak Ramakrishnanb3a6ca72015-03-27 20:07:28 +0000835 private ConfiguredTargetFunctionException(ActionConflictException e) {
836 super(e, Transience.PERSISTENT);
837 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100838 }
839}