blob: 7215ffef01cf7b64369f4c7154116dea7beb9af5 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2015 The Bazel Authors. All rights reserved.
Mark Schallerb25759c2015-07-29 17:09:18 +00002//
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
shreyax246f0aa2018-02-23 07:46:54 -080016import com.google.common.annotations.VisibleForTesting;
tomlua155b532017-11-08 20:12:47 +010017import com.google.common.base.Preconditions;
michajlo2266a6f2017-08-04 18:49:49 +020018import com.google.common.collect.Collections2;
19import com.google.common.collect.ImmutableSet;
Mark Schallerb25759c2015-07-29 17:09:18 +000020import com.google.common.collect.Lists;
Eric Fellheimere27d0632015-09-25 21:35:26 +000021import com.google.common.collect.Multimap;
michajlof3e91b22017-08-08 00:33:26 +020022import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000023import com.google.devtools.build.lib.cmdline.Label;
Mark Schallerb25759c2015-07-29 17:09:18 +000024import com.google.devtools.build.lib.events.EventHandler;
Eric Fellheimere27d0632015-09-25 21:35:26 +000025import com.google.devtools.build.lib.packages.Attribute;
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +000026import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
Dmitry Lomov6073eb62016-01-21 21:26:32 +000027import com.google.devtools.build.lib.packages.DependencyFilter;
Mark Schallerb25759c2015-07-29 17:09:18 +000028import com.google.devtools.build.lib.packages.InputFile;
29import com.google.devtools.build.lib.packages.NoSuchPackageException;
30import com.google.devtools.build.lib.packages.NoSuchTargetException;
Mark Schallerb25759c2015-07-29 17:09:18 +000031import com.google.devtools.build.lib.packages.OutputFile;
32import com.google.devtools.build.lib.packages.Package;
33import com.google.devtools.build.lib.packages.PackageGroup;
34import com.google.devtools.build.lib.packages.Rule;
35import com.google.devtools.build.lib.packages.Target;
Mark Schallerb25759c2015-07-29 17:09:18 +000036import com.google.devtools.build.skyframe.SkyFunction;
37import com.google.devtools.build.skyframe.SkyFunctionException;
38import com.google.devtools.build.skyframe.SkyKey;
39import com.google.devtools.build.skyframe.SkyValue;
40import com.google.devtools.build.skyframe.ValueOrException2;
Eric Fellheimere27d0632015-09-25 21:35:26 +000041import java.util.Collection;
42import java.util.HashMap;
Mark Schallerb25759c2015-07-29 17:09:18 +000043import java.util.HashSet;
44import java.util.List;
Eric Fellheimere27d0632015-09-25 21:35:26 +000045import java.util.Map;
Mark Schallerb25759c2015-07-29 17:09:18 +000046import java.util.Set;
Mark Schallerb25759c2015-07-29 17:09:18 +000047import javax.annotation.Nullable;
48
49/**
50 * This class can be extended to define {@link SkyFunction}s that traverse a target and its
51 * transitive dependencies and return values based on that traversal.
52 *
53 * <p>The {@code TProcessedTargets} type parameter represents the result of processing a target and
54 * its transitive dependencies.
55 *
56 * <p>{@code TransitiveBaseTraversalFunction} asks for one to be constructed via {@link
57 * #processTarget}, and then asks for it to be updated based on the current target's
58 * attributes' dependencies via {@link #processDeps}, and then asks for it to be updated based
59 * on the current target' aspects' dependencies via {@link #processDeps}. Finally, it calls
60 * {@link #computeSkyValue} with the {#code ProcessedTargets} to get the {@link SkyValue} to
61 * return.
62 */
Mark Schaller6b6d8a92015-10-23 01:00:28 +000063abstract class TransitiveBaseTraversalFunction<TProcessedTargets> implements SkyFunction {
Mark Schallerb25759c2015-07-29 17:09:18 +000064 /**
65 * Returns a {@link SkyKey} corresponding to the traversal of a target specified by {@code label}
66 * and its transitive dependencies.
67 *
68 * <p>Extenders of this class should implement this function to return a key with their
69 * specialized {@link SkyFunction}'s name.
70 *
71 * <p>{@link TransitiveBaseTraversalFunction} calls this for each dependency of a target, and
72 * then gets their values from the environment.
73 *
74 * <p>The key's {@link SkyFunction} may throw at most {@link NoSuchPackageException} and
75 * {@link NoSuchTargetException}. Other exception types are not handled by {@link
76 * TransitiveBaseTraversalFunction}.
77 */
78 abstract SkyKey getKey(Label label);
79
Mark Schaller6b6d8a92015-10-23 01:00:28 +000080 abstract TProcessedTargets processTarget(Label label, TargetAndErrorIfAny targetAndErrorIfAny);
Mark Schallerb25759c2015-07-29 17:09:18 +000081
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000082 abstract void processDeps(
83 TProcessedTargets processedTargets,
84 EventHandler eventHandler,
Mark Schallerb25759c2015-07-29 17:09:18 +000085 TargetAndErrorIfAny targetAndErrorIfAny,
jcatercecb3a82018-05-01 14:37:48 -070086 Iterable<Map.Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000087 depEntries)
88 throws InterruptedException;
Mark Schallerb25759c2015-07-29 17:09:18 +000089
90 /**
91 * Returns a {@link SkyValue} based on the target and any errors it has, and the values
92 * accumulated across it and a traversal of its transitive dependencies.
93 */
94 abstract SkyValue computeSkyValue(TargetAndErrorIfAny targetAndErrorIfAny,
95 TProcessedTargets processedTargets);
96
Mark Schaller6b6d8a92015-10-23 01:00:28 +000097 /**
98 * Returns a {@link TargetMarkerValue} corresponding to the {@param targetMarkerKey} or {@code
99 * null} if the value isn't ready.
100 */
101 @Nullable
102 abstract TargetMarkerValue getTargetMarkerValue(SkyKey targetMarkerKey, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000103 throws NoSuchTargetException, NoSuchPackageException, InterruptedException;
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000104
ulfjackdc73a1d2017-10-30 07:05:53 -0400105 abstract Label argumentFromKey(SkyKey key);
106
Mark Schallerb25759c2015-07-29 17:09:18 +0000107 @Override
108 public SkyValue compute(SkyKey key, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000109 throws TransitiveBaseTraversalFunctionException, InterruptedException {
ulfjackdc73a1d2017-10-30 07:05:53 -0400110 Label label = argumentFromKey(key);
Mark Schallerb25759c2015-07-29 17:09:18 +0000111 LoadTargetResults loadTargetResults;
112 try {
113 loadTargetResults = loadTarget(env, label);
114 } catch (NoSuchTargetException e) {
115 throw new TransitiveBaseTraversalFunctionException(e);
116 } catch (NoSuchPackageException e) {
117 throw new TransitiveBaseTraversalFunctionException(e);
118 }
119 LoadTargetResultsType loadTargetResultsType = loadTargetResults.getType();
120 if (loadTargetResultsType.equals(LoadTargetResultsType.VALUES_MISSING)) {
121 return null;
122 }
123 Preconditions.checkState(
124 loadTargetResultsType.equals(LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY),
125 loadTargetResultsType);
126 TargetAndErrorIfAny targetAndErrorIfAny = (TargetAndErrorIfAny) loadTargetResults;
Mark Schallerb25759c2015-07-29 17:09:18 +0000127
shreyax246f0aa2018-02-23 07:46:54 -0800128 // Process deps from attributes. It is essential that the last getValue(s) call we made to
129 // skyframe for building this node was for the corresponding PackageValue.
130 Collection<SkyKey> labelDepKeys = getLabelDepKeys(env, targetAndErrorIfAny);
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000131
Eric Fellheimere27d0632015-09-25 21:35:26 +0000132 Map<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> depMap =
133 env.getValuesOrThrow(labelDepKeys, NoSuchPackageException.class,
134 NoSuchTargetException.class);
Mark Schallerb25759c2015-07-29 17:09:18 +0000135 if (env.valuesMissing()) {
136 return null;
137 }
shreyax246f0aa2018-02-23 07:46:54 -0800138 // Process deps from attributes. It is essential that the second-to-last getValue(s) call we
139 // made to skyframe for building this node was for the corresponding PackageValue.
Eric Fellheimere27d0632015-09-25 21:35:26 +0000140 Iterable<SkyKey> labelAspectKeys =
shreyax246f0aa2018-02-23 07:46:54 -0800141 getStrictLabelAspectDepKeys(env, depMap, targetAndErrorIfAny);
jcatercecb3a82018-05-01 14:37:48 -0700142 Set<Map.Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
143 labelAspectEntries =
144 env.getValuesOrThrow(
145 labelAspectKeys, NoSuchPackageException.class, NoSuchTargetException.class)
146 .entrySet();
Mark Schallerb25759c2015-07-29 17:09:18 +0000147 if (env.valuesMissing()) {
148 return null;
149 }
150
shreyax7b1b4a42018-02-20 15:35:48 -0800151 TProcessedTargets processedTargets = processTarget(label, targetAndErrorIfAny);
152 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, depMap.entrySet());
153 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, labelAspectEntries);
154
Mark Schallerb25759c2015-07-29 17:09:18 +0000155 return computeSkyValue(targetAndErrorIfAny, processedTargets);
156 }
157
shreyax246f0aa2018-02-23 07:46:54 -0800158 Collection<SkyKey> getLabelDepKeys(
159 SkyFunction.Environment env, TargetAndErrorIfAny targetAndErrorIfAny)
160 throws InterruptedException {
161 return Collections2.transform(getLabelDeps(targetAndErrorIfAny.getTarget()), this::getKey);
162 }
163
164 Iterable<SkyKey> getStrictLabelAspectDepKeys(
165 SkyFunction.Environment env,
166 Map<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> depMap,
167 TargetAndErrorIfAny targetAndErrorIfAny)
168 throws InterruptedException {
169 return getStrictLabelAspectKeys(targetAndErrorIfAny.getTarget(), depMap, env);
170 }
171
Mark Schallerb25759c2015-07-29 17:09:18 +0000172 @Override
173 public String extractTag(SkyKey skyKey) {
ulfjackdc73a1d2017-10-30 07:05:53 -0400174 return Label.print(argumentFromKey(skyKey));
Mark Schallerb25759c2015-07-29 17:09:18 +0000175 }
176
Eric Fellheimerb5c98842015-08-12 23:24:21 +0000177 /**
178 * Return an Iterable of SkyKeys corresponding to the Aspect-related dependencies of target.
179 *
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000180 * <p>This method may return a precise set of aspect keys, but may need to request additional
181 * dependencies from the env to do so.
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000182 */
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000183 private Iterable<SkyKey> getStrictLabelAspectKeys(
184 Target target,
185 Map<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> depMap,
186 Environment env)
187 throws InterruptedException {
Eric Fellheimere27d0632015-09-25 21:35:26 +0000188 List<SkyKey> depKeys = Lists.newArrayList();
189 if (target instanceof Rule) {
190 Map<Label, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> labelDepMap =
191 new HashMap<>(depMap.size());
jcatercecb3a82018-05-01 14:37:48 -0700192 for (Map.Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>
193 entry : depMap.entrySet()) {
ulfjackdc73a1d2017-10-30 07:05:53 -0400194 labelDepMap.put(argumentFromKey(entry.getKey()), entry.getValue());
Eric Fellheimere27d0632015-09-25 21:35:26 +0000195 }
196
197 Multimap<Attribute, Label> transitions =
Dmitry Lomov6073eb62016-01-21 21:26:32 +0000198 ((Rule) target).getTransitions(DependencyFilter.NO_NODEP_ATTRIBUTES);
jcatercecb3a82018-05-01 14:37:48 -0700199 for (Map.Entry<Attribute, Label> entry : transitions.entries()) {
Eric Fellheimere27d0632015-09-25 21:35:26 +0000200 ValueOrException2<NoSuchPackageException, NoSuchTargetException> value =
201 labelDepMap.get(entry.getValue());
202 for (Label label :
Dmitry Lomov6231d082015-11-02 17:17:20 +0000203 getAspectLabels((Rule) target, entry.getKey(), entry.getValue(), value, env)) {
Eric Fellheimere27d0632015-09-25 21:35:26 +0000204 depKeys.add(getKey(label));
205 }
206 }
207 }
208 return depKeys;
209 }
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000210
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000211 /** Get the Aspect-related Label deps for the given edge. */
212 protected abstract Collection<Label> getAspectLabels(
Dmitry Lomov6231d082015-11-02 17:17:20 +0000213 Rule fromRule,
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000214 Attribute attr,
215 Label toLabel,
216 ValueOrException2<NoSuchPackageException, NoSuchTargetException> toVal,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000217 Environment env)
218 throws InterruptedException;
Mark Schallerb25759c2015-07-29 17:09:18 +0000219
Mark Schallerb25759c2015-07-29 17:09:18 +0000220 // TODO(bazel-team): Unify this logic with that in LabelVisitor, and possibly DependencyResolver.
michajlo2266a6f2017-08-04 18:49:49 +0200221 private static Collection<Label> getLabelDeps(Target target) throws InterruptedException {
Mark Schallerb25759c2015-07-29 17:09:18 +0000222 if (target instanceof OutputFile) {
223 Rule rule = ((OutputFile) target).getGeneratingRule();
michajlo2266a6f2017-08-04 18:49:49 +0200224 List<Label> visibilityLabels = visitTargetVisibility(target);
michajlof3e91b22017-08-08 00:33:26 +0200225 HashSet<Label> result = Sets.newHashSetWithExpectedSize(visibilityLabels.size() + 1);
michajlo2266a6f2017-08-04 18:49:49 +0200226 result.add(rule.getLabel());
227 result.addAll(visibilityLabels);
228 return result;
Mark Schallerb25759c2015-07-29 17:09:18 +0000229 } else if (target instanceof InputFile) {
michajlo2266a6f2017-08-04 18:49:49 +0200230 return new HashSet<>(visitTargetVisibility(target));
Mark Schallerb25759c2015-07-29 17:09:18 +0000231 } else if (target instanceof Rule) {
michajlo2266a6f2017-08-04 18:49:49 +0200232 List<Label> visibilityLabels = visitTargetVisibility(target);
233 Collection<Label> ruleLabels = visitRule(target);
michajlof3e91b22017-08-08 00:33:26 +0200234 HashSet<Label> result =
235 Sets.newHashSetWithExpectedSize(visibilityLabels.size() + ruleLabels.size());
michajlo2266a6f2017-08-04 18:49:49 +0200236 result.addAll(visibilityLabels);
237 result.addAll(ruleLabels);
238 return result;
Mark Schallerb25759c2015-07-29 17:09:18 +0000239 } else if (target instanceof PackageGroup) {
michajlo2266a6f2017-08-04 18:49:49 +0200240 return new HashSet<>(visitPackageGroup((PackageGroup) target));
241 } else {
242 return ImmutableSet.of();
Mark Schallerb25759c2015-07-29 17:09:18 +0000243 }
Mark Schallerb25759c2015-07-29 17:09:18 +0000244 }
245
michajlo2266a6f2017-08-04 18:49:49 +0200246 private static Collection<Label> visitRule(Target target) throws InterruptedException {
247 return ((Rule) target).getTransitions(DependencyFilter.NO_NODEP_ATTRIBUTES).values();
Mark Schallerb25759c2015-07-29 17:09:18 +0000248 }
249
michajlo2266a6f2017-08-04 18:49:49 +0200250 private static List<Label> visitTargetVisibility(Target target) {
251 return target.getVisibility().getDependencyLabels();
Mark Schallerb25759c2015-07-29 17:09:18 +0000252 }
253
michajlo2266a6f2017-08-04 18:49:49 +0200254 private static List<Label> visitPackageGroup(PackageGroup packageGroup) {
255 return packageGroup.getIncludes();
Mark Schallerb25759c2015-07-29 17:09:18 +0000256 }
257
Mark Schallerb25759c2015-07-29 17:09:18 +0000258 enum LoadTargetResultsType {
259 VALUES_MISSING,
260 TARGET_AND_ERROR_IF_ANY
261 }
262
263 interface LoadTargetResults {
264 LoadTargetResultsType getType();
265 }
266
267 private static class ValuesMissing implements LoadTargetResults {
268
269 private static final ValuesMissing INSTANCE = new ValuesMissing();
270
271 private ValuesMissing() {}
272
273 @Override
274 public LoadTargetResultsType getType() {
275 return LoadTargetResultsType.VALUES_MISSING;
276 }
277 }
278
279 interface TargetAndErrorIfAny {
280
281 boolean isPackageLoadedSuccessfully();
282
283 @Nullable NoSuchTargetException getErrorLoadingTarget();
284
285 Target getTarget();
286 }
287
shreyax246f0aa2018-02-23 07:46:54 -0800288 @VisibleForTesting
289 static class TargetAndErrorIfAnyImpl implements TargetAndErrorIfAny, LoadTargetResults {
Mark Schallerb25759c2015-07-29 17:09:18 +0000290
291 private final boolean packageLoadedSuccessfully;
292 @Nullable private final NoSuchTargetException errorLoadingTarget;
293 private final Target target;
294
shreyax246f0aa2018-02-23 07:46:54 -0800295 @VisibleForTesting
296 TargetAndErrorIfAnyImpl(
297 boolean packageLoadedSuccessfully,
298 @Nullable NoSuchTargetException errorLoadingTarget,
299 Target target) {
Mark Schallerb25759c2015-07-29 17:09:18 +0000300 this.packageLoadedSuccessfully = packageLoadedSuccessfully;
301 this.errorLoadingTarget = errorLoadingTarget;
302 this.target = target;
303 }
304
305 @Override
306 public LoadTargetResultsType getType() {
307 return LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY;
308 }
309
310 @Override
311 public boolean isPackageLoadedSuccessfully() {
312 return packageLoadedSuccessfully;
313 }
314
315 @Override
316 @Nullable
317 public NoSuchTargetException getErrorLoadingTarget() {
318 return errorLoadingTarget;
319 }
320
321 @Override
322 public Target getTarget() {
323 return target;
324 }
325 }
326
shreyax246f0aa2018-02-23 07:46:54 -0800327 LoadTargetResults loadTarget(Environment env, Label label)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000328 throws NoSuchTargetException, NoSuchPackageException, InterruptedException {
Mark Schallerb25759c2015-07-29 17:09:18 +0000329 SkyKey packageKey = PackageValue.key(label.getPackageIdentifier());
330 SkyKey targetKey = TargetMarkerValue.key(label);
331
332 boolean packageLoadedSuccessfully;
333 Target target;
334 NoSuchTargetException errorLoadingTarget = null;
335 try {
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000336 TargetMarkerValue targetValue = getTargetMarkerValue(targetKey, env);
337 boolean targetValueMissing = targetValue == null;
338 Preconditions.checkState(targetValueMissing == env.valuesMissing(), targetKey);
339 if (targetValueMissing) {
Mark Schallerb25759c2015-07-29 17:09:18 +0000340 return ValuesMissing.INSTANCE;
341 }
342 PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey,
343 NoSuchPackageException.class);
344 if (packageValue == null) {
345 return ValuesMissing.INSTANCE;
346 }
347
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000348 Package pkg = packageValue.getPackage();
349 if (pkg.containsErrors()) {
350 throw new BuildFileContainsErrorsException(label.getPackageIdentifier());
351 }
Mark Schallerb25759c2015-07-29 17:09:18 +0000352 packageLoadedSuccessfully = true;
353 try {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000354 target = pkg.getTarget(label.getName());
Mark Schallerb25759c2015-07-29 17:09:18 +0000355 } catch (NoSuchTargetException unexpected) {
356 // Not expected since the TargetMarkerFunction would have failed earlier if the Target
357 // was not present.
358 throw new IllegalStateException(unexpected);
359 }
360 } catch (NoSuchTargetException e) {
361 if (!e.hasTarget()) {
362 throw e;
363 }
364
365 // We know that a Target may be extracted, but we need to get it out of the Package
366 // (which is known to be in error).
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000367 PackageValue packageValue =
368 (PackageValue) Preconditions.checkNotNull(env.getValue(packageKey), label);
369 Package pkg = packageValue.getPackage();
Mark Schallerb25759c2015-07-29 17:09:18 +0000370 try {
371 target = pkg.getTarget(label.getName());
372 } catch (NoSuchTargetException nste) {
373 throw new IllegalStateException("Expected target to exist", nste);
374 }
375
376 errorLoadingTarget = e;
377 packageLoadedSuccessfully = false;
378 }
379 return new TargetAndErrorIfAnyImpl(packageLoadedSuccessfully, errorLoadingTarget, target);
380 }
381
382 /**
383 * Used to declare all the exception types that can be wrapped in the exception thrown by
384 * {@link TransitiveTraversalFunction#compute}.
385 */
386 static class TransitiveBaseTraversalFunctionException extends SkyFunctionException {
387 /**
388 * Used to propagate an error from a direct target dependency to the target that depended on
389 * it.
390 */
391 public TransitiveBaseTraversalFunctionException(NoSuchPackageException e) {
392 super(e, Transience.PERSISTENT);
393 }
394
395 /**
396 * In nokeep_going mode, used to propagate an error from a direct target dependency to the
397 * target that depended on it.
398 *
399 * <p>In keep_going mode, used the same way, but only for targets that could not be loaded at
400 * all (we proceed with transitive loading on targets that contain errors).</p>
401 */
402 public TransitiveBaseTraversalFunctionException(NoSuchTargetException e) {
403 super(e, Transience.PERSISTENT);
404 }
405 }
406}