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/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());
+ }
+}