blob: 130f681d88392afe50fd8c9aa610df45d65ec57c [file] [log] [blame]
Mark Schallerb25759c2015-07-29 17:09:18 +00001// Copyright 2015 Google Inc. All rights reserved.
2//
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
16import com.google.common.base.Preconditions;
Eric Fellheimer85fe0612015-08-18 21:09:54 +000017import com.google.common.collect.Iterables;
Mark Schallerb25759c2015-07-29 17:09:18 +000018import com.google.common.collect.Lists;
Mark Schallerb25759c2015-07-29 17:09:18 +000019import com.google.devtools.build.lib.events.Event;
20import com.google.devtools.build.lib.events.EventHandler;
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +000021import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
Mark Schallerb25759c2015-07-29 17:09:18 +000022import com.google.devtools.build.lib.packages.InputFile;
23import com.google.devtools.build.lib.packages.NoSuchPackageException;
24import com.google.devtools.build.lib.packages.NoSuchTargetException;
25import com.google.devtools.build.lib.packages.NoSuchThingException;
26import com.google.devtools.build.lib.packages.OutputFile;
27import com.google.devtools.build.lib.packages.Package;
28import com.google.devtools.build.lib.packages.PackageGroup;
29import com.google.devtools.build.lib.packages.Rule;
30import com.google.devtools.build.lib.packages.Target;
31import com.google.devtools.build.lib.packages.TargetUtils;
32import com.google.devtools.build.lib.syntax.Label;
33import com.google.devtools.build.skyframe.SkyFunction;
34import com.google.devtools.build.skyframe.SkyFunctionException;
35import com.google.devtools.build.skyframe.SkyKey;
36import com.google.devtools.build.skyframe.SkyValue;
37import com.google.devtools.build.skyframe.ValueOrException2;
38
Mark Schallerb25759c2015-07-29 17:09:18 +000039import java.util.HashSet;
40import java.util.List;
41import java.util.Map.Entry;
42import java.util.Set;
43
44import javax.annotation.Nullable;
45
46/**
47 * This class can be extended to define {@link SkyFunction}s that traverse a target and its
48 * transitive dependencies and return values based on that traversal.
49 *
50 * <p>The {@code TProcessedTargets} type parameter represents the result of processing a target and
51 * its transitive dependencies.
52 *
53 * <p>{@code TransitiveBaseTraversalFunction} asks for one to be constructed via {@link
54 * #processTarget}, and then asks for it to be updated based on the current target's
55 * attributes' dependencies via {@link #processDeps}, and then asks for it to be updated based
56 * on the current target' aspects' dependencies via {@link #processDeps}. Finally, it calls
57 * {@link #computeSkyValue} with the {#code ProcessedTargets} to get the {@link SkyValue} to
58 * return.
59 */
60abstract class TransitiveBaseTraversalFunction<TProcessedTargets>
61 implements SkyFunction {
62
63 /**
64 * Returns a {@link SkyKey} corresponding to the traversal of a target specified by {@code label}
65 * and its transitive dependencies.
66 *
67 * <p>Extenders of this class should implement this function to return a key with their
68 * specialized {@link SkyFunction}'s name.
69 *
70 * <p>{@link TransitiveBaseTraversalFunction} calls this for each dependency of a target, and
71 * then gets their values from the environment.
72 *
73 * <p>The key's {@link SkyFunction} may throw at most {@link NoSuchPackageException} and
74 * {@link NoSuchTargetException}. Other exception types are not handled by {@link
75 * TransitiveBaseTraversalFunction}.
76 */
77 abstract SkyKey getKey(Label label);
78
79 abstract TProcessedTargets processTarget(Label label,
80 TargetAndErrorIfAny targetAndErrorIfAny);
81
82 abstract void processDeps(TProcessedTargets processedTargets, EventHandler eventHandler,
83 TargetAndErrorIfAny targetAndErrorIfAny,
84 Iterable<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
85 depEntries);
86
87 /**
88 * Returns a {@link SkyValue} based on the target and any errors it has, and the values
89 * accumulated across it and a traversal of its transitive dependencies.
90 */
91 abstract SkyValue computeSkyValue(TargetAndErrorIfAny targetAndErrorIfAny,
92 TProcessedTargets processedTargets);
93
94 @Override
95 public SkyValue compute(SkyKey key, Environment env)
96 throws TransitiveBaseTraversalFunctionException {
97 Label label = (Label) key.argument();
98 LoadTargetResults loadTargetResults;
99 try {
100 loadTargetResults = loadTarget(env, label);
101 } catch (NoSuchTargetException e) {
102 throw new TransitiveBaseTraversalFunctionException(e);
103 } catch (NoSuchPackageException e) {
104 throw new TransitiveBaseTraversalFunctionException(e);
105 }
106 LoadTargetResultsType loadTargetResultsType = loadTargetResults.getType();
107 if (loadTargetResultsType.equals(LoadTargetResultsType.VALUES_MISSING)) {
108 return null;
109 }
110 Preconditions.checkState(
111 loadTargetResultsType.equals(LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY),
112 loadTargetResultsType);
113 TargetAndErrorIfAny targetAndErrorIfAny = (TargetAndErrorIfAny) loadTargetResults;
114 TProcessedTargets processedTargets = processTarget(label, targetAndErrorIfAny);
115
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000116 // Process deps from attributes and conservative aspects of current target.
Mark Schallerb25759c2015-07-29 17:09:18 +0000117 Iterable<SkyKey> labelDepKeys = getLabelDepKeys(targetAndErrorIfAny.getTarget());
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000118 Iterable<SkyKey> labelAspectKeys =
119 getConservativeLabelAspectKeys(targetAndErrorIfAny.getTarget());
120 Iterable<SkyKey> depAndAspectKeys = Iterables.concat(labelDepKeys, labelAspectKeys);
121
Mark Schallerb25759c2015-07-29 17:09:18 +0000122 Set<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000123 depsAndAspectEntries = env.getValuesOrThrow(depAndAspectKeys,
124 NoSuchPackageException.class, NoSuchTargetException.class).entrySet();
125 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, depsAndAspectEntries);
Mark Schallerb25759c2015-07-29 17:09:18 +0000126 if (env.valuesMissing()) {
127 return null;
128 }
129
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000130
131 // Process deps from strict aspects.
132 labelAspectKeys = getStrictLabelAspectKeys(targetAndErrorIfAny.getTarget(), env);
Mark Schallerb25759c2015-07-29 17:09:18 +0000133 Set<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>>
134 labelAspectEntries = env.getValuesOrThrow(labelAspectKeys, NoSuchPackageException.class,
135 NoSuchTargetException.class).entrySet();
136 processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, labelAspectEntries);
137 if (env.valuesMissing()) {
138 return null;
139 }
140
141 return computeSkyValue(targetAndErrorIfAny, processedTargets);
142 }
143
144 @Override
145 public String extractTag(SkyKey skyKey) {
146 return Label.print(((Label) skyKey.argument()));
147 }
148
Eric Fellheimerb5c98842015-08-12 23:24:21 +0000149 /**
150 * Return an Iterable of SkyKeys corresponding to the Aspect-related dependencies of target.
151 *
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000152 * <p>This method may return a precise set of aspect keys, but may need to request additional
153 * dependencies from the env to do so.
154 *
155 * <p>Subclasses should implement only one of #getStrictLabelAspectKeys and
156 * @getConservativeLabelAspectKeys.
157 */
158 protected abstract Iterable<SkyKey> getStrictLabelAspectKeys(Target target, Environment env);
159
160 /**
161 * Return an Iterable of SkyKeys corresponding to the Aspect-related dependencies of target.
162 *
Eric Fellheimerb5c98842015-08-12 23:24:21 +0000163 * <p>This method may return a conservative over-approximation of the exact set.
164 */
Eric Fellheimer85fe0612015-08-18 21:09:54 +0000165 protected abstract Iterable<SkyKey> getConservativeLabelAspectKeys(Target target);
Mark Schallerb25759c2015-07-29 17:09:18 +0000166
167 private Iterable<SkyKey> getLabelDepKeys(Target target) {
168 List<SkyKey> depKeys = Lists.newArrayList();
169 for (Label depLabel : getLabelDeps(target)) {
170 depKeys.add(getKey(depLabel));
171 }
172 return depKeys;
173 }
174
175 // TODO(bazel-team): Unify this logic with that in LabelVisitor, and possibly DependencyResolver.
176 private static Iterable<Label> getLabelDeps(Target target) {
177 final Set<Label> labels = new HashSet<>();
178 if (target instanceof OutputFile) {
179 Rule rule = ((OutputFile) target).getGeneratingRule();
180 labels.add(rule.getLabel());
181 visitTargetVisibility(target, labels);
182 } else if (target instanceof InputFile) {
183 visitTargetVisibility(target, labels);
184 } else if (target instanceof Rule) {
185 visitTargetVisibility(target, labels);
186 visitRule(target, labels);
187 } else if (target instanceof PackageGroup) {
188 visitPackageGroup((PackageGroup) target, labels);
189 }
190 return labels;
191 }
192
193 private static void visitRule(Target target, Set<Label> labels) {
194 labels.addAll(((Rule) target).getLabels(Rule.NO_NODEP_ATTRIBUTES));
195 }
196
197 private static void visitTargetVisibility(Target target, Set<Label> labels) {
198 labels.addAll(target.getVisibility().getDependencyLabels());
199 }
200
201 private static void visitPackageGroup(PackageGroup packageGroup, Set<Label> labels) {
202 labels.addAll(packageGroup.getIncludes());
203 }
204
205 protected void maybeReportErrorAboutMissingEdge(Target target, Label depLabel,
206 NoSuchThingException e, EventHandler eventHandler) {
207 if (e instanceof NoSuchTargetException) {
208 NoSuchTargetException nste = (NoSuchTargetException) e;
209 if (depLabel.equals(nste.getLabel())) {
210 eventHandler.handle(Event.error(TargetUtils.getLocationMaybe(target),
211 TargetUtils.formatMissingEdge(target, depLabel, e)));
212 }
213 } else if (e instanceof NoSuchPackageException) {
214 NoSuchPackageException nspe = (NoSuchPackageException) e;
215 if (nspe.getPackageId().equals(depLabel.getPackageIdentifier())) {
216 eventHandler.handle(Event.error(TargetUtils.getLocationMaybe(target),
217 TargetUtils.formatMissingEdge(target, depLabel, e)));
218 }
219 }
220 }
221
222 enum LoadTargetResultsType {
223 VALUES_MISSING,
224 TARGET_AND_ERROR_IF_ANY
225 }
226
227 interface LoadTargetResults {
228 LoadTargetResultsType getType();
229 }
230
231 private static class ValuesMissing implements LoadTargetResults {
232
233 private static final ValuesMissing INSTANCE = new ValuesMissing();
234
235 private ValuesMissing() {}
236
237 @Override
238 public LoadTargetResultsType getType() {
239 return LoadTargetResultsType.VALUES_MISSING;
240 }
241 }
242
243 interface TargetAndErrorIfAny {
244
245 boolean isPackageLoadedSuccessfully();
246
247 @Nullable NoSuchTargetException getErrorLoadingTarget();
248
249 Target getTarget();
250 }
251
252 private static class TargetAndErrorIfAnyImpl implements TargetAndErrorIfAny, LoadTargetResults {
253
254 private final boolean packageLoadedSuccessfully;
255 @Nullable private final NoSuchTargetException errorLoadingTarget;
256 private final Target target;
257
258 private TargetAndErrorIfAnyImpl(boolean packageLoadedSuccessfully,
259 @Nullable NoSuchTargetException errorLoadingTarget, Target target) {
260 this.packageLoadedSuccessfully = packageLoadedSuccessfully;
261 this.errorLoadingTarget = errorLoadingTarget;
262 this.target = target;
263 }
264
265 @Override
266 public LoadTargetResultsType getType() {
267 return LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY;
268 }
269
270 @Override
271 public boolean isPackageLoadedSuccessfully() {
272 return packageLoadedSuccessfully;
273 }
274
275 @Override
276 @Nullable
277 public NoSuchTargetException getErrorLoadingTarget() {
278 return errorLoadingTarget;
279 }
280
281 @Override
282 public Target getTarget() {
283 return target;
284 }
285 }
286
287 private static LoadTargetResults loadTarget(Environment env, Label label)
288 throws NoSuchTargetException, NoSuchPackageException {
289 SkyKey packageKey = PackageValue.key(label.getPackageIdentifier());
290 SkyKey targetKey = TargetMarkerValue.key(label);
291
292 boolean packageLoadedSuccessfully;
293 Target target;
294 NoSuchTargetException errorLoadingTarget = null;
295 try {
296 TargetMarkerValue targetValue = (TargetMarkerValue) env.getValueOrThrow(targetKey,
297 NoSuchTargetException.class, NoSuchPackageException.class);
298 if (targetValue == null) {
299 return ValuesMissing.INSTANCE;
300 }
301 PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey,
302 NoSuchPackageException.class);
303 if (packageValue == null) {
304 return ValuesMissing.INSTANCE;
305 }
306
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000307 Package pkg = packageValue.getPackage();
308 if (pkg.containsErrors()) {
309 throw new BuildFileContainsErrorsException(label.getPackageIdentifier());
310 }
Mark Schallerb25759c2015-07-29 17:09:18 +0000311 packageLoadedSuccessfully = true;
312 try {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000313 target = pkg.getTarget(label.getName());
Mark Schallerb25759c2015-07-29 17:09:18 +0000314 } catch (NoSuchTargetException unexpected) {
315 // Not expected since the TargetMarkerFunction would have failed earlier if the Target
316 // was not present.
317 throw new IllegalStateException(unexpected);
318 }
319 } catch (NoSuchTargetException e) {
320 if (!e.hasTarget()) {
321 throw e;
322 }
323
324 // We know that a Target may be extracted, but we need to get it out of the Package
325 // (which is known to be in error).
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000326 PackageValue packageValue =
327 (PackageValue) Preconditions.checkNotNull(env.getValue(packageKey), label);
328 Package pkg = packageValue.getPackage();
Mark Schallerb25759c2015-07-29 17:09:18 +0000329 try {
330 target = pkg.getTarget(label.getName());
331 } catch (NoSuchTargetException nste) {
332 throw new IllegalStateException("Expected target to exist", nste);
333 }
334
335 errorLoadingTarget = e;
336 packageLoadedSuccessfully = false;
337 }
338 return new TargetAndErrorIfAnyImpl(packageLoadedSuccessfully, errorLoadingTarget, target);
339 }
340
341 /**
342 * Used to declare all the exception types that can be wrapped in the exception thrown by
343 * {@link TransitiveTraversalFunction#compute}.
344 */
345 static class TransitiveBaseTraversalFunctionException extends SkyFunctionException {
346 /**
347 * Used to propagate an error from a direct target dependency to the target that depended on
348 * it.
349 */
350 public TransitiveBaseTraversalFunctionException(NoSuchPackageException e) {
351 super(e, Transience.PERSISTENT);
352 }
353
354 /**
355 * In nokeep_going mode, used to propagate an error from a direct target dependency to the
356 * target that depended on it.
357 *
358 * <p>In keep_going mode, used the same way, but only for targets that could not be loaded at
359 * all (we proceed with transitive loading on targets that contain errors).</p>
360 */
361 public TransitiveBaseTraversalFunctionException(NoSuchTargetException e) {
362 super(e, Transience.PERSISTENT);
363 }
364 }
365}