On-the-fly target pattern resolution in SkyQueryEnvironment

Moves pattern resolving logic from TargetPatternFunction.Resolver to
a top level class. Adds a layer of abstraction to the Resolver
implementation enabling it to be backed by either an Environment or
a Graph, for use in SkyFunction evaluation or on-the-fly evaluation,
respectively. Finally, SkyQueryEnvironment#preloadOrThrow now checks
to see if each target pattern exists in the graph, and any that
don't will be resolved on-the-fly.

--
MOS_MIGRATED_REVID=88861201
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
index caca634..2c9d4ad 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPattern.java
@@ -117,8 +117,7 @@
    * Evaluates the current target pattern and returns the result.
    */
   public abstract <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
-      throws TargetParsingException, InterruptedException,
-      TargetPatternResolver.MissingDepException;
+      throws TargetParsingException, InterruptedException;
 
   private static final class SingleTarget extends TargetPattern {
 
@@ -131,8 +130,7 @@
 
     @Override
     public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
-        throws TargetParsingException, InterruptedException,
-        TargetPatternResolver.MissingDepException {
+        throws TargetParsingException, InterruptedException {
       return resolver.getExplicitTarget(targetName);
     }
   }
@@ -148,8 +146,7 @@
 
     @Override
     public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
-        throws TargetParsingException, InterruptedException,
-        TargetPatternResolver.MissingDepException {
+        throws TargetParsingException, InterruptedException {
       if (resolver.isPackage(path)) {
         // User has specified a package name. lookout for default target.
         return resolver.getExplicitTarget("//" + path);
@@ -194,8 +191,7 @@
 
     @Override
     public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
-        throws TargetParsingException, InterruptedException,
-        TargetPatternResolver.MissingDepException {
+        throws TargetParsingException, InterruptedException {
       if (checkWildcardConflict) {
         ResolvedTargets<T> targets = getWildcardConflict(resolver);
         if (targets != null) {
@@ -214,7 +210,7 @@
      *         is such a target. Otherwise, return null.
      */
     private <T> ResolvedTargets<T> getWildcardConflict(TargetPatternResolver<T> resolver)
-        throws InterruptedException, TargetPatternResolver.MissingDepException {
+        throws InterruptedException {
       if (!isAbsolute) {
         return null;
       }
@@ -255,8 +251,7 @@
 
     @Override
     public <T> ResolvedTargets<T> eval(TargetPatternResolver<T> resolver)
-        throws TargetParsingException, InterruptedException,
-        TargetPatternResolver.MissingDepException {
+        throws TargetParsingException, InterruptedException {
       return resolver.findTargetsBeneathDirectory(originalPattern, pathPrefix, rulesOnly);
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
index 109179f..ae91caa 100644
--- a/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/cmdline/TargetPatternResolver.java
@@ -32,13 +32,13 @@
    * Returns a single target corresponding to the given name, or null. This method may only throw an
    * exception if the current thread was interrupted.
    */
-  T getTargetOrNull(String targetName) throws InterruptedException, MissingDepException;
+  T getTargetOrNull(String targetName) throws InterruptedException;
 
   /**
    * Returns a single target corresponding to the given name, or an empty or failed result.
    */
   ResolvedTargets<T> getExplicitTarget(String targetName)
-      throws TargetParsingException, InterruptedException, MissingDepException;
+      throws TargetParsingException, InterruptedException;
 
   /**
    * Returns the set containing the targets found in the given package. The specified directory is
@@ -50,7 +50,7 @@
    * @param rulesOnly whether to return rules only
    */
   ResolvedTargets<T> getTargetsInPackage(String originalPattern, String packageName,
-      boolean rulesOnly) throws TargetParsingException, InterruptedException, MissingDepException;
+      boolean rulesOnly) throws TargetParsingException, InterruptedException;
 
   /**
    * Returns the set containing the targets found below the given {@code pathPrefix}. Conceptually,
@@ -71,24 +71,18 @@
    * @param rulesOnly whether to return rules only
    */
   ResolvedTargets<T> findTargetsBeneathDirectory(String originalPattern, String pathPrefix,
-      boolean rulesOnly) throws TargetParsingException, InterruptedException, MissingDepException;
+      boolean rulesOnly) throws TargetParsingException, InterruptedException;
 
   /**
    * Returns true, if and only if the given name corresponds to a package, i.e., a file with the
    * name {@code packageName/BUILD} exists.
    */
-  boolean isPackage(String packageName) throws MissingDepException;
+  boolean isPackage(String packageName);
 
   /**
    * Returns the target kind of the given target, for example {@code cc_library rule}.
    */
   String getTargetKind(T target);
 
-  /**
-   * A missing dependency is needed before target parsing can proceed. Currently used only in
-   * skyframe to notify the framework of missing dependencies.
-   */
-  // TODO(bazel-team): Avoid this use of exception for expected control flow management.
-  public class MissingDepException extends Exception {
-  }
+
 }
diff --git a/src/main/java/com/google/devtools/build/lib/pkgcache/PackageProvider.java b/src/main/java/com/google/devtools/build/lib/pkgcache/PackageProvider.java
index 0573768e..5363cb5 100644
--- a/src/main/java/com/google/devtools/build/lib/pkgcache/PackageProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/pkgcache/PackageProvider.java
@@ -55,6 +55,9 @@
    *
    * <p> If these don't hold, then attempting to read the package with {@link #getPackage} may fail
    * or may return a package containing errors.
+   *
+   * @param eventHandler the eventHandler on which to report warnings and errors
+   * @param packageName the name of the package.
    */
-  boolean isPackage(String packageName);
+  boolean isPackage(EventHandler eventHandler, String packageName);
 }
diff --git a/src/main/java/com/google/devtools/build/lib/pkgcache/RecursivePackageProvider.java b/src/main/java/com/google/devtools/build/lib/pkgcache/RecursivePackageProvider.java
index 8d7bd1d..5c17e92 100644
--- a/src/main/java/com/google/devtools/build/lib/pkgcache/RecursivePackageProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/pkgcache/RecursivePackageProvider.java
@@ -13,12 +13,8 @@
 // limitations under the License.
 package com.google.devtools.build.lib.pkgcache;
 
-import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.vfs.PathFragment;
-
-import java.util.concurrent.ThreadPoolExecutor;
-
-import javax.annotation.Nullable;
+import com.google.devtools.build.lib.vfs.RootedPath;
 
 /**
  * Support for resolving {@code package/...} target patterns.
@@ -26,32 +22,9 @@
 public interface RecursivePackageProvider extends PackageProvider {
 
   /**
-   * <p>Visits the names of all packages beneath the given directory recursively and concurrently.
-   *
-   * <p>Note: This operation needs to stat directories recursively. It could be very expensive when
-   * there is a big tree under the given directory.
-   *
-   * <p>Over a single iteration, package names are unique.
-   *
-   * <p>This method uses the given thread pool to call the observer method, possibly concurrently
-   * (depending on the thread pool). When this method terminates, however, all such threads will
-   * have completed.
-   *
-   * <p>To abort the traversal, call {@link Thread#interrupt()} on the calling thread.
-   *
-   * <p>This method guarantees that all BUILD files it returns correspond to valid package names
-   * that are not marked as deleted within the current build.
-   *
-   * @param eventHandler an eventHandler which should be used to log any errors that occur while
-   *    scanning directories for BUILD files
-   * @param directory a relative, canonical path specifying the directory to search
-   * @param useTopLevelExcludes whether to skip a pre-set list of top level directories
-   * @param visitorPool the thread pool to use to visit packages in parallel
-   * @param observer is called for each path fragment found; thread-safe if the thread pool supports
-   *    multiple parallel threads
-   * @throws InterruptedException if the calling thread was interrupted.
+   * Returns the names of all the packages under a given directory.
+
+   * @param directory a {@link RootedPath} specifying the directory to search
    */
-  void visitPackageNamesRecursively(EventHandler eventHandler, PathFragment directory,
-      boolean useTopLevelExcludes, @Nullable ThreadPoolExecutor visitorPool,
-      PathPackageLocator.AcceptsPathFragment observer) throws InterruptedException;
+  Iterable<PathFragment> getPackagesUnderDirectory(RootedPath directory);
 }
diff --git a/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java
index 96e8509..537738e 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/AbstractBlazeQueryEnvironment.java
@@ -26,6 +26,7 @@
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.pkgcache.PackageProvider;
+import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
 import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
 import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader;
 import com.google.devtools.build.lib.query2.engine.QueryEnvironment;
@@ -43,6 +44,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import javax.annotation.Nullable;
+
 /**
  * {@link QueryEnvironment} that can evaluate queries to produce a result, and implements as much
  * of QueryEnvironment as possible while remaining mostly agnostic as to the objects being stored.
@@ -92,10 +95,12 @@
       PackageProvider packageProvider,
       TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean orderedResults,
       List<String> universeScope, int loadingPhaseThreads,
-      EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions) {
+      EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
+      @Nullable PathPackageLocator packagePath) {
     return newQueryEnvironment(transitivePackageLoader, graphFactory, packageProvider,
         targetPatternEvaluator, keepGoing, /*strictScope=*/true, orderedResults,
-        universeScope, loadingPhaseThreads, Rule.ALL_LABELS, eventHandler, settings, functions);
+        universeScope, loadingPhaseThreads, Rule.ALL_LABELS, eventHandler, settings, functions,
+        packagePath);
   }
 
   public static AbstractBlazeQueryEnvironment<Target> newQueryEnvironment(
@@ -104,15 +109,17 @@
       TargetPatternEvaluator targetPatternEvaluator, boolean keepGoing, boolean strictScope,
       boolean orderedResults, List<String> universeScope, int loadingPhaseThreads,
       Predicate<Label> labelFilter,
-      EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions) {
+      EventHandler eventHandler, Set<Setting> settings, Iterable<QueryFunction> functions,
+      @Nullable PathPackageLocator packagePath) {
     Preconditions.checkNotNull(universeScope);
-    return orderedResults || universeScope.isEmpty()
+    return orderedResults || universeScope.isEmpty() || packagePath == null
         ? new BlazeQueryEnvironment(transitivePackageLoader, packageProvider,
         targetPatternEvaluator, keepGoing, strictScope, loadingPhaseThreads,
         labelFilter, eventHandler, settings, functions)
         : new SkyQueryEnvironment(
             keepGoing, strictScope, loadingPhaseThreads, labelFilter, eventHandler, settings,
-            functions, targetPatternEvaluator.getOffset(), graphFactory, universeScope);
+            functions, targetPatternEvaluator.getOffset(), graphFactory, universeScope,
+            packagePath);
   }
 
   /**
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 4a0dae6..0b01884 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
@@ -21,17 +21,21 @@
 import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.cmdline.ResolvedTargets;
 import com.google.devtools.build.lib.cmdline.TargetParsingException;
+import com.google.devtools.build.lib.cmdline.TargetPattern;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.graph.Digraph;
 import com.google.devtools.build.lib.packages.NoSuchThingException;
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
 import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
 import com.google.devtools.build.lib.query2.engine.QueryEvalResult;
 import com.google.devtools.build.lib.query2.engine.QueryException;
 import com.google.devtools.build.lib.query2.engine.QueryExpression;
+import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
 import com.google.devtools.build.lib.skyframe.PackageValue;
+import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver;
 import com.google.devtools.build.lib.skyframe.SkyFunctions;
 import com.google.devtools.build.lib.skyframe.TargetPatternValue;
 import com.google.devtools.build.lib.skyframe.TransitiveTargetValue;
@@ -69,6 +73,7 @@
   private final WalkableGraphFactory graphFactory;
   private final List<String> universeScope;
   private final String parserPrefix;
+  private final PathPackageLocator pkgPath;
 
   public SkyQueryEnvironment(boolean keepGoing, boolean strictScope, int loadingPhaseThreads,
       Predicate<Label> labelFilter,
@@ -76,13 +81,14 @@
       Set<Setting> settings,
       Iterable<QueryFunction> extraFunctions, String parserPrefix,
       WalkableGraphFactory graphFactory,
-      List<String> universeScope) {
+      List<String> universeScope, PathPackageLocator pkgPath) {
     super(keepGoing, strictScope, labelFilter,
         eventHandler,
         settings,
         extraFunctions);
     this.loadingPhaseThreads = loadingPhaseThreads;
     this.graphFactory = graphFactory;
+    this.pkgPath = pkgPath;
     this.universeScope = Preconditions.checkNotNull(universeScope);
     this.parserPrefix = parserPrefix;
     Preconditions.checkState(!universeScope.isEmpty(),
@@ -307,20 +313,40 @@
 
   protected Map<String, ResolvedTargets<Target>> preloadOrThrow(Collection<String> patterns)
       throws QueryException, TargetParsingException {
-    Map<String, ResolvedTargets<Target>> result =
-        Maps.newHashMapWithExpectedSize(patterns.size());
+    Map<String, ResolvedTargets<Target>> result = Maps.newHashMapWithExpectedSize(patterns.size());
     for (String pattern : patterns) {
       SkyKey patternKey = TargetPatternValue.key(pattern,
           TargetPatternEvaluator.DEFAULT_FILTERING_POLICY, parserPrefix);
-      checkExistence(patternKey);
-      TargetPatternValue value = (TargetPatternValue) graph.getValue(patternKey);
-      if (value != null) {
-        result.put(pattern, value.getTargets());
-      } else if (!keepGoing) {
-        throw (TargetParsingException) Preconditions.checkNotNull(graph.getException(patternKey),
-            pattern);
+
+      TargetPatternValue.TargetPattern targetPattern =
+          ((TargetPatternValue.TargetPattern) patternKey.argument());
+
+      if (graph.exists(patternKey)) {
+        // If the graph already contains a value for this target pattern, use it.
+        TargetPatternValue value = (TargetPatternValue) graph.getValue(patternKey);
+        if (value != null) {
+          result.put(pattern, value.getTargets());
+        } else if (!keepGoing) {
+          throw (TargetParsingException) Preconditions.checkNotNull(graph.getException(patternKey),
+              pattern);
+        } else {
+          result.put(pattern, ResolvedTargets.<Target>builder().setError().build());
+        }
       } else {
-        result.put(pattern, ResolvedTargets.<Target>builder().setError().build());
+        // If the graph doesn't contain a value for this target pattern, try to directly evaluate
+        // it, by making use of packages already present in the graph.
+        TargetPattern.Parser parser = new TargetPattern.Parser(targetPattern.getOffset());
+        GraphBackedRecursivePackageProvider provider =
+            new GraphBackedRecursivePackageProvider(graph);
+        RecursivePackageProviderBackedTargetPatternResolver resolver =
+            new RecursivePackageProviderBackedTargetPatternResolver(provider, eventHandler,
+                targetPattern.getPolicy(), pkgPath);
+        TargetPattern parsedPattern = parser.parse(targetPattern.getPattern());
+        try {
+          result.put(pattern, parsedPattern.eval(resolver));
+        } catch (InterruptedException e) {
+          throw new QueryException(e.getMessage());
+        }
       }
     }
     return result;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genquery/GenQuery.java b/src/main/java/com/google/devtools/build/lib/rules/genquery/GenQuery.java
index e1d1fdd..09d2ec5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/genquery/GenQuery.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/genquery/GenQuery.java
@@ -44,8 +44,7 @@
 import com.google.devtools.build.lib.packages.Type;
 import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
 import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
-import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
-import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
+import com.google.devtools.build.lib.pkgcache.PackageProvider;
 import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator;
 import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
 import com.google.devtools.build.lib.query2.engine.BlazeQueryEvalResult;
@@ -82,7 +81,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ThreadPoolExecutor;
 
 import javax.annotation.Nullable;
 
@@ -213,23 +211,20 @@
   @Nullable
   private byte[] executeQuery(RuleContext ruleContext, QueryOptions queryOptions,
       Set<Target> scope, String query) throws InterruptedException {
-    RecursivePackageProvider packageProvider;
-    Predicate<Label> labelFilter;
-    TargetPatternEvaluator evaluator;
 
     SkyFunction.Environment env = ruleContext.getAnalysisEnvironment().getSkyframeEnv();
     Pair<ImmutableMap<PackageIdentifier, Package>, Set<Label>> closureInfo =
         constructPackageMap(env, scope);
     ImmutableMap<PackageIdentifier, Package> packageMap = closureInfo.first;
     Set<Label> validTargets = closureInfo.second;
-    packageProvider = new PreloadedMapPackageProvider(packageMap, validTargets);
-    evaluator = new SkyframeEnvTargetPatternEvaluator(env);
-    labelFilter = Predicates.in(validTargets);
+    PackageProvider packageProvider = new PreloadedMapPackageProvider(packageMap, validTargets);
+    TargetPatternEvaluator evaluator = new SkyframeEnvTargetPatternEvaluator(env);
+    Predicate<Label> labelFilter = Predicates.in(validTargets);
 
     return doQuery(queryOptions, packageProvider, labelFilter, evaluator, query, ruleContext);
   }
 
-  private byte[] doQuery(QueryOptions queryOptions, RecursivePackageProvider packageProvider,
+  private byte[] doQuery(QueryOptions queryOptions, PackageProvider packageProvider,
                          Predicate<Label> labelFilter, TargetPatternEvaluator evaluator,
                          String query, RuleContext ruleContext)
       throws InterruptedException {
@@ -267,7 +262,8 @@
               labelFilter,
               getEventHandler(ruleContext),
               settings,
-              ImmutableList.<QueryFunction>of()).evaluateQuery(query);
+              ImmutableList.<QueryFunction>of(),
+              /*packagePath=*/null).evaluateQuery(query);
     } catch (SkyframeRestartQueryException e) {
       // Do not emit errors for skyframe restarts. They make output of the ConfiguredTargetFunction
       // inconsistent from run to run, and make detecting legitimate errors more difficult.
@@ -374,7 +370,7 @@
   /**
    * Provide packages and targets to the query operations using precomputed transitive closure.
    */
-  private static final class PreloadedMapPackageProvider implements RecursivePackageProvider {
+  private static final class PreloadedMapPackageProvider implements PackageProvider {
 
     private final ImmutableMap<PackageIdentifier, Package> pkgMap;
     private final Set<Label> targets;
@@ -401,17 +397,7 @@
     }
 
     @Override
-    public boolean isPackage(String packageName) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void visitPackageNamesRecursively(EventHandler eventHandler,
-                                             PathFragment directory,
-                                             boolean useTopLevelExcludes,
-                                             ThreadPoolExecutor visitorPool,
-                                             PathPackageLocator.AcceptsPathFragment observer)
-        throws InterruptedException {
+    public boolean isPackage(EventHandler eventHandler, String packageName) {
       throw new UnsupportedOperationException();
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
index c2851b2..7b54eb1 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/QueryCommand.java
@@ -174,6 +174,7 @@
         runtime.getTargetPatternEvaluator(),
         keepGoing, orderedResults, universeScope, loadingPhaseThreads, runtime.getReporter(),
         settings,
-        functions.build());
+        functions.build(),
+        runtime.getPackageManager().getPackagePath());
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/EnvironmentBackedRecursivePackageProvider.java b/src/main/java/com/google/devtools/build/lib/skyframe/EnvironmentBackedRecursivePackageProvider.java
new file mode 100644
index 0000000..901e877
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/EnvironmentBackedRecursivePackageProvider.java
@@ -0,0 +1,116 @@
+// Copyright 2015 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.Iterables;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageIdentifier;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.skyframe.SkyFunction.Environment;
+import com.google.devtools.build.skyframe.SkyKey;
+
+/**
+ * A {@link RecursivePackageProvider} backed by an {@link Environment}. Its methods
+ * may throw {@link MissingDepException} if the package values this depends on haven't been
+ * calculated and added to its environment.
+ *
+ * <p>That exception will be caught by the compute method in {@link TargetPatternFunction}, which
+ * then returns {@code null} in accordance with the skyframe missing dependency policy.
+ */
+public final class EnvironmentBackedRecursivePackageProvider implements RecursivePackageProvider {
+
+  private final Environment env;
+
+  public EnvironmentBackedRecursivePackageProvider(Environment env) {
+    this.env = env;
+  }
+
+  @Override
+  public Package getPackage(EventHandler eventHandler, PackageIdentifier packageName)
+      throws NoSuchPackageException, MissingDepException {
+    SkyKey pkgKey = PackageValue.key(packageName);
+    Package pkg;
+    try {
+      PackageValue pkgValue =
+          (PackageValue) env.getValueOrThrow(pkgKey, NoSuchPackageException.class);
+      if (pkgValue == null) {
+        throw new MissingDepException();
+      }
+      pkg = pkgValue.getPackage();
+    } catch (NoSuchPackageException e) {
+      pkg = e.getPackage();
+      if (pkg == null) {
+        throw e;
+      }
+    }
+    return pkg;
+  }
+
+  @Override
+  public boolean isPackage(EventHandler eventHandler, String packageName)
+      throws MissingDepException {
+    SkyKey packageLookupKey = PackageLookupValue.key(new PathFragment(packageName));
+    try {
+      PackageLookupValue packageLookupValue =
+          (PackageLookupValue) env.getValueOrThrow(packageLookupKey, NoSuchPackageException.class,
+              InconsistentFilesystemException.class);
+      if (packageLookupValue == null) {
+        throw new MissingDepException();
+      }
+      return packageLookupValue.packageExists();
+    } catch (NoSuchPackageException | InconsistentFilesystemException e) {
+      eventHandler.handle(Event.error(e.getMessage()));
+      return false;
+    }
+  }
+
+  @Override
+  public Iterable<PathFragment> getPackagesUnderDirectory(RootedPath directory)
+      throws MissingDepException {
+    RecursivePkgValue lookup = (RecursivePkgValue) env.getValue(RecursivePkgValue.key(directory));
+    if (lookup == null) {
+      // Typically a null value from Environment.getValue(k) means that either the key k is missing
+      // a dependency or an exception was thrown during evaluation of k. Here, if this getValue
+      // call returns null, it can only mean a missing dependency, because
+      // RecursivePkgFunction#compute never throws.
+      throw new MissingDepException();
+    }
+    // TODO(bazel-team): Make RecursivePkgValue return NestedSet<PathFragment> so this transform is
+    // unnecessary.
+    return Iterables.transform(lookup.getPackages(), PathFragment.TO_PATH_FRAGMENT);
+  }
+
+  @Override
+  public Target getTarget(EventHandler eventHandler, Label label) throws NoSuchPackageException,
+      NoSuchTargetException, MissingDepException {
+    return getPackage(eventHandler, label.getPackageIdentifier()).getTarget(label.getName());
+  }
+
+  /**
+   * Indicates that a missing dependency is needed before target parsing can proceed. Currently
+   * used only in skyframe to notify the framework of missing dependencies. Caught by the compute
+   * method in {@link com.google.devtools.build.lib.skyframe.TargetPatternFunction}, which then
+   * returns null in accordance with the skyframe missing dependency policy.
+   */
+  class MissingDepException extends RuntimeException {
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/GraphBackedRecursivePackageProvider.java b/src/main/java/com/google/devtools/build/lib/skyframe/GraphBackedRecursivePackageProvider.java
new file mode 100644
index 0000000..62f1e68
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/GraphBackedRecursivePackageProvider.java
@@ -0,0 +1,111 @@
+// Copyright 2015 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.base.Preconditions;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageIdentifier;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.WalkableGraph;
+
+import java.util.Collections;
+
+/** A {@link RecursivePackageProvider} backed by a {@link WalkableGraph}. */
+public final class GraphBackedRecursivePackageProvider implements RecursivePackageProvider {
+
+  private final WalkableGraph graph;
+
+  public GraphBackedRecursivePackageProvider(WalkableGraph graph) {
+    this.graph = graph;
+  }
+
+  @Override
+  public Package getPackage(EventHandler eventHandler, PackageIdentifier packageName)
+      throws NoSuchPackageException {
+    SkyKey pkgKey = PackageValue.key(packageName);
+
+    PackageValue pkgValue;
+    if (graph.exists(pkgKey)) {
+      pkgValue = (PackageValue) graph.getValue(pkgKey);
+      if (pkgValue == null) {
+        throw (NoSuchPackageException) Preconditions.checkNotNull(graph.getException(pkgKey));
+      }
+    } else {
+      // If the package key does not exist in the graph, then it must not correspond to any package,
+      // because the SkyQuery environment has already loaded the universe.
+      throw new BuildFileNotFoundException(packageName.toString(),
+          "BUILD file not found on package path");
+    }
+    return pkgValue.getPackage();
+  }
+
+  @Override
+  public boolean isPackage(EventHandler eventHandler, String packageName) {
+    SkyKey packageLookupKey = PackageLookupValue.key(new PathFragment(packageName));
+    if (!graph.exists(packageLookupKey)) {
+      // If the package lookup key does not exist in the graph, then it must not correspond to any
+      // package, because the SkyQuery environment has already loaded the universe.
+      return false;
+    }
+    PackageLookupValue packageLookupValue = (PackageLookupValue) graph.getValue(packageLookupKey);
+    if (packageLookupValue == null) {
+      Exception exception = Preconditions.checkNotNull(graph.getException(packageLookupKey),
+          "During package lookup for '%s', got null for exception", packageName);
+      if (exception instanceof NoSuchPackageException
+          || exception instanceof InconsistentFilesystemException) {
+        eventHandler.handle(Event.error(exception.getMessage()));
+        return false;
+      } else {
+        throw new IllegalStateException("During package lookup for '" + packageName
+            + "', got unexpected exception type", exception);
+      }
+    }
+    return packageLookupValue.packageExists();
+  }
+
+  @Override
+  public Iterable<PathFragment> getPackagesUnderDirectory(RootedPath directory) {
+    SkyKey recursivePackageKey = RecursivePkgValue.key(directory);
+    if (!graph.exists(recursivePackageKey)) {
+      // If the recursive package key does not exist in the graph, then it must not correspond to
+      // any directory transitively containing packages, because the SkyQuery environment has
+      // already loaded the universe.
+      return Collections.emptyList();
+    }
+    // If the recursive package key exists in the graph, then it must have a value and must not
+    // have an exception, because RecursivePkgFunction#compute never throws.
+    RecursivePkgValue lookup =
+        (RecursivePkgValue) Preconditions.checkNotNull(graph.getValue(recursivePackageKey));
+    // TODO(bazel-team): Make RecursivePkgValue return NestedSet<PathFragment> so this transform is
+    // unnecessary.
+    return Iterables.transform(lookup.getPackages(), PathFragment.TO_PATH_FRAGMENT);
+  }
+
+  @Override
+  public Target getTarget(EventHandler eventHandler, Label label)
+      throws NoSuchPackageException, NoSuchTargetException {
+    return getPackage(eventHandler, label.getPackageIdentifier()).getTarget(label.getName());
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java
new file mode 100644
index 0000000..a1b16f0
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursivePackageProviderBackedTargetPatternResolver.java
@@ -0,0 +1,206 @@
+// Copyright 2015 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.devtools.build.lib.cmdline.LabelValidator;
+import com.google.devtools.build.lib.cmdline.ResolvedTargets;
+import com.google.devtools.build.lib.cmdline.TargetParsingException;
+import com.google.devtools.build.lib.cmdline.TargetPatternResolver;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchThingException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageIdentifier;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
+import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
+import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
+import com.google.devtools.build.lib.pkgcache.TargetPatternResolverUtil;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
+
+/**
+ * A {@link TargetPatternResolver} backed by a {@link RecursivePackageProvider}.
+ */
+public class RecursivePackageProviderBackedTargetPatternResolver
+    implements TargetPatternResolver<Target> {
+
+  private final RecursivePackageProvider recursivePackageProvider;
+  private final EventHandler eventHandler;
+  private final FilteringPolicy policy;
+  private final PathPackageLocator pkgPath;
+
+  public RecursivePackageProviderBackedTargetPatternResolver(
+      final RecursivePackageProvider recursivePackageProvider,
+      EventHandler eventHandler,
+      FilteringPolicy policy,
+      PathPackageLocator pkgPath) {
+    this.recursivePackageProvider = recursivePackageProvider;
+    this.eventHandler = eventHandler;
+    this.policy = policy;
+    this.pkgPath = pkgPath;
+  }
+
+  @Override
+  public void warn(String msg) {
+    eventHandler.handle(Event.warn(msg));
+  }
+
+  /**
+   * Gets a {@link Package} from the {@link RecursivePackageProvider}. May return a {@link Package}
+   * that has errors.
+   */
+  private Package getPackage(PackageIdentifier pkgIdentifier)
+      throws NoSuchPackageException, InterruptedException {
+    Package pkg;
+    try {
+      pkg = recursivePackageProvider.getPackage(eventHandler, pkgIdentifier);
+    } catch (NoSuchPackageException e) {
+      pkg = e.getPackage();
+      if (pkg == null) {
+        throw e;
+      }
+    }
+    return pkg;
+  }
+
+  @Override
+  public Target getTargetOrNull(String targetName) throws InterruptedException {
+    try {
+      Label label = Label.parseAbsolute(targetName);
+      if (!isPackage(label.getPackageName())) {
+        return null;
+      }
+      return recursivePackageProvider.getTarget(eventHandler, label);
+    } catch (Label.SyntaxException | NoSuchThingException e) {
+      return null;
+    }
+  }
+
+  @Override
+  public ResolvedTargets<Target> getExplicitTarget(String targetName)
+      throws TargetParsingException, InterruptedException {
+    Label label = TargetPatternResolverUtil.label(targetName);
+    try {
+      Target target = recursivePackageProvider.getTarget(eventHandler, label);
+      return policy.shouldRetain(target, true)
+          ? ResolvedTargets.of(target)
+          : ResolvedTargets.<Target>empty();
+    } catch (NoSuchThingException e) {
+      throw new TargetParsingException(e.getMessage(), e);
+    }
+  }
+
+  @Override
+  public ResolvedTargets<Target> getTargetsInPackage(String originalPattern, String packageName,
+                                                     boolean rulesOnly)
+      throws TargetParsingException, InterruptedException {
+    FilteringPolicy actualPolicy = rulesOnly
+        ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
+        : policy;
+    return getTargetsInPackage(originalPattern, new PathFragment(packageName), actualPolicy);
+  }
+
+  private ResolvedTargets<Target> getTargetsInPackage(String originalPattern,
+      PathFragment packageNameFragment, FilteringPolicy policy)
+      throws TargetParsingException, InterruptedException {
+    String packageName = packageNameFragment.toString();
+
+    // It's possible for this check to pass, but for
+    // Label.validatePackageNameFull to report an error because the
+    // package name is illegal.  That's a little weird, but we can live with
+    // that for now--see test case: testBadPackageNameButGoodEnoughForALabel.
+    if (LabelValidator.validatePackageName(packageName) != null) {
+      throw new TargetParsingException("'" + packageName + "' is not a valid package name");
+    }
+    if (!isPackage(packageName)) {
+      throw new TargetParsingException(
+          TargetPatternResolverUtil.getParsingErrorMessage(
+              "no such package '" + packageName + "': BUILD file not found on package path",
+              originalPattern));
+    }
+
+    try {
+      Package pkg = getPackage(PackageIdentifier.createInDefaultRepo(packageNameFragment));
+      return TargetPatternResolverUtil.resolvePackageTargets(pkg, policy);
+    } catch (NoSuchThingException e) {
+      String message = TargetPatternResolverUtil.getParsingErrorMessage(
+          "package contains errors", originalPattern);
+      throw new TargetParsingException(message, e);
+    }
+  }
+
+  @Override
+  public boolean isPackage(String packageName) {
+    return recursivePackageProvider.isPackage(eventHandler, packageName);
+  }
+
+  @Override
+  public String getTargetKind(Target target) {
+    return target.getTargetKind();
+  }
+
+  @Override
+  public ResolvedTargets<Target> findTargetsBeneathDirectory(
+      String originalPattern, String pathPrefix, boolean rulesOnly)
+      throws TargetParsingException, InterruptedException {
+    FilteringPolicy actualPolicy = rulesOnly
+        ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
+        : policy;
+
+    PathFragment directory = new PathFragment(pathPrefix);
+    if (directory.containsUplevelReferences()) {
+      throw new TargetParsingException("up-level references are not permitted: '"
+          + directory.getPathString() + "'");
+    }
+    if (!pathPrefix.isEmpty() && (LabelValidator.validatePackageName(pathPrefix) != null)) {
+      throw new TargetParsingException("'" + pathPrefix + "' is not a valid package name");
+    }
+
+    ResolvedTargets.Builder<Target> builder = ResolvedTargets.builder();
+
+    for (Path root : pkgPath.getPathEntries()) {
+      RootedPath rootedPath = RootedPath.toRootedPath(root, directory);
+      Iterable<PathFragment> packagesUnderDirectory = recursivePackageProvider
+          .getPackagesUnderDirectory(rootedPath);
+      for (PathFragment pkg : packagesUnderDirectory) {
+        builder.merge(getTargetsInPackage(originalPattern, pkg, FilteringPolicies.NO_FILTER));
+      }
+    }
+
+    if (builder.isEmpty()) {
+      throw new TargetParsingException("no targets found beneath '" + directory + "'");
+    }
+
+    // Apply the transform after the check so we only return the
+    // error if the tree really contains no targets.
+    ResolvedTargets<Target> intermediateResult = builder.build();
+    ResolvedTargets.Builder<Target> filteredBuilder = ResolvedTargets.builder();
+    if (intermediateResult.hasError()) {
+      filteredBuilder.setError();
+    }
+    for (Target target : intermediateResult.getTargets()) {
+      if (actualPolicy.shouldRetain(target, false)) {
+        filteredBuilder.add(target);
+      }
+    }
+    return filteredBuilder.build();
+  }
+
+}
+
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
index cc32bf8..3e9b6ca 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageManager.java
@@ -136,7 +136,7 @@
   }
 
   @Override
-  public boolean isPackage(String packageName) {
+  public boolean isPackage(EventHandler eventHandler, String packageName) {
     return getBuildFileForPackage(packageName) != null;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternFunction.java
index b17a282..9322998 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TargetPatternFunction.java
@@ -13,32 +13,15 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
-import com.google.devtools.build.lib.cmdline.LabelValidator;
-import com.google.devtools.build.lib.cmdline.ResolvedTargets;
 import com.google.devtools.build.lib.cmdline.TargetParsingException;
 import com.google.devtools.build.lib.cmdline.TargetPattern;
-import com.google.devtools.build.lib.cmdline.TargetPatternResolver;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.packages.NoSuchPackageException;
-import com.google.devtools.build.lib.packages.NoSuchThingException;
-import com.google.devtools.build.lib.packages.Package;
-import com.google.devtools.build.lib.packages.PackageIdentifier;
-import com.google.devtools.build.lib.packages.Target;
-import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
-import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
-import com.google.devtools.build.lib.pkgcache.TargetPatternResolverUtil;
-import com.google.devtools.build.lib.syntax.Label;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.lib.skyframe.EnvironmentBackedRecursivePackageProvider.MissingDepException;
 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 java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.Nullable;
@@ -60,15 +43,23 @@
       InterruptedException {
     TargetPatternValue.TargetPattern patternKey =
         ((TargetPatternValue.TargetPattern) key.argument());
-
     TargetPattern.Parser parser = new TargetPattern.Parser(patternKey.getOffset());
     try {
-      Resolver resolver = new Resolver(env, patternKey.getPolicy(), pkgPath);
+      EnvironmentBackedRecursivePackageProvider provider =
+          new EnvironmentBackedRecursivePackageProvider(env);
+      RecursivePackageProviderBackedTargetPatternResolver resolver =
+          new RecursivePackageProviderBackedTargetPatternResolver(provider, env.getListener(),
+              patternKey.getPolicy(), pkgPath.get());
       TargetPattern resolvedPattern = parser.parse(patternKey.getPattern());
       return new TargetPatternValue(resolvedPattern.eval(resolver));
     } catch (TargetParsingException e) {
       throw new TargetPatternFunctionException(e);
-    } catch (TargetPatternResolver.MissingDepException e) {
+    } catch (MissingDepException e) {
+      // The EnvironmentBackedRecursivePackageProvider constructed above might throw
+      // MissingDepException to signal when it has a dependency on a missing Environment value.
+      // Note that MissingDepException extends RuntimeException because the methods called
+      // on EnvironmentBackedRecursivePackageProvider all belong to an interface shared with other
+      // implementations that are unconcerned with MissingDepExceptions.
       return null;
     }
   }
@@ -79,192 +70,7 @@
     return null;
   }
 
-  private static class Resolver implements TargetPatternResolver<Target> {
-    private final Environment env;
-    private final FilteringPolicy policy;
-    private final AtomicReference<PathPackageLocator> pkgPath;
 
-    public Resolver(Environment env, FilteringPolicy policy,
-                    AtomicReference<PathPackageLocator> pkgPath) {
-      this.policy = policy;
-      this.env = env;
-      this.pkgPath = pkgPath;
-    }
-
-    @Override
-    public void warn(String msg) {
-      env.getListener().handle(Event.warn(msg));
-    }
-
-    /**
-     * Gets a Package via the Skyframe env. May return a Package that has errors.
-     */
-    private Package getPackage(PackageIdentifier pkgIdentifier)
-        throws MissingDepException, NoSuchThingException {
-      SkyKey pkgKey = PackageValue.key(pkgIdentifier);
-      Package pkg;
-      try {
-        PackageValue pkgValue =
-            (PackageValue) env.getValueOrThrow(pkgKey, NoSuchPackageException.class);
-        if (pkgValue == null) {
-          throw new MissingDepException();
-        }
-        pkg = pkgValue.getPackage();
-      } catch (NoSuchPackageException e) {
-        pkg = e.getPackage();
-        if (pkg == null) {
-          throw e;
-        }
-      }
-      return pkg;
-    }
-
-    @Override
-    public Target getTargetOrNull(String targetName) throws InterruptedException,
-        MissingDepException {
-      try {
-        Label label = Label.parseAbsolute(targetName);
-        if (!isPackage(label.getPackageName())) {
-          return null;
-        }
-        Package pkg = getPackage(label.getPackageIdentifier());
-        return pkg.getTarget(label.getName());
-      } catch (Label.SyntaxException | NoSuchThingException e) {
-        return null;
-      }
-    }
-
-    @Override
-    public ResolvedTargets<Target> getExplicitTarget(String targetName)
-        throws TargetParsingException, InterruptedException, MissingDepException {
-      Label label = TargetPatternResolverUtil.label(targetName);
-      try {
-        Package pkg = getPackage(label.getPackageIdentifier());
-        Target target = pkg.getTarget(label.getName());
-        return  policy.shouldRetain(target, true)
-            ? ResolvedTargets.of(target)
-            : ResolvedTargets.<Target>empty();
-      } catch (NoSuchThingException e) {
-        throw new TargetParsingException(e.getMessage(), e);
-      }
-    }
-
-    @Override
-    public ResolvedTargets<Target> getTargetsInPackage(String originalPattern, String packageName,
-                                                       boolean rulesOnly)
-        throws TargetParsingException, InterruptedException, MissingDepException {
-      FilteringPolicy actualPolicy = rulesOnly
-          ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
-          : policy;
-      return getTargetsInPackage(originalPattern, packageName, actualPolicy);
-    }
-
-    private ResolvedTargets<Target> getTargetsInPackage(String originalPattern, String packageName,
-                                                        FilteringPolicy policy)
-        throws TargetParsingException, MissingDepException {
-      // Normalise, e.g "foo//bar" -> "foo/bar"; "foo/" -> "foo":
-      PathFragment packageNameFragment = new PathFragment(packageName);
-      packageName = packageNameFragment.toString();
-
-      // It's possible for this check to pass, but for
-      // Label.validatePackageNameFull to report an error because the
-      // package name is illegal.  That's a little weird, but we can live with
-      // that for now--see test case: testBadPackageNameButGoodEnoughForALabel.
-      // (BTW I tried duplicating that validation logic in Label but it was
-      // extremely tricky.)
-      if (LabelValidator.validatePackageName(packageName) != null) {
-        throw new TargetParsingException("'" + packageName + "' is not a valid package name");
-      }
-      if (!isPackage(packageName)) {
-        throw new TargetParsingException(
-            TargetPatternResolverUtil.getParsingErrorMessage(
-                "no such package '" + packageName + "': BUILD file not found on package path",
-                originalPattern));
-      }
-
-      try {
-        Package pkg = getPackage(
-            PackageIdentifier.createInDefaultRepo(packageNameFragment.toString()));
-        return TargetPatternResolverUtil.resolvePackageTargets(pkg, policy);
-      } catch (NoSuchThingException e) {
-        String message = TargetPatternResolverUtil.getParsingErrorMessage(
-            "package contains errors", originalPattern);
-        throw new TargetParsingException(message, e);
-      }
-    }
-
-    @Override
-    public boolean isPackage(String packageName) throws MissingDepException {
-      SkyKey packageLookupKey;
-      packageLookupKey = PackageLookupValue.key(new PathFragment(packageName));
-      PackageLookupValue packageLookupValue = (PackageLookupValue) env.getValue(packageLookupKey);
-      if (packageLookupValue == null) {
-        throw new MissingDepException();
-      }
-      return packageLookupValue.packageExists();
-    }
-
-    @Override
-    public String getTargetKind(Target target) {
-      return target.getTargetKind();
-    }
-
-    @Override
-    public ResolvedTargets<Target> findTargetsBeneathDirectory(
-        String originalPattern, String pathPrefix, boolean rulesOnly)
-        throws TargetParsingException, MissingDepException {
-      FilteringPolicy actualPolicy = rulesOnly
-          ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
-          : policy;
-
-      PathFragment directory = new PathFragment(pathPrefix);
-      if (directory.containsUplevelReferences()) {
-        throw new TargetParsingException("up-level references are not permitted: '"
-            + directory.getPathString() + "'");
-      }
-      if (!pathPrefix.isEmpty() && (LabelValidator.validatePackageName(pathPrefix) != null)) {
-        throw new TargetParsingException("'" + pathPrefix + "' is not a valid package name");
-      }
-
-      ResolvedTargets.Builder<Target> builder = ResolvedTargets.builder();
-
-      List<RecursivePkgValue> lookupValues = new ArrayList<>();
-      for (Path root : pkgPath.get().getPathEntries()) {
-        SkyKey key = RecursivePkgValue.key(RootedPath.toRootedPath(root, directory));
-        RecursivePkgValue lookup = (RecursivePkgValue) env.getValue(key);
-        if (lookup != null) {
-          lookupValues.add(lookup);
-        }
-      }
-      if (env.valuesMissing()) {
-        throw new MissingDepException();
-      }
-
-      for (RecursivePkgValue value : lookupValues) {
-        for (String pkg : value.getPackages()) {
-          builder.merge(getTargetsInPackage(originalPattern, pkg, FilteringPolicies.NO_FILTER));
-        }
-      }
-
-      if (builder.isEmpty()) {
-        throw new TargetParsingException("no targets found beneath '" + directory + "'");
-      }
-
-      // Apply the transform after the check so we only return the
-      // error if the tree really contains no targets.
-      ResolvedTargets<Target> intermediateResult = builder.build();
-      ResolvedTargets.Builder<Target> filteredBuilder = ResolvedTargets.builder();
-      if (intermediateResult.hasError()) {
-        filteredBuilder.setError();
-      }
-      for (Target target : intermediateResult.getTargets()) {
-        if (actualPolicy.shouldRetain(target, false)) {
-          filteredBuilder.add(target);
-        }
-      }
-      return filteredBuilder.build();
-    }
-  }
 
   /**
    * Used to declare all the exception types that can be wrapped in the exception thrown by