Update from Google.

--
MOE_MIGRATED_REVID=85702957
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CachingAnalysisEnvironment.java b/src/main/java/com/google/devtools/build/lib/analysis/CachingAnalysisEnvironment.java
new file mode 100644
index 0000000..bc45ba3
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CachingAnalysisEnvironment.java
@@ -0,0 +1,303 @@
+// 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.analysis;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactFactory;
+import com.google.devtools.build.lib.actions.ArtifactOwner;
+import com.google.devtools.build.lib.actions.MiddlemanFactory;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoCollection;
+import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
+import com.google.devtools.build.lib.analysis.config.BinTools;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue;
+import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.skyframe.SkyFunction;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.annotation.Nullable;
+
+/**
+ * The implementation of AnalysisEnvironment used for analysis. It tracks metadata for each
+ * configured target, such as the errors and warnings emitted by that target. It is intended that
+ * a separate instance is used for each configured target, so that these don't mix up.
+ */
+public class CachingAnalysisEnvironment implements AnalysisEnvironment {
+  private final ArtifactFactory artifactFactory;
+
+  private final ArtifactOwner owner;
+  /**
+   * If this is the system analysis environment, then errors and warnings are directly reported
+   * to the global reporter, rather than stored, i.e., we don't track here whether there are any
+   * errors.
+   */
+  private final boolean isSystemEnv;
+  private final boolean extendedSanityChecks;
+
+  /**
+   * If false, no actions will be registered, they'll all be just dropped.
+   *
+   * <p>Usually, an analysis environment should register all actions. However, in some scenarios we
+   * analyze some targets twice, but the first one only serves the purpose of collecting information
+   * for the second analysis. In this case we don't register actions created by the first pass in
+   * order to avoid action conflicts.
+   */
+  private final boolean allowRegisteringActions;
+
+  private boolean enabled = true;
+  private MiddlemanFactory middlemanFactory;
+  private EventHandler errorEventListener;
+  private SkyFunction.Environment skyframeEnv;
+  private Map<Artifact, String> artifacts;
+  private final BinTools binTools;
+
+  /**
+   * The list of actions registered by the configured target this analysis environment is
+   * responsible for. May get cleared out at the end of the analysis of said target.
+   */
+  final List<Action> actions = new ArrayList<>();
+
+  public CachingAnalysisEnvironment(ArtifactFactory artifactFactory,
+      ArtifactOwner owner, boolean isSystemEnv, boolean extendedSanityChecks,
+      EventHandler errorEventListener, SkyFunction.Environment env, boolean allowRegisteringActions,
+      BinTools binTools) {
+    this.artifactFactory = artifactFactory;
+    this.owner = Preconditions.checkNotNull(owner);
+    this.isSystemEnv = isSystemEnv;
+    this.extendedSanityChecks = extendedSanityChecks;
+    this.errorEventListener = errorEventListener;
+    this.skyframeEnv = env;
+    this.allowRegisteringActions = allowRegisteringActions;
+    this.binTools = binTools;
+    middlemanFactory = new MiddlemanFactory(artifactFactory, this);
+    artifacts = new HashMap<>();
+  }
+
+  public void disable(Target target) {
+    if (!hasErrors() && allowRegisteringActions) {
+      verifyGeneratedArtifactHaveActions(target);
+    }
+    artifacts = null;
+    middlemanFactory = null;
+    enabled = false;
+    errorEventListener = null;
+    skyframeEnv = null;
+  }
+
+  private static StringBuilder shortDescription(Action action) {
+    if (action == null) {
+      return new StringBuilder("null Action");
+    }
+    return new StringBuilder()
+      .append(action.getClass().getName())
+      .append(' ')
+      .append(action.getMnemonic());
+  }
+
+  /**
+   * Sanity checks that all generated artifacts have a generating action.
+   * @param target for error reporting
+   */
+  public void verifyGeneratedArtifactHaveActions(Target target) {
+    Collection<String> orphanArtifacts = getOrphanArtifactMap().values();
+    List<String> checkedActions = null;
+    if (!orphanArtifacts.isEmpty()) {
+      checkedActions = Lists.newArrayListWithCapacity(actions.size());
+      for (Action action : actions) {
+        StringBuilder sb = shortDescription(action);
+        for (Artifact o : action.getOutputs()) {
+          sb.append("\n    ");
+          sb.append(o.getExecPathString());
+        }
+        checkedActions.add(sb.toString());
+      }
+      throw new IllegalStateException(
+          String.format(
+              "%s %s : These artifacts miss a generating action:\n%s\n"
+              + "These actions we checked:\n%s\n",
+              target.getTargetKind(), target.getLabel(),
+              Joiner.on('\n').join(orphanArtifacts), Joiner.on('\n').join(checkedActions)));
+    }
+  }
+
+  @Override
+  public ImmutableSet<Artifact> getOrphanArtifacts() {
+    return ImmutableSet.copyOf(getOrphanArtifactMap().keySet());
+  }
+
+  private Map<Artifact, String> getOrphanArtifactMap() {
+    // Construct this set to avoid poor performance under large --runs_per_test.
+    Set<Artifact> artifactsWithActions = new HashSet<>();
+    for (Action action : actions) {
+      // Don't bother checking that every Artifact only appears once; that test is performed
+      // elsewhere (see #testNonUniqueOutputs in ActionListenerIntegrationTest).
+      artifactsWithActions.addAll(action.getOutputs());
+    }
+    // The order of the artifacts.entrySet iteration is unspecified - we use a TreeMap here to
+    // guarantee that the return value of this method is deterministic.
+    Map<Artifact, String> orphanArtifacts = new TreeMap<>();
+    for (Map.Entry<Artifact, String> entry : artifacts.entrySet()) {
+      Artifact a = entry.getKey();
+      if (!a.isSourceArtifact() && !artifactsWithActions.contains(a)) {
+        orphanArtifacts.put(a, String.format("%s\n%s",
+            a.getExecPathString(),  // uncovered artifact
+            entry.getValue()));  // origin of creation
+      }
+    }
+    return orphanArtifacts;
+  }
+
+  @Override
+  public EventHandler getEventHandler() {
+    return errorEventListener;
+  }
+
+  @Override
+  public boolean hasErrors() {
+    // The system analysis environment never has errors.
+    if (isSystemEnv) {
+      return false;
+    }
+    Preconditions.checkState(enabled);
+    return ((StoredEventHandler) errorEventListener).hasErrors();
+  }
+
+  @Override
+  public MiddlemanFactory getMiddlemanFactory() {
+    Preconditions.checkState(enabled);
+    return middlemanFactory;
+  }
+
+  /**
+   * Keeps track of artifacts. We check that all of them have an owner when the environment is
+   * sealed (disable()). For performance reasons we only track the originating stacktrace when
+   * running with --experimental_extended_sanity_checks.
+   */
+  private Artifact trackArtifactAndOrigin(Artifact a, @Nullable Throwable e) {
+    if ((e != null) && !artifacts.containsKey(a)) {
+      StringWriter sw = new StringWriter();
+      e.printStackTrace(new PrintWriter(sw));
+      artifacts.put(a, sw.toString());
+    } else {
+      artifacts.put(a, "No origin, run with --experimental_extended_sanity_checks");
+    }
+    return a;
+  }
+
+  @Override
+  public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
+    Preconditions.checkState(enabled);
+    return trackArtifactAndOrigin(
+        artifactFactory.getDerivedArtifact(rootRelativePath, root, getOwner()),
+        extendedSanityChecks ? new Throwable() : null);
+  }
+
+  @Override
+  public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
+    Preconditions.checkState(enabled);
+    return trackArtifactAndOrigin(
+        artifactFactory.getFilesetArtifact(rootRelativePath, root, getOwner()),
+        extendedSanityChecks ? new Throwable() : null);
+  }
+
+  @Override
+  public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
+    return artifactFactory.getConstantMetadataArtifact(rootRelativePath, root, getOwner());
+  }
+
+  @Override
+  public Artifact getEmbeddedToolArtifact(String embeddedPath) {
+    Preconditions.checkState(enabled);
+    return binTools.getEmbeddedArtifact(embeddedPath, artifactFactory);
+  }
+
+  @Override
+  public void registerAction(Action... actions) {
+    Preconditions.checkState(enabled);
+    if (allowRegisteringActions) {
+      for (Action action : actions) {
+        this.actions.add(action);
+      }
+    }
+  }
+
+  @Override
+  public Action getLocalGeneratingAction(Artifact artifact) {
+    Preconditions.checkState(allowRegisteringActions);
+    for (Action action : actions) {
+      if (action.getOutputs().contains(artifact)) {
+        return action;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public Collection<Action> getRegisteredActions() {
+    return Collections.unmodifiableCollection(actions);
+  }
+
+  @Override
+  public SkyFunction.Environment getSkyframeEnv() {
+    return skyframeEnv;
+  }
+
+  @Override
+  public Artifact getStableWorkspaceStatusArtifact() {
+    return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
+            .getStableArtifact();
+  }
+
+  @Override
+  public Artifact getVolatileWorkspaceStatusArtifact() {
+    return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
+            .getVolatileArtifact();
+  }
+
+  @Override
+  public ImmutableList<Artifact> getBuildInfo(RuleContext ruleContext, BuildInfoKey key) {
+    boolean stamp = AnalysisUtils.isStampingEnabled(ruleContext);
+    BuildInfoCollection collection =
+        ((BuildInfoCollectionValue) skyframeEnv.getValue(BuildInfoCollectionValue.key(
+        new BuildInfoCollectionValue.BuildInfoKeyAndConfig(key, ruleContext.getConfiguration()))))
+        .getCollection();
+   return stamp ? collection.getStampedBuildInfo() : collection.getRedactedBuildInfo();
+  }
+
+  @Override
+  public ArtifactOwner getOwner() {
+    return owner;
+  }
+}