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