aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java
diff options
context:
space:
mode:
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.java234
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);
+ }
+ }
+}