Update from Google.

--
MOE_MIGRATED_REVID=85702957
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
new file mode 100644
index 0000000..2525c8d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
@@ -0,0 +1,341 @@
+// 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.actions;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action.MiddlemanType;
+import com.google.devtools.build.lib.actions.cache.ActionCache;
+import com.google.devtools.build.lib.actions.cache.Digest;
+import com.google.devtools.build.lib.actions.cache.Metadata;
+import com.google.devtools.build.lib.actions.cache.MetadataHandler;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.events.EventKind;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * Checks whether an {@link Action} needs to be executed, or whether it has not changed since it was
+ * last stored in the action cache. Must be informed of the new Action data after execution as well.
+ *
+ * <p>The fingerprint, input files names, and metadata (either mtimes or MD5sums) of each action are
+ * cached in the action cache to avoid unnecessary rebuilds. Middleman artifacts are handled
+ * specially, avoiding the need to create actual files corresponding to the middleman artifacts.
+ * Instead of that, results of MiddlemanAction dependency checks are cached internally and then
+ * reused whenever an input middleman artifact is encountered.
+ *
+ * <p>While instances of this class hold references to action and metadata cache instances, they are
+ * otherwise lightweight, and should be constructed anew and discarded for each build request.
+ */
+public class ActionCacheChecker {
+  private final ActionCache actionCache;
+  private final Predicate<? super Action> executionFilter;
+  private final ArtifactResolver artifactResolver;
+  // True iff --verbose_explanations flag is set.
+  private final boolean verboseExplanations;
+
+  public ActionCacheChecker(ActionCache actionCache, ArtifactResolver artifactResolver,
+      Predicate<? super Action> executionFilter, boolean verboseExplanations) {
+    this.actionCache = actionCache;
+    this.executionFilter = executionFilter;
+    this.artifactResolver = artifactResolver;
+    this.verboseExplanations = verboseExplanations;
+  }
+
+  public boolean isActionExecutionProhibited(Action action) {
+    return !executionFilter.apply(action);
+  }
+
+  /**
+   * Checks whether one of existing output paths is already used as a key.
+   * If yes, returns it - otherwise uses first output file as a key
+   */
+  private ActionCache.Entry getCacheEntry(Action action) {
+    for (Artifact output : action.getOutputs()) {
+      ActionCache.Entry entry = actionCache.get(output.getExecPathString());
+      if (entry != null) {
+        return entry;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Validate metadata state for action input or output artifacts.
+   *
+   * @param entry cached action information.
+   * @param action action to be validated.
+   * @param metadataHandler provider of metadata for the artifacts this action interacts with.
+   * @param checkOutput true to validate output artifacts, Otherwise, just
+   *                    validate inputs.
+   *
+   * @return true if at least one artifact has changed, false - otherwise.
+   */
+  private boolean validateArtifacts(ActionCache.Entry entry, Action action,
+      MetadataHandler metadataHandler, boolean checkOutput) {
+    Iterable<Artifact> artifacts = checkOutput
+        ? Iterables.concat(action.getOutputs(), action.getInputs())
+        : action.getInputs();
+    Map<String, Metadata> mdMap = new HashMap<>();
+    for (Artifact artifact : artifacts) {
+      mdMap.put(artifact.getExecPathString(), metadataHandler.getMetadataMaybe(artifact));
+    }
+    return !Digest.fromMetadata(mdMap).equals(entry.getFileDigest());
+  }
+
+  private void reportCommand(EventHandler handler, Action action) {
+    if (handler != null) {
+      if (verboseExplanations) {
+        String keyDescription = action.describeKey();
+        reportRebuild(handler, action,
+            keyDescription == null ? "action command has changed" :
+            "action command has changed.\nNew action: " + keyDescription);
+      } else {
+        reportRebuild(handler, action,
+            "action command has changed (try --verbose_explanations for more info)");
+      }
+    }
+  }
+
+  protected boolean unconditionalExecution(Action action) {
+    return !isActionExecutionProhibited(action) && action.executeUnconditionally();
+  }
+
+  /**
+   * Checks whether {@code action} needs to be executed and returns a non-null Token if so.
+   *
+   * <p>The method checks if any of the action's inputs or outputs have changed. Returns a non-null
+   * {@link Token} if the action needs to be executed, and null otherwise.
+   *
+   * <p>If this method returns non-null, indicating that the action will be executed, the
+   * metadataHandler's {@link MetadataHandler#discardMetadata} method must be called, so that it
+   * does not serve stale metadata for the action's outputs after the action is executed.
+   */
+  // Note: the handler should only be used for DEPCHECKER events; there's no
+  // guarantee it will be available for other events.
+  public Token getTokenIfNeedToExecute(Action action, EventHandler handler,
+      MetadataHandler metadataHandler) {
+    // TODO(bazel-team): (2010) For RunfilesAction/SymlinkAction and similar actions that
+    // produce only symlinks we should not check whether inputs are valid at all - all that matters
+    // that inputs and outputs are still exist (and new inputs have not appeared). All other checks
+    // are unnecessary. In other words, the only metadata we should check for them is file existence
+    // itself.
+
+    MiddlemanType middlemanType = action.getActionType();
+    if (middlemanType.isMiddleman()) {
+      // Some types of middlemen are not checked because they should not
+      // propagate invalidation of their inputs.
+      if (middlemanType != MiddlemanType.ERROR_PROPAGATING_MIDDLEMAN) {
+        checkMiddlemanAction(action, handler, metadataHandler);
+      }
+      return null;
+    }
+    ActionCache.Entry entry = null; // Populated lazily.
+
+    // Update action inputs from cache, if necessary.
+    boolean inputsKnown = action.inputsKnown();
+    if (!inputsKnown) {
+      Preconditions.checkState(action.discoversInputs());
+      entry = getCacheEntry(action);
+      updateActionInputs(action, entry);
+    }
+    if (mustExecute(action, entry, handler, metadataHandler)) {
+      return new Token(getKeyString(action));
+    }
+    return null;
+  }
+
+  protected boolean mustExecute(Action action, @Nullable ActionCache.Entry entry,
+      EventHandler handler, MetadataHandler metadataHandler) {
+    // Unconditional execution can be applied only for actions that are allowed to be executed.
+    if (unconditionalExecution(action)) {
+      Preconditions.checkState(action.isVolatile());
+      reportUnconditionalExecution(handler, action);
+      return true; // must execute - unconditional execution is requested.
+    }
+
+    if (entry == null) {
+      entry = getCacheEntry(action);
+    }
+    if (entry == null) {
+      reportNewAction(handler, action);
+      return true; // must execute -- no cache entry (e.g. first build)
+    }
+
+    if (entry.isCorrupted()) {
+      reportCorruptedCacheEntry(handler, action);
+      return true; // cache entry is corrupted - must execute
+    } else if (validateArtifacts(entry, action, metadataHandler, true)) {
+      reportChanged(handler, action);
+      return true; // files have changed
+    } else if (!entry.getActionKey().equals(action.getKey())){
+      reportCommand(handler, action);
+      return true; // must execute -- action key is different
+    }
+
+    entry.getFileDigest();
+    return false; // cache hit
+  }
+
+  public void afterExecution(Action action, Token token, MetadataHandler metadataHandler)
+      throws IOException {
+    Preconditions.checkArgument(token != null);
+    String key = token.cacheKey;
+    ActionCache.Entry entry = actionCache.createEntry(action.getKey());
+    for (Artifact output : action.getOutputs()) {
+      // Remove old records from the cache if they used different key.
+      String execPath = output.getExecPathString();
+      if (!key.equals(execPath)) {
+        actionCache.remove(key);
+      }
+      // Output files *must* exist and be accessible after successful action execution.
+      Metadata metadata = metadataHandler.getMetadata(output);
+      Preconditions.checkState(metadata != null);
+      entry.addFile(output.getExecPath(), metadata);
+    }
+    for (Artifact input : action.getInputs()) {
+      entry.addFile(input.getExecPath(), metadataHandler.getMetadataMaybe(input));
+    }
+    entry.getFileDigest();
+    actionCache.put(key, entry);
+  }
+
+  protected void updateActionInputs(Action action, ActionCache.Entry entry) {
+    if (entry == null || entry.isCorrupted()) {
+      return;
+    }
+
+    List<PathFragment> outputs = new ArrayList<>();
+    for (Artifact output : action.getOutputs()) {
+      outputs.add(output.getExecPath());
+    }
+    List<PathFragment> inputs = new ArrayList<>();
+    for (String path : entry.getPaths()) {
+      PathFragment execPath = new PathFragment(path);
+      // Code assumes that action has only 1-2 outputs and ArrayList.contains() will be
+      // most efficient.
+      if (!outputs.contains(execPath)) {
+        inputs.add(execPath);
+      }
+    }
+    action.updateInputsFromCache(artifactResolver, inputs);
+  }
+
+  /**
+   * Special handling for the MiddlemanAction. Since MiddlemanAction output
+   * artifacts are purely fictional and used only to stay within dependency
+   * graph model limitations (action has to depend on artifacts, not on other
+   * actions), we do not need to validate metadata for the outputs - only for
+   * inputs. We also do not need to validate MiddlemanAction key, since action
+   * cache entry key already incorporates that information for the middlemen
+   * and we will experience a cache miss when it is different. Whenever it
+   * encounters middleman artifacts as input artifacts for other actions, it
+   * consults with the aggregated middleman digest computed here.
+   */
+  protected void checkMiddlemanAction(Action action, EventHandler handler,
+      MetadataHandler metadataHandler) {
+    Artifact middleman = action.getPrimaryOutput();
+    String cacheKey = middleman.getExecPathString();
+    ActionCache.Entry entry = actionCache.get(cacheKey);
+    boolean changed = false;
+    if (entry != null) {
+      if (entry.isCorrupted()) {
+        reportCorruptedCacheEntry(handler, action);
+        changed = true;
+      } else if (validateArtifacts(entry, action, metadataHandler, false)) {
+        reportChanged(handler, action);
+        changed = true;
+      }
+    } else {
+      reportChangedDeps(handler, action);
+      changed = true;
+    }
+    if (changed) {
+      // Compute the aggregated middleman digest.
+      // Since we never validate action key for middlemen, we should not store
+      // it in the cache entry and just use empty string instead.
+      entry = actionCache.createEntry("");
+      for (Artifact input : action.getInputs()) {
+        entry.addFile(input.getExecPath(), metadataHandler.getMetadataMaybe(input));
+      }
+    }
+
+    metadataHandler.setDigestForVirtualArtifact(middleman, entry.getFileDigest());
+    if (changed) {
+      actionCache.put(cacheKey, entry);
+    }
+  }
+
+  /**
+   * Returns an action key. It is always set to the first output exec path string.
+   */
+  private static String getKeyString(Action action) {
+    Preconditions.checkState(!action.getOutputs().isEmpty());
+    return action.getOutputs().iterator().next().getExecPathString();
+  }
+
+
+  /**
+   * In most cases, this method should not be called directly - reportXXX() methods
+   * should be used instead. This is done to avoid cost associated with building
+   * the message.
+   */
+  private static void reportRebuild(@Nullable EventHandler handler, Action action, String message) {
+    // For MiddlemanAction, do not report rebuild.
+    if (handler != null && !action.getActionType().isMiddleman()) {
+      handler.handle(new Event(
+          EventKind.DEPCHECKER, null, "Executing " + action.prettyPrint() + ": " + message + "."));
+    }
+  }
+
+  // Called by IncrementalDependencyChecker.
+  protected static void reportUnconditionalExecution(
+      @Nullable EventHandler handler, Action action) {
+    reportRebuild(handler, action, "unconditional execution is requested");
+  }
+
+  private static void reportChanged(@Nullable EventHandler handler, Action action) {
+    reportRebuild(handler, action, "One of the files has changed");
+  }
+
+  private static void reportChangedDeps(@Nullable EventHandler handler, Action action) {
+    reportRebuild(handler, action, "the set of files on which this action depends has changed");
+  }
+
+  private static void reportNewAction(@Nullable EventHandler handler, Action action) {
+    reportRebuild(handler, action, "no entry in the cache (action is new)");
+  }
+
+  private static void reportCorruptedCacheEntry(@Nullable EventHandler handler, Action action) {
+    reportRebuild(handler, action, "cache entry is corrupted");
+  }
+
+  /** Wrapper for all context needed by the ActionCacheChecker to handle a single action. */
+  public static final class Token {
+    private final String cacheKey;
+
+    private Token(String cacheKey) {
+      this.cacheKey = Preconditions.checkNotNull(cacheKey);
+    }
+  }
+}