diff options
author | Mark Schaller <mschaller@google.com> | 2015-07-29 17:09:18 +0000 |
---|---|---|
committer | Damien Martin-Guillerez <dmarting@google.com> | 2015-07-30 11:30:47 +0000 |
commit | b25759cab708722fb6c97a0816085c8f4ec24b98 (patch) | |
tree | 1e85f18135b5a29cc31b343f14ffcc3154207904 /src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java | |
parent | f3efffc83d3706c50861fefbfb19b2cff0b5e4ea (diff) |
Extract base class from TransitiveTargetFunction
--
MOS_MIGRATED_REVID=99386094
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java new file mode 100644 index 0000000000..36484551e6 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveBaseTraversalFunction.java @@ -0,0 +1,376 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.lib.skyframe; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.packages.AspectDefinition; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.InputFile; +import com.google.devtools.build.lib.packages.NoSuchPackageException; +import com.google.devtools.build.lib.packages.NoSuchTargetException; +import com.google.devtools.build.lib.packages.NoSuchThingException; +import com.google.devtools.build.lib.packages.OutputFile; +import com.google.devtools.build.lib.packages.Package; +import com.google.devtools.build.lib.packages.PackageGroup; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.Target; +import com.google.devtools.build.lib.packages.TargetUtils; +import com.google.devtools.build.lib.syntax.Label; +import com.google.devtools.build.skyframe.SkyFunction; +import com.google.devtools.build.skyframe.SkyFunctionException; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.ValueOrException2; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * This class can be extended to define {@link SkyFunction}s that traverse a target and its + * transitive dependencies and return values based on that traversal. + * + * <p>The {@code TProcessedTargets} type parameter represents the result of processing a target and + * its transitive dependencies. + * + * <p>{@code TransitiveBaseTraversalFunction} asks for one to be constructed via {@link + * #processTarget}, and then asks for it to be updated based on the current target's + * attributes' dependencies via {@link #processDeps}, and then asks for it to be updated based + * on the current target' aspects' dependencies via {@link #processDeps}. Finally, it calls + * {@link #computeSkyValue} with the {#code ProcessedTargets} to get the {@link SkyValue} to + * return. + */ +abstract class TransitiveBaseTraversalFunction<TProcessedTargets> + implements SkyFunction { + + /** + * Returns a {@link SkyKey} corresponding to the traversal of a target specified by {@code label} + * and its transitive dependencies. + * + * <p>Extenders of this class should implement this function to return a key with their + * specialized {@link SkyFunction}'s name. + * + * <p>{@link TransitiveBaseTraversalFunction} calls this for each dependency of a target, and + * then gets their values from the environment. + * + * <p>The key's {@link SkyFunction} may throw at most {@link NoSuchPackageException} and + * {@link NoSuchTargetException}. Other exception types are not handled by {@link + * TransitiveBaseTraversalFunction}. + */ + abstract SkyKey getKey(Label label); + + abstract TProcessedTargets processTarget(Label label, + TargetAndErrorIfAny targetAndErrorIfAny); + + abstract void processDeps(TProcessedTargets processedTargets, EventHandler eventHandler, + TargetAndErrorIfAny targetAndErrorIfAny, + Iterable<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>> + depEntries); + + /** + * Returns a {@link SkyValue} based on the target and any errors it has, and the values + * accumulated across it and a traversal of its transitive dependencies. + */ + abstract SkyValue computeSkyValue(TargetAndErrorIfAny targetAndErrorIfAny, + TProcessedTargets processedTargets); + + @Override + public SkyValue compute(SkyKey key, Environment env) + throws TransitiveBaseTraversalFunctionException { + Label label = (Label) key.argument(); + LoadTargetResults loadTargetResults; + try { + loadTargetResults = loadTarget(env, label); + } catch (NoSuchTargetException e) { + throw new TransitiveBaseTraversalFunctionException(e); + } catch (NoSuchPackageException e) { + throw new TransitiveBaseTraversalFunctionException(e); + } + LoadTargetResultsType loadTargetResultsType = loadTargetResults.getType(); + if (loadTargetResultsType.equals(LoadTargetResultsType.VALUES_MISSING)) { + return null; + } + Preconditions.checkState( + loadTargetResultsType.equals(LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY), + loadTargetResultsType); + TargetAndErrorIfAny targetAndErrorIfAny = (TargetAndErrorIfAny) loadTargetResults; + TProcessedTargets processedTargets = processTarget(label, targetAndErrorIfAny); + + // Process deps from attributes of current target. + Iterable<SkyKey> labelDepKeys = getLabelDepKeys(targetAndErrorIfAny.getTarget()); + Set<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>> + labelDepEntries = env.getValuesOrThrow(labelDepKeys, NoSuchPackageException.class, + NoSuchTargetException.class).entrySet(); + processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, labelDepEntries); + if (env.valuesMissing()) { + return null; + } + + // Process deps from aspects. + Iterable<SkyKey> labelAspectKeys = getLabelAspectKeys(targetAndErrorIfAny.getTarget(), env); + Set<Entry<SkyKey, ValueOrException2<NoSuchPackageException, NoSuchTargetException>>> + labelAspectEntries = env.getValuesOrThrow(labelAspectKeys, NoSuchPackageException.class, + NoSuchTargetException.class).entrySet(); + processDeps(processedTargets, env.getListener(), targetAndErrorIfAny, labelAspectEntries); + if (env.valuesMissing()) { + return null; + } + + return computeSkyValue(targetAndErrorIfAny, processedTargets); + } + + @Override + public String extractTag(SkyKey skyKey) { + return Label.print(((Label) skyKey.argument())); + } + + private Iterable<SkyKey> getLabelAspectKeys(Target target, Environment env) { + List<SkyKey> depKeys = Lists.newArrayList(); + if (target instanceof Rule) { + Multimap<Attribute, Label> transitions = + ((Rule) target).getTransitions(Rule.NO_NODEP_ATTRIBUTES); + for (Entry<Attribute, Label> entry : transitions.entries()) { + SkyKey packageKey = PackageValue.key(entry.getValue().getPackageIdentifier()); + try { + PackageValue pkgValue = (PackageValue) env.getValueOrThrow(packageKey, + NoSuchThingException.class); + if (pkgValue == null) { + continue; + } + Collection<Label> labels = AspectDefinition.visitAspectsIfRequired(target, entry.getKey(), + pkgValue.getPackage().getTarget(entry.getValue().getName())).values(); + for (Label label : labels) { + depKeys.add(getKey(label)); + } + } catch (NoSuchThingException e) { + // Do nothing. This error was handled when we computed the corresponding + // TransitiveTargetValue. + } + } + } + return depKeys; + } + + private Iterable<SkyKey> getLabelDepKeys(Target target) { + List<SkyKey> depKeys = Lists.newArrayList(); + for (Label depLabel : getLabelDeps(target)) { + depKeys.add(getKey(depLabel)); + } + return depKeys; + } + + // TODO(bazel-team): Unify this logic with that in LabelVisitor, and possibly DependencyResolver. + private static Iterable<Label> getLabelDeps(Target target) { + final Set<Label> labels = new HashSet<>(); + if (target instanceof OutputFile) { + Rule rule = ((OutputFile) target).getGeneratingRule(); + labels.add(rule.getLabel()); + visitTargetVisibility(target, labels); + } else if (target instanceof InputFile) { + visitTargetVisibility(target, labels); + } else if (target instanceof Rule) { + visitTargetVisibility(target, labels); + visitRule(target, labels); + } else if (target instanceof PackageGroup) { + visitPackageGroup((PackageGroup) target, labels); + } + return labels; + } + + private static void visitRule(Target target, Set<Label> labels) { + labels.addAll(((Rule) target).getLabels(Rule.NO_NODEP_ATTRIBUTES)); + } + + private static void visitTargetVisibility(Target target, Set<Label> labels) { + labels.addAll(target.getVisibility().getDependencyLabels()); + } + + private static void visitPackageGroup(PackageGroup packageGroup, Set<Label> labels) { + labels.addAll(packageGroup.getIncludes()); + } + + protected void maybeReportErrorAboutMissingEdge(Target target, Label depLabel, + NoSuchThingException e, EventHandler eventHandler) { + if (e instanceof NoSuchTargetException) { + NoSuchTargetException nste = (NoSuchTargetException) e; + if (depLabel.equals(nste.getLabel())) { + eventHandler.handle(Event.error(TargetUtils.getLocationMaybe(target), + TargetUtils.formatMissingEdge(target, depLabel, e))); + } + } else if (e instanceof NoSuchPackageException) { + NoSuchPackageException nspe = (NoSuchPackageException) e; + if (nspe.getPackageId().equals(depLabel.getPackageIdentifier())) { + eventHandler.handle(Event.error(TargetUtils.getLocationMaybe(target), + TargetUtils.formatMissingEdge(target, depLabel, e))); + } + } + } + + enum LoadTargetResultsType { + VALUES_MISSING, + TARGET_AND_ERROR_IF_ANY + } + + interface LoadTargetResults { + LoadTargetResultsType getType(); + } + + private static class ValuesMissing implements LoadTargetResults { + + private static final ValuesMissing INSTANCE = new ValuesMissing(); + + private ValuesMissing() {} + + @Override + public LoadTargetResultsType getType() { + return LoadTargetResultsType.VALUES_MISSING; + } + } + + interface TargetAndErrorIfAny { + + boolean isPackageLoadedSuccessfully(); + + @Nullable NoSuchTargetException getErrorLoadingTarget(); + + Target getTarget(); + } + + private static class TargetAndErrorIfAnyImpl implements TargetAndErrorIfAny, LoadTargetResults { + + private final boolean packageLoadedSuccessfully; + @Nullable private final NoSuchTargetException errorLoadingTarget; + private final Target target; + + private TargetAndErrorIfAnyImpl(boolean packageLoadedSuccessfully, + @Nullable NoSuchTargetException errorLoadingTarget, Target target) { + this.packageLoadedSuccessfully = packageLoadedSuccessfully; + this.errorLoadingTarget = errorLoadingTarget; + this.target = target; + } + + @Override + public LoadTargetResultsType getType() { + return LoadTargetResultsType.TARGET_AND_ERROR_IF_ANY; + } + + @Override + public boolean isPackageLoadedSuccessfully() { + return packageLoadedSuccessfully; + } + + @Override + @Nullable + public NoSuchTargetException getErrorLoadingTarget() { + return errorLoadingTarget; + } + + @Override + public Target getTarget() { + return target; + } + } + + private static LoadTargetResults loadTarget(Environment env, Label label) + throws NoSuchTargetException, NoSuchPackageException { + SkyKey packageKey = PackageValue.key(label.getPackageIdentifier()); + SkyKey targetKey = TargetMarkerValue.key(label); + + boolean packageLoadedSuccessfully; + Target target; + NoSuchTargetException errorLoadingTarget = null; + try { + TargetMarkerValue targetValue = (TargetMarkerValue) env.getValueOrThrow(targetKey, + NoSuchTargetException.class, NoSuchPackageException.class); + if (targetValue == null) { + return ValuesMissing.INSTANCE; + } + PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey, + NoSuchPackageException.class); + if (packageValue == null) { + return ValuesMissing.INSTANCE; + } + + packageLoadedSuccessfully = true; + try { + target = packageValue.getPackage().getTarget(label.getName()); + } catch (NoSuchTargetException unexpected) { + // Not expected since the TargetMarkerFunction would have failed earlier if the Target + // was not present. + throw new IllegalStateException(unexpected); + } + } catch (NoSuchTargetException e) { + if (!e.hasTarget()) { + throw e; + } + + // We know that a Target may be extracted, but we need to get it out of the Package + // (which is known to be in error). + Package pkg; + try { + PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey, + NoSuchPackageException.class); + if (packageValue == null) { + return ValuesMissing.INSTANCE; + } + throw new IllegalStateException( + "Expected bad package: " + label.getPackageIdentifier()); + } catch (NoSuchPackageException nsp) { + pkg = Preconditions.checkNotNull(nsp.getPackage(), label.getPackageIdentifier()); + } + try { + target = pkg.getTarget(label.getName()); + } catch (NoSuchTargetException nste) { + throw new IllegalStateException("Expected target to exist", nste); + } + + errorLoadingTarget = e; + packageLoadedSuccessfully = false; + } + return new TargetAndErrorIfAnyImpl(packageLoadedSuccessfully, errorLoadingTarget, target); + } + + /** + * Used to declare all the exception types that can be wrapped in the exception thrown by + * {@link TransitiveTraversalFunction#compute}. + */ + static class TransitiveBaseTraversalFunctionException extends SkyFunctionException { + /** + * Used to propagate an error from a direct target dependency to the target that depended on + * it. + */ + public TransitiveBaseTraversalFunctionException(NoSuchPackageException e) { + super(e, Transience.PERSISTENT); + } + + /** + * In nokeep_going mode, used to propagate an error from a direct target dependency to the + * target that depended on it. + * + * <p>In keep_going mode, used the same way, but only for targets that could not be loaded at + * all (we proceed with transitive loading on targets that contain errors).</p> + */ + public TransitiveBaseTraversalFunctionException(NoSuchTargetException e) { + super(e, Transience.PERSISTENT); + } + } +} |