blob: 726672de97e3bbeb81d4a2eaad5079b778f17de2 [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
michajlo2266a6f2017-08-04 18:49:49 +020016import com.google.common.collect.Collections2;
17import com.google.common.collect.ImmutableSet;
Mark Schallerb25759c2015-07-29 17:09:18 +000018import com.google.common.collect.Lists;
Eric Fellheimere27d0632015-09-25 21:35:26 +000019import com.google.common.collect.Multimap;
michajlof3e91b22017-08-08 00:33:26 +020020import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000021import com.google.devtools.build.lib.cmdline.Label;
Mark Schallerb25759c2015-07-29 17:09:18 +000022import com.google.devtools.build.lib.events.EventHandler;
Eric Fellheimere27d0632015-09-25 21:35:26 +000023import com.google.devtools.build.lib.packages.Attribute;
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +000024import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
Dmitry Lomov6073eb62016-01-21 21:26:32 +000025import com.google.devtools.build.lib.packages.DependencyFilter;
Mark Schallerb25759c2015-07-29 17:09:18 +000026import com.google.devtools.build.lib.packages.InputFile;
27import com.google.devtools.build.lib.packages.NoSuchPackageException;
28import com.google.devtools.build.lib.packages.NoSuchTargetException;
Mark Schallerb25759c2015-07-29 17:09:18 +000029import com.google.devtools.build.lib.packages.OutputFile;
30import com.google.devtools.build.lib.packages.Package;
31import com.google.devtools.build.lib.packages.PackageGroup;
32import com.google.devtools.build.lib.packages.Rule;
33import com.google.devtools.build.lib.packages.Target;
Mark Schaller6df81792015-12-10 18:47:47 +000034import com.google.devtools.build.lib.util.Preconditions;
Mark Schallerb25759c2015-07-29 17:09:18 +000035import com.google.devtools.build.skyframe.SkyFunction;
36import com.google.devtools.build.skyframe.SkyFunctionException;
37import com.google.devtools.build.skyframe.SkyKey;
38import com.google.devtools.build.skyframe.SkyValue;
39import com.google.devtools.build.skyframe.ValueOrException2;
Eric Fellheimere27d0632015-09-25 21:35:26 +000040import java.util.Collection;
41import java.util.HashMap;
Mark Schallerb25759c2015-07-29 17:09:18 +000042import java.util.HashSet;
43import java.util.List;
Eric Fellheimere27d0632015-09-25 21:35:26 +000044import java.util.Map;
Mark Schallerb25759c2015-07-29 17:09:18 +000045import java.util.Map.Entry;
46import 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 /**
66 * Returns a {@link SkyKey} corresponding to the traversal of a target specified by {@code label}
67 * and its transitive dependencies.
68 *
69 * <p>Extenders of this class should implement this function to return a key with their
70 * specialized {@link SkyFunction}'s name.
71 *
72 * <p>{@link TransitiveBaseTraversalFunction} calls this for each dependency of a target, and
73 * then gets their values from the environment.
74 *
75 * <p>The key's {@link SkyFunction} may throw at most {@link NoSuchPackageException} and
76 * {@link NoSuchTargetException}. Other exception types are not handled by {@link
77 * TransitiveBaseTraversalFunction}.
78 */
79 abstract SkyKey getKey(Label label);
80
Mark Schaller6b6d8a92015-10-23 01:00:28 +000081 abstract TProcessedTargets processTarget(Label label, TargetAndErrorIfAny targetAndErrorIfAny);
Mark Schallerb25759c2015-07-29 17:09:18 +000082
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000083 abstract void processDeps(
84 TProcessedTargets processedTargets,
85 EventHandler eventHandler,
Mark Schallerb25759c2015-07-29 17:09:18 +000086 TargetAndErrorIfAny targetAndErrorIfAny,
87 Iterable<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +000088 depEntries)
89 throws InterruptedException;
Mark Schallerb25759c2015-07-29 17:09:18 +000090
91 /**
92 * Returns a {@link SkyValue} based on the target and any errors it has, and the values
93 * accumulated across it and a traversal of its transitive dependencies.
94 */
95 abstract SkyValue computeSkyValue(TargetAndErrorIfAny targetAndErrorIfAny,
96 TProcessedTargets processedTargets);
97
Mark Schaller6b6d8a92015-10-23 01:00:28 +000098 /**
99 * Returns a {@link TargetMarkerValue} corresponding to the {@param targetMarkerKey} or {@code
100 * null} if the value isn't ready.
101 */
102 @Nullable
103 abstract TargetMarkerValue getTargetMarkerValue(SkyKey targetMarkerKey, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000104 throws NoSuchTargetException, NoSuchPackageException, InterruptedException;
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000105
Mark Schallerb25759c2015-07-29 17:09:18 +0000106 @Override
107 public SkyValue compute(SkyKey key, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000108 throws TransitiveBaseTraversalFunctionException, InterruptedException {
Mark Schallerb25759c2015-07-29 17:09:18 +0000109 Label label = (Label) key.argument();
110 LoadTargetResults loadTargetResults;
111 try {
112 loadTargetResults = loadTarget(env, label);
113 } catch (NoSuchTargetException e) {
114 throw new TransitiveBaseTraversalFunctionException(e);
115 } catch (NoSuchPackageException e) {
116 throw new TransitiveBaseTraversalFunctionException(e);
117 }
118 LoadTargetResultsType loadTargetResultsType = loadTargetResults.getType();
119 if (loadTargetResultsType.equals(LoadTargetResultsType.VALUES_MISSING)) {
120 return null;
121 }
122 Preconditions.checkState(
123 loadTargetResultsType.equals(LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY),
124 loadTargetResultsType);
125 TargetAndErrorIfAny targetAndErrorIfAny = (TargetAndErrorIfAny) loadTargetResults;
126 TProcessedTargets processedTargets = processTarget(label, targetAndErrorIfAny);
127
Eric Fellheimere27d0632015-09-25 21:35:26 +0000128 // Process deps from attributes.
michajlo2266a6f2017-08-04 18:49:49 +0200129 Collection<SkyKey> labelDepKeys =
130 Collections2.transform(getLabelDeps(targetAndErrorIfAny.getTarget()), this::getKey);
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);
135 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, depMap.entrySet());
Mark Schallerb25759c2015-07-29 17:09:18 +0000136 if (env.valuesMissing()) {
137 return null;
138 }
139
Eric Fellheimere27d0632015-09-25 21:35:26 +0000140 // Process deps from aspects.
141 Iterable<SkyKey> labelAspectKeys =
142 getStrictLabelAspectKeys(targetAndErrorIfAny.getTarget(), depMap, env);
Mark Schallerb25759c2015-07-29 17:09:18 +0000143 Set<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
144 labelAspectEntries = env.getValuesOrThrow(labelAspectKeys, NoSuchPackageException.class,
145 NoSuchTargetException.class).entrySet();
146 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, labelAspectEntries);
147 if (env.valuesMissing()) {
148 return null;
149 }
150
151 return computeSkyValue(targetAndErrorIfAny, processedTargets);
152 }
153
154 @Override
155 public String extractTag(SkyKey skyKey) {
156 return Label.print(((Label) skyKey.argument()));
157 }
158
Eric Fellheimerb5c98842015-08-12 23:24:21 +0000159 /**
160 * Return an Iterable of SkyKeys corresponding to the Aspect-related dependencies of target.
161 *
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000162 * <p>This method may return a precise set of aspect keys, but may need to request additional
163 * dependencies from the env to do so.
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000164 */
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000165 private Iterable<SkyKey> getStrictLabelAspectKeys(
166 Target target,
167 Map<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> depMap,
168 Environment env)
169 throws InterruptedException {
Eric Fellheimere27d0632015-09-25 21:35:26 +0000170 List<SkyKey> depKeys = Lists.newArrayList();
171 if (target instanceof Rule) {
172 Map<Label, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> labelDepMap =
173 new HashMap<>(depMap.size());
174 for (Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>> entry :
175 depMap.entrySet()) {
176 labelDepMap.put((Label) entry.getKey().argument(), entry.getValue());
177 }
178
179 Multimap<Attribute, Label> transitions =
Dmitry Lomov6073eb62016-01-21 21:26:32 +0000180 ((Rule) target).getTransitions(DependencyFilter.NO_NODEP_ATTRIBUTES);
Eric Fellheimere27d0632015-09-25 21:35:26 +0000181 for (Entry<Attribute, Label> entry : transitions.entries()) {
182 ValueOrException2<NoSuchPackageException, NoSuchTargetException> value =
183 labelDepMap.get(entry.getValue());
184 for (Label label :
Dmitry Lomov6231d082015-11-02 17:17:20 +0000185 getAspectLabels((Rule) target, entry.getKey(), entry.getValue(), value, env)) {
Eric Fellheimere27d0632015-09-25 21:35:26 +0000186 depKeys.add(getKey(label));
187 }
188 }
189 }
190 return depKeys;
191 }
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000192
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000193 /** Get the Aspect-related Label deps for the given edge. */
194 protected abstract Collection<Label> getAspectLabels(
Dmitry Lomov6231d082015-11-02 17:17:20 +0000195 Rule fromRule,
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000196 Attribute attr,
197 Label toLabel,
198 ValueOrException2<NoSuchPackageException, NoSuchTargetException> toVal,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000199 Environment env)
200 throws InterruptedException;
Mark Schallerb25759c2015-07-29 17:09:18 +0000201
Mark Schallerb25759c2015-07-29 17:09:18 +0000202 // TODO(bazel-team): Unify this logic with that in LabelVisitor, and possibly DependencyResolver.
michajlo2266a6f2017-08-04 18:49:49 +0200203 private static Collection<Label> getLabelDeps(Target target) throws InterruptedException {
Mark Schallerb25759c2015-07-29 17:09:18 +0000204 if (target instanceof OutputFile) {
205 Rule rule = ((OutputFile) target).getGeneratingRule();
michajlo2266a6f2017-08-04 18:49:49 +0200206 List<Label> visibilityLabels = visitTargetVisibility(target);
michajlof3e91b22017-08-08 00:33:26 +0200207 HashSet<Label> result = Sets.newHashSetWithExpectedSize(visibilityLabels.size() + 1);
michajlo2266a6f2017-08-04 18:49:49 +0200208 result.add(rule.getLabel());
209 result.addAll(visibilityLabels);
210 return result;
Mark Schallerb25759c2015-07-29 17:09:18 +0000211 } else if (target instanceof InputFile) {
michajlo2266a6f2017-08-04 18:49:49 +0200212 return new HashSet<>(visitTargetVisibility(target));
Mark Schallerb25759c2015-07-29 17:09:18 +0000213 } else if (target instanceof Rule) {
michajlo2266a6f2017-08-04 18:49:49 +0200214 List<Label> visibilityLabels = visitTargetVisibility(target);
215 Collection<Label> ruleLabels = visitRule(target);
michajlof3e91b22017-08-08 00:33:26 +0200216 HashSet<Label> result =
217 Sets.newHashSetWithExpectedSize(visibilityLabels.size() + ruleLabels.size());
michajlo2266a6f2017-08-04 18:49:49 +0200218 result.addAll(visibilityLabels);
219 result.addAll(ruleLabels);
220 return result;
Mark Schallerb25759c2015-07-29 17:09:18 +0000221 } else if (target instanceof PackageGroup) {
michajlo2266a6f2017-08-04 18:49:49 +0200222 return new HashSet<>(visitPackageGroup((PackageGroup) target));
223 } else {
224 return ImmutableSet.of();
Mark Schallerb25759c2015-07-29 17:09:18 +0000225 }
Mark Schallerb25759c2015-07-29 17:09:18 +0000226 }
227
michajlo2266a6f2017-08-04 18:49:49 +0200228 private static Collection<Label> visitRule(Target target) throws InterruptedException {
229 return ((Rule) target).getTransitions(DependencyFilter.NO_NODEP_ATTRIBUTES).values();
Mark Schallerb25759c2015-07-29 17:09:18 +0000230 }
231
michajlo2266a6f2017-08-04 18:49:49 +0200232 private static List<Label> visitTargetVisibility(Target target) {
233 return target.getVisibility().getDependencyLabels();
Mark Schallerb25759c2015-07-29 17:09:18 +0000234 }
235
michajlo2266a6f2017-08-04 18:49:49 +0200236 private static List<Label> visitPackageGroup(PackageGroup packageGroup) {
237 return packageGroup.getIncludes();
Mark Schallerb25759c2015-07-29 17:09:18 +0000238 }
239
Mark Schallerb25759c2015-07-29 17:09:18 +0000240 enum LoadTargetResultsType {
241 VALUES_MISSING,
242 TARGET_AND_ERROR_IF_ANY
243 }
244
245 interface LoadTargetResults {
246 LoadTargetResultsType getType();
247 }
248
249 private static class ValuesMissing implements LoadTargetResults {
250
251 private static final ValuesMissing INSTANCE = new ValuesMissing();
252
253 private ValuesMissing() {}
254
255 @Override
256 public LoadTargetResultsType getType() {
257 return LoadTargetResultsType.VALUES_MISSING;
258 }
259 }
260
261 interface TargetAndErrorIfAny {
262
263 boolean isPackageLoadedSuccessfully();
264
265 @Nullable NoSuchTargetException getErrorLoadingTarget();
266
267 Target getTarget();
268 }
269
270 private static class TargetAndErrorIfAnyImpl implements TargetAndErrorIfAny, LoadTargetResults {
271
272 private final boolean packageLoadedSuccessfully;
273 @Nullable private final NoSuchTargetException errorLoadingTarget;
274 private final Target target;
275
276 private TargetAndErrorIfAnyImpl(boolean packageLoadedSuccessfully,
277 @Nullable NoSuchTargetException errorLoadingTarget, Target target) {
278 this.packageLoadedSuccessfully = packageLoadedSuccessfully;
279 this.errorLoadingTarget = errorLoadingTarget;
280 this.target = target;
281 }
282
283 @Override
284 public LoadTargetResultsType getType() {
285 return LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY;
286 }
287
288 @Override
289 public boolean isPackageLoadedSuccessfully() {
290 return packageLoadedSuccessfully;
291 }
292
293 @Override
294 @Nullable
295 public NoSuchTargetException getErrorLoadingTarget() {
296 return errorLoadingTarget;
297 }
298
299 @Override
300 public Target getTarget() {
301 return target;
302 }
303 }
304
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000305 private LoadTargetResults loadTarget(Environment env, Label label)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000306 throws NoSuchTargetException, NoSuchPackageException, InterruptedException {
Mark Schallerb25759c2015-07-29 17:09:18 +0000307 SkyKey packageKey = PackageValue.key(label.getPackageIdentifier());
308 SkyKey targetKey = TargetMarkerValue.key(label);
309
310 boolean packageLoadedSuccessfully;
311 Target target;
312 NoSuchTargetException errorLoadingTarget = null;
313 try {
Mark Schaller6b6d8a92015-10-23 01:00:28 +0000314 TargetMarkerValue targetValue = getTargetMarkerValue(targetKey, env);
315 boolean targetValueMissing = targetValue == null;
316 Preconditions.checkState(targetValueMissing == env.valuesMissing(), targetKey);
317 if (targetValueMissing) {
Mark Schallerb25759c2015-07-29 17:09:18 +0000318 return ValuesMissing.INSTANCE;
319 }
320 PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey,
321 NoSuchPackageException.class);
322 if (packageValue == null) {
323 return ValuesMissing.INSTANCE;
324 }
325
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000326 Package pkg = packageValue.getPackage();
327 if (pkg.containsErrors()) {
328 throw new BuildFileContainsErrorsException(label.getPackageIdentifier());
329 }
Mark Schallerb25759c2015-07-29 17:09:18 +0000330 packageLoadedSuccessfully = true;
331 try {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000332 target = pkg.getTarget(label.getName());
Mark Schallerb25759c2015-07-29 17:09:18 +0000333 } catch (NoSuchTargetException unexpected) {
334 // Not expected since the TargetMarkerFunction would have failed earlier if the Target
335 // was not present.
336 throw new IllegalStateException(unexpected);
337 }
338 } catch (NoSuchTargetException e) {
339 if (!e.hasTarget()) {
340 throw e;
341 }
342
343 // We know that a Target may be extracted, but we need to get it out of the Package
344 // (which is known to be in error).
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000345 PackageValue packageValue =
346 (PackageValue) Preconditions.checkNotNull(env.getValue(packageKey), label);
347 Package pkg = packageValue.getPackage();
Mark Schallerb25759c2015-07-29 17:09:18 +0000348 try {
349 target = pkg.getTarget(label.getName());
350 } catch (NoSuchTargetException nste) {
351 throw new IllegalStateException("Expected target to exist", nste);
352 }
353
354 errorLoadingTarget = e;
355 packageLoadedSuccessfully = false;
356 }
357 return new TargetAndErrorIfAnyImpl(packageLoadedSuccessfully, errorLoadingTarget, target);
358 }
359
360 /**
361 * Used to declare all the exception types that can be wrapped in the exception thrown by
362 * {@link TransitiveTraversalFunction#compute}.
363 */
364 static class TransitiveBaseTraversalFunctionException extends SkyFunctionException {
365 /**
366 * Used to propagate an error from a direct target dependency to the target that depended on
367 * it.
368 */
369 public TransitiveBaseTraversalFunctionException(NoSuchPackageException e) {
370 super(e, Transience.PERSISTENT);
371 }
372
373 /**
374 * In nokeep_going mode, used to propagate an error from a direct target dependency to the
375 * target that depended on it.
376 *
377 * <p>In keep_going mode, used the same way, but only for targets that could not be loaded at
378 * all (we proceed with transitive loading on targets that contain errors).</p>
379 */
380 public TransitiveBaseTraversalFunctionException(NoSuchTargetException e) {
381 super(e, Transience.PERSISTENT);
382 }
383 }
384}