Use WorkspaceNameValue.key() to seed RBuildFile traversals under WORKSPACE file modifications.
Move some RBuildFilesVisitor-specific logic out to that file for better organization and to remove some clutter from SkyQueryEnvironment
PiperOrigin-RevId: 296281889
diff --git a/src/main/java/com/google/devtools/build/lib/query2/ParallelSkyQueryUtils.java b/src/main/java/com/google/devtools/build/lib/query2/ParallelSkyQueryUtils.java
index 9e81dbd..1034dc9 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/ParallelSkyQueryUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/ParallelSkyQueryUtils.java
@@ -154,7 +154,7 @@
/*resultUniquifier=*/ env.createSkyKeyUniquifier(),
context,
callback);
- visitor.visitAndWaitForCompletion(env.getSkyKeysForFileFragments(fileIdentifiers));
+ visitor.visitFileIdentifiersAndWaitForCompletion(env.graph, fileIdentifiers);
}
static QueryTaskFuture<Void> getDepsUnboundedParallel(
diff --git a/src/main/java/com/google/devtools/build/lib/query2/RBuildFilesVisitor.java b/src/main/java/com/google/devtools/build/lib/query2/RBuildFilesVisitor.java
index 73b638c..819b081 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/RBuildFilesVisitor.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/RBuildFilesVisitor.java
@@ -13,9 +13,15 @@
// limitations under the License.
package com.google.devtools.build.lib.query2;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.devtools.build.lib.actions.FileStateValue;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet;
import com.google.devtools.build.lib.packages.Target;
@@ -24,10 +30,21 @@
import com.google.devtools.build.lib.query2.engine.QueryException;
import com.google.devtools.build.lib.query2.engine.QueryExpressionContext;
import com.google.devtools.build.lib.query2.engine.Uniquifier;
+import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
+import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction;
+import com.google.devtools.build.lib.skyframe.PackageLookupValue;
+import com.google.devtools.build.lib.skyframe.PackageValue;
import com.google.devtools.build.lib.skyframe.SkyFunctions;
+import com.google.devtools.build.lib.skyframe.WorkspaceNameValue;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.build.skyframe.WalkableGraph;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
/** A helper class that computes 'rbuildfiles(<blah>)' via BFS. */
@@ -52,6 +69,8 @@
SkyFunctions.PREPARE_DEPS_OF_PATTERN,
SkyFunctions.PREPARE_DEPS_OF_PATTERNS);
+ private static final SkyKey EXTERNAL_PACKAGE_KEY =
+ PackageValue.key(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER);
private final SkyQueryEnvironment env;
private final QueryExpressionContext<Target> context;
private final Uniquifier<SkyKey> visitUniquifier;
@@ -85,6 +104,13 @@
if (resultUniquifier.unique(rdep)) {
keysToUseForResult.add((PackageIdentifier) rdep.argument());
}
+ // PackageValue(//p) has a transitive dep on the PackageValue(//external), so we need to
+ // make sure these dep paths are traversed. These dep paths go through the singleton
+ // WorkspaceNameValue(), and that node has a direct dep on PackageValue(//external), so it
+ // suffices to ensure we visit PackageValue(//external).
+ if (rdep.equals(EXTERNAL_PACKAGE_KEY)) {
+ keysToVisitNext.add(rdep);
+ }
} else if (!NODES_TO_PRUNE_TRAVERSAL.contains(rdep.functionName())) {
processNonPackageRdepAndDetermineVisitations(rdep, keysToVisitNext, keysToUseForResult);
}
@@ -121,4 +147,102 @@
Iterable<SkyKey> prospectiveVisitationKeys) throws QueryException {
return visitUniquifier.unique(prospectiveVisitationKeys);
}
+
+ /** Initiates the graph visitation algorithm seeded by a set of file paths. */
+ public void visitFileIdentifiersAndWaitForCompletion(
+ WalkableGraph graph, Iterable<PathFragment> fileKeys)
+ throws QueryException, InterruptedException {
+ visitAndWaitForCompletion(getSkyKeysForFileFragments(graph, fileKeys));
+ }
+
+ /**
+ * The passed in {@link PathFragment}s can be associated uniquely to a {@link FileStateValue} with
+ * the following logic (the same logic that's in {@link ContainingPackageLookupFunction}): For
+ * each given file path, we look for the nearest ancestor directory (starting with its parent
+ * directory), if any, that has a package. The {@link PackageLookupValue} for this package tells
+ * us the package root that we should use for the {@link RootedPath} for the {@link
+ * FileStateValue} key.
+ *
+ * <p>For the reverse graph traversal, we are looking for all packages that are transitively
+ * reverse dependencies of those {@link FileStateValue} keys. This function returns a collection
+ * of SkyKeys whose transitive reverse dependencies must contain the exact same set of packages.
+ *
+ * <p>Note that there may not be nodes in the graph corresponding to the returned SkyKeys.
+ */
+ private static Collection<SkyKey> getSkyKeysForFileFragments(
+ WalkableGraph graph, Iterable<PathFragment> pathFragments) throws InterruptedException {
+ Set<SkyKey> result = new HashSet<>();
+ Multimap<PathFragment, PathFragment> currentToOriginal = ArrayListMultimap.create();
+ for (PathFragment pathFragment : pathFragments) {
+ currentToOriginal.put(pathFragment, pathFragment);
+ }
+ while (!currentToOriginal.isEmpty()) {
+ Multimap<SkyKey, PathFragment> packageLookupKeysToOriginal = ArrayListMultimap.create();
+ Multimap<SkyKey, PathFragment> packageLookupKeysToCurrent = ArrayListMultimap.create();
+ for (Map.Entry<PathFragment, PathFragment> entry : currentToOriginal.entries()) {
+ PathFragment current = entry.getKey();
+ PathFragment original = entry.getValue();
+ for (SkyKey packageLookupKey : getPkgLookupKeysForFile(current)) {
+ packageLookupKeysToOriginal.put(packageLookupKey, original);
+ packageLookupKeysToCurrent.put(packageLookupKey, current);
+ }
+
+ // The WORKSPACE file is a transitive dependency of every package. Unfortunately, there is
+ // no specific SkyValue that we can use to figure out under which package path entries it
+ // lives so we add a dependency on the WorkspaceNameValue key.
+ if (original.equals(current) && WorkspaceFileHelper.matchWorkspaceFileName(original)) {
+ // TODO(mschaller): this should not be checked at runtime. These are constants!
+ Preconditions.checkState(
+ LabelConstants.WORKSPACE_FILE_NAME
+ .getParentDirectory()
+ .equals(PathFragment.EMPTY_FRAGMENT),
+ LabelConstants.WORKSPACE_FILE_NAME);
+ result.add(WorkspaceNameValue.key());
+ }
+ }
+ Map<SkyKey, SkyValue> lookupValues =
+ graph.getSuccessfulValues(packageLookupKeysToOriginal.keySet());
+ for (Map.Entry<SkyKey, SkyValue> entry : lookupValues.entrySet()) {
+ SkyKey packageLookupKey = entry.getKey();
+ PackageLookupValue packageLookupValue = (PackageLookupValue) entry.getValue();
+ if (packageLookupValue.packageExists()) {
+ Collection<PathFragment> originalFiles =
+ packageLookupKeysToOriginal.get(packageLookupKey);
+ Preconditions.checkState(!originalFiles.isEmpty(), entry);
+ for (PathFragment fileName : originalFiles) {
+ result.add(
+ FileStateValue.key(
+ RootedPath.toRootedPath(packageLookupValue.getRoot(), fileName)));
+ }
+ for (PathFragment current : packageLookupKeysToCurrent.get(packageLookupKey)) {
+ currentToOriginal.removeAll(current);
+ }
+ }
+ }
+ Multimap<PathFragment, PathFragment> newCurrentToOriginal = ArrayListMultimap.create();
+ for (PathFragment pathFragment : currentToOriginal.keySet()) {
+ PathFragment parent = pathFragment.getParentDirectory();
+ if (parent != null) {
+ newCurrentToOriginal.putAll(parent, currentToOriginal.get(pathFragment));
+ }
+ }
+ currentToOriginal = newCurrentToOriginal;
+ }
+ return result;
+ }
+
+ /**
+ * Returns package lookup keys for looking up the package root for which there may be a relevant
+ * {@link FileStateValue} node in the graph for {@code originalFileFragment}, which is assumed to
+ * be a file path.
+ *
+ * <p>This is a helper function for {@link #getSkyKeysForFileFragments}.
+ */
+ private static Iterable<SkyKey> getPkgLookupKeysForFile(PathFragment currentPathFragment) {
+ PathFragment parentPathFragment = currentPathFragment.getParentDirectory();
+ return parentPathFragment == null
+ ? ImmutableList.of()
+ : ImmutableList.of(
+ PackageLookupValue.key(PackageIdentifier.createInMainRepo(parentPathFragment)));
+ }
}
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 6f5e062..e0ae25c 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
@@ -38,9 +38,7 @@
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.google.devtools.build.lib.actions.FileStateValue;
import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.cmdline.TargetPattern;
@@ -85,11 +83,8 @@
import com.google.devtools.build.lib.query2.engine.ThreadSafeOutputFormatterCallback;
import com.google.devtools.build.lib.query2.engine.Uniquifier;
import com.google.devtools.build.lib.query2.query.BlazeTargetAccessor;
-import com.google.devtools.build.lib.rules.repository.WorkspaceFileHelper;
import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesValue;
-import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction;
import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
-import com.google.devtools.build.lib.skyframe.PackageLookupValue;
import com.google.devtools.build.lib.skyframe.PackageValue;
import com.google.devtools.build.lib.skyframe.PrepareDepsOfPatternsFunction;
import com.google.devtools.build.lib.skyframe.RecursivePackageProviderBackedTargetPatternResolver;
@@ -101,7 +96,6 @@
import com.google.devtools.build.lib.supplier.MemoizingInterruptibleSupplier;
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.skyframe.EvaluationContext;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyFunctionName;
@@ -1211,115 +1205,6 @@
return target;
}
- private static ImmutableList<SkyKey> getDependenciesForWorkspaceFile(
- PathFragment originalFileFragment, PathFragment currentPathFragment) {
- if (originalFileFragment.equals(currentPathFragment)
- && WorkspaceFileHelper.matchWorkspaceFileName(originalFileFragment)) {
- // TODO(mschaller): this should not be checked at runtime. These are constants!
- Preconditions.checkState(
- LabelConstants.WORKSPACE_FILE_NAME
- .getParentDirectory()
- .equals(PathFragment.EMPTY_FRAGMENT),
- LabelConstants.WORKSPACE_FILE_NAME);
- // The WORKSPACE file is a transitive dependency of every package. Unfortunately, there is no
- // specific SkyValue that we can use to figure out under which package path entries it lives,
- // so we add another dependency that's a transitive dependency of every package.
- //
- // The PackageLookupValue for //external is not a good choice, because it's not requested when
- // that package is built so that we can have a directory called external/ in the workspace
- // (this is special-cased in PackageFunction). The principled solution would be to not rely
- // on external/ and introduce a SkyValue for the concept of "the WORKSPACE file of the main
- // repository", but we don't have that yet, so we improvise.
- return ImmutableList.of(PackageValue.key(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER));
- }
-
- return ImmutableList.of();
- }
-
- /**
- * Returns package lookup keys for looking up the package root for which there may be a relevant
- * (from the perspective of {@link #getRBuildFiles}) {@link FileStateValue} node in the graph for
- * {@code originalFileFragment}, which is assumed to be a file path.
- *
- * <p>This is a helper function for {@link #getSkyKeysForFileFragments}.
- */
- private static Iterable<SkyKey> getPkgLookupKeysForFile(PathFragment currentPathFragment) {
- PathFragment parentPathFragment = currentPathFragment.getParentDirectory();
- return parentPathFragment == null
- ? ImmutableList.of()
- : ImmutableList.of(
- PackageLookupValue.key(PackageIdentifier.createInMainRepo(parentPathFragment)));
- }
-
- /**
- * Returns SkyKeys for which there may be relevant (from the perspective of {@link
- * #getRBuildFiles}) FileStateValues in the graph corresponding to the given {@code
- * pathFragments}, which are assumed to be file paths.
- *
- * <p>The passed in {@link PathFragment}s can be associated uniquely to a {@link FileStateValue}
- * with the following logic (the same logic that's in {@link ContainingPackageLookupFunction}):
- * For each given file path, we look for the nearest ancestor directory (starting with its parent
- * directory), if any, that has a package. The {@link PackageLookupValue} for this package tells
- * us the package root that we should use for the {@link RootedPath} for the {@link
- * FileStateValue} key.
- *
- * <p>For the reverse graph traversal in {@link #getRBuildFiles}, we are looking for all packages
- * that are transitively reverse dependencies of those {@link FileStateValue} keys. This function
- * returns a collection of SkyKeys whose transitive reverse dependencies must contain the exact
- * same set of packages.
- *
- * <p>Note that there may not be nodes in the graph corresponding to the returned SkyKeys.
- */
- protected Collection<SkyKey> getSkyKeysForFileFragments(Iterable<PathFragment> pathFragments)
- throws InterruptedException {
- Set<SkyKey> result = new HashSet<>();
- Multimap<PathFragment, PathFragment> currentToOriginal = ArrayListMultimap.create();
- for (PathFragment pathFragment : pathFragments) {
- currentToOriginal.put(pathFragment, pathFragment);
- }
- while (!currentToOriginal.isEmpty()) {
- Multimap<SkyKey, PathFragment> packageLookupKeysToOriginal = ArrayListMultimap.create();
- Multimap<SkyKey, PathFragment> packageLookupKeysToCurrent = ArrayListMultimap.create();
- for (Map.Entry<PathFragment, PathFragment> entry : currentToOriginal.entries()) {
- PathFragment current = entry.getKey();
- PathFragment original = entry.getValue();
- for (SkyKey packageLookupKey : getPkgLookupKeysForFile(current)) {
- packageLookupKeysToOriginal.put(packageLookupKey, original);
- packageLookupKeysToCurrent.put(packageLookupKey, current);
- }
-
- result.addAll(getDependenciesForWorkspaceFile(original, current));
- }
- Map<SkyKey, SkyValue> lookupValues =
- graph.getSuccessfulValues(packageLookupKeysToOriginal.keySet());
- for (Map.Entry<SkyKey, SkyValue> entry : lookupValues.entrySet()) {
- SkyKey packageLookupKey = entry.getKey();
- PackageLookupValue packageLookupValue = (PackageLookupValue) entry.getValue();
- if (packageLookupValue.packageExists()) {
- Collection<PathFragment> originalFiles =
- packageLookupKeysToOriginal.get(packageLookupKey);
- Preconditions.checkState(!originalFiles.isEmpty(), entry);
- for (PathFragment fileName : originalFiles) {
- result.add(FileStateValue.key(
- RootedPath.toRootedPath(packageLookupValue.getRoot(), fileName)));
- }
- for (PathFragment current : packageLookupKeysToCurrent.get(packageLookupKey)) {
- currentToOriginal.removeAll(current);
- }
- }
- }
- Multimap<PathFragment, PathFragment> newCurrentToOriginal = ArrayListMultimap.create();
- for (PathFragment pathFragment : currentToOriginal.keySet()) {
- PathFragment parent = pathFragment.getParentDirectory();
- if (parent != null) {
- newCurrentToOriginal.putAll(parent, currentToOriginal.get(pathFragment));
- }
- }
- currentToOriginal = newCurrentToOriginal;
- }
- return result;
- }
-
protected Iterable<Target> getBuildFileTargetsForPackageKeys(
Set<PackageIdentifier> pkgIds, QueryExpressionContext<Target> context)
throws QueryException, InterruptedException {