diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java new file mode 100644 index 0000000000..417cfca06e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java @@ -0,0 +1,234 @@ +// Copyright 2014 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.collect.Lists; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.EventHandler; +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.PackageGroup; +import com.google.devtools.build.lib.packages.PackageIdentifier; +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.ValueOrException; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This class builds transitive Target values such that evaluating a Target value is similar to + * running it through the LabelVisitor. + */ +public class TransitiveTargetFunction implements SkyFunction { + + @Override + public SkyValue compute(SkyKey key, Environment env) throws TransitiveTargetFunctionException { + Label label = (Label) key.argument(); + SkyKey packageKey = PackageValue.key(label.getPackageIdentifier()); + SkyKey targetKey = TargetMarkerValue.key(label); + Target target; + boolean packageLoadedSuccessfully; + boolean successfulTransitiveLoading = true; + NestedSetBuilder<Label> transitiveRootCauses = NestedSetBuilder.stableOrder(); + NoSuchTargetException errorLoadingTarget = null; + try { + TargetMarkerValue targetValue = (TargetMarkerValue) env.getValueOrThrow(targetKey, + NoSuchThingException.class); + if (targetValue == null) { + return null; + } + PackageValue packageValue = (PackageValue) env.getValueOrThrow(packageKey, + NoSuchThingException.class); + if (packageValue == null) { + return null; + } + + packageLoadedSuccessfully = true; + target = packageValue.getPackage().getTarget(label.getName()); + } catch (NoSuchTargetException e) { + target = e.getTarget(); + if (target == null) { + throw new TransitiveTargetFunctionException(e); + } + successfulTransitiveLoading = false; + transitiveRootCauses.add(label); + errorLoadingTarget = e; + packageLoadedSuccessfully = e.getPackageLoadedSuccessfully(); + } catch (NoSuchPackageException e) { + throw new TransitiveTargetFunctionException(e); + } catch (NoSuchThingException e) { + throw new IllegalStateException(e + + " not NoSuchTargetException or NoSuchPackageException"); + } + + NestedSetBuilder<PackageIdentifier> transitiveSuccessfulPkgs = NestedSetBuilder.stableOrder(); + NestedSetBuilder<PackageIdentifier> transitiveUnsuccessfulPkgs = NestedSetBuilder.stableOrder(); + NestedSetBuilder<Label> transitiveTargets = NestedSetBuilder.stableOrder(); + + PackageIdentifier packageId = target.getPackage().getPackageIdentifier(); + if (packageLoadedSuccessfully) { + transitiveSuccessfulPkgs.add(packageId); + } else { + transitiveUnsuccessfulPkgs.add(packageId); + } + transitiveTargets.add(target.getLabel()); + for (Map.Entry<SkyKey, ValueOrException<NoSuchThingException>> entry : + env.getValuesOrThrow(getLabelDepKeys(target), NoSuchThingException.class).entrySet()) { + Label depLabel = (Label) entry.getKey().argument(); + TransitiveTargetValue transitiveTargetValue; + try { + transitiveTargetValue = (TransitiveTargetValue) entry.getValue().get(); + if (transitiveTargetValue == null) { + continue; + } + } catch (NoSuchPackageException | NoSuchTargetException e) { + successfulTransitiveLoading = false; + transitiveRootCauses.add(depLabel); + maybeReportErrorAboutMissingEdge(target, depLabel, e, env.getListener()); + continue; + } catch (NoSuchThingException e) { + throw new IllegalStateException("Unexpected Exception type from TransitiveTargetValue.", e); + } + transitiveSuccessfulPkgs.addTransitive( + transitiveTargetValue.getTransitiveSuccessfulPackages()); + transitiveUnsuccessfulPkgs.addTransitive( + transitiveTargetValue.getTransitiveUnsuccessfulPackages()); + transitiveTargets.addTransitive(transitiveTargetValue.getTransitiveTargets()); + NestedSet<Label> rootCauses = transitiveTargetValue.getTransitiveRootCauses(); + if (rootCauses != null) { + successfulTransitiveLoading = false; + transitiveRootCauses.addTransitive(rootCauses); + if (transitiveTargetValue.getErrorLoadingTarget() != null) { + maybeReportErrorAboutMissingEdge(target, depLabel, + transitiveTargetValue.getErrorLoadingTarget(), env.getListener()); + } + } + } + + if (env.valuesMissing()) { + return null; + } + + NestedSet<PackageIdentifier> successfullyLoadedPackages = transitiveSuccessfulPkgs.build(); + NestedSet<PackageIdentifier> unsuccessfullyLoadedPackages = transitiveUnsuccessfulPkgs.build(); + NestedSet<Label> loadedTargets = transitiveTargets.build(); + if (successfulTransitiveLoading) { + return TransitiveTargetValue.successfulTransitiveLoading(successfullyLoadedPackages, + unsuccessfullyLoadedPackages, loadedTargets); + } else { + NestedSet<Label> rootCauses = transitiveRootCauses.build(); + return TransitiveTargetValue.unsuccessfulTransitiveLoading(successfullyLoadedPackages, + unsuccessfullyLoadedPackages, loadedTargets, rootCauses, errorLoadingTarget); + } + } + + @Override + public String extractTag(SkyKey skyKey) { + return Label.print(((Label) skyKey.argument())); + } + + private static 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.getPackageName().equals(depLabel.getPackageName())) { + eventHandler.handle(Event.error(TargetUtils.getLocationMaybe(target), + TargetUtils.formatMissingEdge(target, depLabel, e))); + } + } + } + + private static Iterable<SkyKey> getLabelDepKeys(Target target) { + List<SkyKey> depKeys = Lists.newArrayList(); + for (Label depLabel : getLabelDeps(target)) { + depKeys.add(TransitiveTargetValue.key(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); + labels.addAll(((Rule) target).getLabels(Rule.NO_NODEP_ATTRIBUTES)); + } else if (target instanceof PackageGroup) { + visitPackageGroup((PackageGroup) target, labels); + } + return labels; + } + + private static void visitTargetVisibility(Target target, Set<Label> labels) { + for (Label label : target.getVisibility().getDependencyLabels()) { + labels.add(label); + } + } + + private static void visitPackageGroup(PackageGroup packageGroup, Set<Label> labels) { + for (final Label include : packageGroup.getIncludes()) { + labels.add(include); + } + } + + /** + * Used to declare all the exception types that can be wrapped in the exception thrown by + * {@link TransitiveTargetFunction#compute}. + */ + private static class TransitiveTargetFunctionException extends SkyFunctionException { + /** + * Used to propagate an error from a direct target dependency to the + * target that depended on it. + */ + public TransitiveTargetFunctionException(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. + * + * 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). + */ + public TransitiveTargetFunctionException(NoSuchTargetException e) { + super(e, Transience.PERSISTENT); + } + } +} |