Initial (partial) implementation of configured target query. Activated by passing the --post_build_query flag to a build command, with a query expression as the argument. Bazel then executes this query on the configured target graph as constructed by the build command.

Since the prepare graph -> query workflow is how SkyQueryEnvironment works, this is mostly just copying that.

Main missing features/code cleanups:
* Recursive target patterns (/...) are not supported.
* There is no way to specify the configuration of the targets in your query.
* Configuration output is totally opaque (just the hash, or null if no configuration).
* More generally, no output options.
* Some features (visibility, label attrs) not supported.
* No edge filtering (host deps, implicit deps).
* Aspects are totally ignored.
* Graceful failure on errors, edge cases, incompatible flags (like the TAP flags that discard edges).
* Code hygiene issues (calling test-only method to get to Skyframe graph, some code duplication across ConfiguredTargetQueryEnvironment and SkyQueryEnvironment).

Most of the features I plan to leave to rules-side people, since I think they won't be too hard for a general Blaze developer to implement, and designing the right features and user interfaces for these things is better left to the rules side.

PiperOrigin-RevId: 165747829
diff --git a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
index 383a28f..0fb0283 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
@@ -49,7 +49,6 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventKind;
 import com.google.devtools.build.lib.events.ExtendedEventHandler;
-import com.google.devtools.build.lib.graph.Digraph;
 import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
 import com.google.devtools.build.lib.packages.DependencyFilter;
 import com.google.devtools.build.lib.packages.NoSuchPackageException;
@@ -107,11 +106,8 @@
 import com.google.devtools.build.skyframe.WalkableGraph;
 import com.google.devtools.build.skyframe.WalkableGraph.WalkableGraphFactory;
 import java.io.IOException;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Deque;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -513,7 +509,7 @@
     return getReverseDepsOfTransitiveTraversalKeys(Iterables.transform(targets, TARGET_TO_SKY_KEY));
   }
 
-  Collection<Target> getReverseDepsOfTransitiveTraversalKeys(
+  private Collection<Target> getReverseDepsOfTransitiveTraversalKeys(
       Iterable<SkyKey> transitiveTraversalKeys) throws InterruptedException {
     Map<SkyKey, Collection<Target>> rawReverseDeps = getRawReverseDeps(transitiveTraversalKeys);
     return processRawReverseDeps(rawReverseDeps);
@@ -563,51 +559,14 @@
   @Override
   public ThreadSafeMutableSet<Target> getTransitiveClosure(ThreadSafeMutableSet<Target> targets)
       throws InterruptedException {
-    ThreadSafeMutableSet<Target> visited = createThreadSafeMutableSet();
-    ThreadSafeMutableSet<Target> current = targets;
-    while (!current.isEmpty()) {
-      Iterable<Target> toVisit = Iterables.filter(current,
-          Predicates.not(Predicates.in(visited)));
-      current = getFwdDeps(toVisit);
-      Iterables.addAll(visited, toVisit);
-    }
-    return visited;
+    return SkyQueryUtils.getTransitiveClosure(
+        targets, this::getFwdDeps, createThreadSafeMutableSet());
   }
 
-  // Implemented with a breadth-first search.
   @Override
   public ImmutableList<Target> getNodesOnPath(Target from, Target to)
       throws InterruptedException {
-    // Tree of nodes visited so far.
-    Map<Label, Label> nodeToParent = new HashMap<>();
-    Map<Label, Target> labelToTarget = new HashMap<>();
-    // Contains all nodes left to visit in a (LIFO) stack.
-    Deque<Target> toVisit = new ArrayDeque<>();
-    toVisit.add(from);
-    nodeToParent.put(from.getLabel(), null);
-    labelToTarget.put(from.getLabel(), from);
-    while (!toVisit.isEmpty()) {
-      Target current = toVisit.removeFirst();
-      if (to.getLabel().equals(current.getLabel())) {
-        List<Label> labelPath = Digraph.getPathToTreeNode(nodeToParent, to.getLabel());
-        ImmutableList.Builder<Target> targetPathBuilder = ImmutableList.builder();
-        for (Label label : labelPath) {
-          targetPathBuilder.add(Preconditions.checkNotNull(labelToTarget.get(label), label));
-        }
-        return targetPathBuilder.build();
-      }
-      for (Target dep : getFwdDeps(ImmutableList.of(current))) {
-        Label depLabel = dep.getLabel();
-        if (!nodeToParent.containsKey(depLabel)) {
-          nodeToParent.put(depLabel, current.getLabel());
-          labelToTarget.put(depLabel, dep);
-          toVisit.addFirst(dep);
-        }
-      }
-    }
-    // Note that the only current caller of this method checks first to see if there is a path
-    // before calling this method. It is not clear what the return value should be here.
-    return null;
+    return SkyQueryUtils.getNodesOnPath(from, to, this::getFwdDeps, Target::getLabel);
   }
 
   private <R> ListenableFuture<R> safeSubmit(Callable<R> callable) {