Make SkyQuery's rdeps->allrdeps transformations apply when `--universe_scope` uses a "all targets" pattern and the universe of the `rdeps` expression uses a "rules only" pattern.
PiperOrigin-RevId: 361640819
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 21eb22f..c3fc185 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
@@ -224,11 +224,21 @@
}
/**
- * For patterns of type {@link Type#SINGLE_TARGET} and {@link Type#TARGETS_IN_PACKAGE}, returns
- * the {@link PackageIdentifier} corresponding to the package that would contain the target(s)
- * matched by this {@link TargetPattern}.
+ * For patterns of type {@link Type#SINGLE_TARGET}, {@link Type#TARGETS_IN_PACKAGE}, and {@link
+ * Type#TARGETS_BELOW_DIRECTORY}, returns the {@link PackageIdentifier} of the pattern.
+ *
+ * <p>Note that we are using the {@link PackageIdentifier} type as a convenience; there may not
+ * actually be a package corresponding to this directory!
+ *
+ * <p>Examples:
+ *
+ * <ul>
+ * <li>For pattern {@code //foo:bar}, returns package identifier {@code //foo}.
+ * <li>For pattern {@code //foo:all}, returns package identifier {@code //foo}.
+ * <li>For pattern {@code //foo/...}, returns package identifier {@code //foo}.
+ * </ul>
*/
- public PackageIdentifier getDirectoryForTargetOrTargetsInPackage() {
+ public PackageIdentifier getDirectory() {
throw new IllegalStateException();
}
@@ -269,7 +279,7 @@
}
@Override
- public PackageIdentifier getDirectoryForTargetOrTargetsInPackage() {
+ public PackageIdentifier getDirectory() {
return directory;
}
@@ -439,7 +449,7 @@
}
@Override
- public PackageIdentifier getDirectoryForTargetOrTargetsInPackage() {
+ public PackageIdentifier getDirectory() {
return packageIdentifier;
}
@@ -762,16 +772,7 @@
NOT_CONTAINED,
}
- /**
- * Returns a {@link PackageIdentifier} identifying the most specific containing directory of the
- * patterns that could be matched by this pattern.
- *
- * <p>Note that we are using the {@link PackageIdentifier} type as a convenience; there may not
- * actually be a package corresponding to this directory!
- *
- * <p>This returns a {@link PackageIdentifier} that identifies the referred-to directory. For
- * example, for "//foo/bar/...", this method returns a {@link PackageIdentifier} for "foo/bar".
- */
+ @Override
public PackageIdentifier getDirectory() {
return directory;
}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/FilteredDirectRdepsInUniverseExpressionMapper.java b/src/main/java/com/google/devtools/build/lib/query2/FilteredDirectRdepsInUniverseExpressionMapper.java
index b652a82..b4bcd71 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/FilteredDirectRdepsInUniverseExpressionMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/FilteredDirectRdepsInUniverseExpressionMapper.java
@@ -17,6 +17,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.cmdline.TargetPattern;
+import com.google.devtools.build.lib.query2.RdepsToAllRdepsQueryExpressionMapper.Eligibility;
import com.google.devtools.build.lib.query2.engine.BinaryOperatorExpression;
import com.google.devtools.build.lib.query2.engine.FunctionExpression;
import com.google.devtools.build.lib.query2.engine.LetExpression;
@@ -31,6 +32,7 @@
import com.google.devtools.build.lib.query2.engine.TargetLiteral;
import java.util.ArrayList;
import java.util.List;
+import javax.annotation.Nullable;
/**
* A {@link QueryExpressionMapper} that transforms each occurrence of an expression of the form
@@ -64,13 +66,13 @@
*/
public class FilteredDirectRdepsInUniverseExpressionMapper extends QueryExpressionMapper<Void> {
private final TargetPattern.Parser targetPatternParser;
- private final String absoluteUniverseScopePattern;
+ private final TargetPattern absoluteUniverseScopePattern;
@VisibleForTesting
public FilteredDirectRdepsInUniverseExpressionMapper(
- TargetPattern.Parser targetPatternParser, String universeScopePattern) {
+ TargetPattern.Parser targetPatternParser, TargetPattern absoluteUniverseScopePattern) {
this.targetPatternParser = targetPatternParser;
- this.absoluteUniverseScopePattern = targetPatternParser.absolutize(universeScopePattern);
+ this.absoluteUniverseScopePattern = absoluteUniverseScopePattern;
}
@Override
@@ -79,13 +81,18 @@
List<Argument> args = functionExpression.getArgs();
// This transformation only applies to the 3-arg form of rdeps, with depth == 1.
if (args.size() == 3 && args.get(2).getInteger() == 1) {
- List<FunctionExpression> universeFilteringFunctions =
- args.get(0).getExpression().accept(new ExtractFilteringFunctionsFromUniverseVisitor());
- // If we get back a non-empty list of FunctionExpressions, these are filtering functions
- // that can be safely factored out.
- if (!universeFilteringFunctions.isEmpty()) {
- QueryExpression curFunction = makeUnfilteredRdepsWithDepthOne(args.get(1));
- for (FunctionExpression filterExpr : universeFilteringFunctions) {
+ Argument rdepsUniverseArgument = args.get(0);
+ QueryExpression rdepsUniverseExpression = rdepsUniverseArgument.getExpression();
+ ExtractFilteringFunctionsResult result =
+ rdepsUniverseExpression.accept(new ExtractFilteringFunctionsFromUniverseVisitor());
+ // If we get back a non-empty result, then there are filtering functions that can be safely
+ // factored out.
+ if (result.universeArgument != null) {
+ QueryExpression curFunction =
+ makeUnfilteredRdepsWithDepthOne(
+ /*rdepsUniverseArgument=*/ result.universeArgument,
+ /*sourceArgument=*/ args.get(1));
+ for (FunctionExpression filterExpr : result.filteringExpressions) {
curFunction = wrapExprWithFilter(curFunction, filterExpr);
}
return curFunction;
@@ -95,11 +102,11 @@
return super.visit(functionExpression, context);
}
- private FunctionExpression makeUnfilteredRdepsWithDepthOne(Argument target) {
+ private FunctionExpression makeUnfilteredRdepsWithDepthOne(
+ Argument rdepsUniverseArgument, Argument sourceArgument) {
return new FunctionExpression(
new RdepsFunction(),
- ImmutableList.of(
- Argument.of(new TargetLiteral(absoluteUniverseScopePattern)), target, Argument.of(1)));
+ ImmutableList.of(rdepsUniverseArgument, sourceArgument, Argument.of(1)));
}
private static QueryExpression wrapExprWithFilter(
@@ -117,63 +124,86 @@
return new FunctionExpression(filteringFunction, rewrittenArgs);
}
+ private static class ExtractFilteringFunctionsResult {
+ private static final ExtractFilteringFunctionsResult EMPTY =
+ new ExtractFilteringFunctionsResult(ImmutableList.of(), null);
+
+ private final ImmutableList<FunctionExpression> filteringExpressions;
+ @Nullable private final Argument universeArgument;
+
+ private ExtractFilteringFunctionsResult(
+ ImmutableList<FunctionExpression> filteringExpressions,
+ @Nullable Argument universeArgument) {
+ this.filteringExpressions = filteringExpressions;
+ this.universeArgument = universeArgument;
+ }
+ }
+
/**
* Internal visitor applied to the universe argument of all QueryExpressions of the form {@literal
* rdeps(u, x, 1)}.
*/
private class ExtractFilteringFunctionsFromUniverseVisitor
- implements QueryExpressionVisitor<List<FunctionExpression>, Void> {
+ implements QueryExpressionVisitor<ExtractFilteringFunctionsResult, Void> {
@Override
- public List<FunctionExpression> visit(TargetLiteral targetLiteral, Void context) {
- return ImmutableList.of();
+ public ExtractFilteringFunctionsResult visit(TargetLiteral targetLiteral, Void context) {
+ return ExtractFilteringFunctionsResult.EMPTY;
}
@Override
- public List<FunctionExpression> visit(
+ public ExtractFilteringFunctionsResult visit(
BinaryOperatorExpression binaryOperatorExpression, Void context) {
- return ImmutableList.of();
+ return ExtractFilteringFunctionsResult.EMPTY;
}
@SuppressWarnings("MixedMutabilityReturnType")
@Override
- public List<FunctionExpression> visit(FunctionExpression functionExpression, Void context) {
+ public ExtractFilteringFunctionsResult visit(
+ FunctionExpression functionExpression, Void context) {
FilteringQueryFunction filteringFunction =
functionExpression.getFunction().asFilteringFunction();
if (filteringFunction == null) {
- return ImmutableList.of();
+ return ExtractFilteringFunctionsResult.EMPTY;
}
Argument filteredArgument =
functionExpression.getArgs().get(filteringFunction.getExpressionToFilterIndex());
Preconditions.checkArgument(filteredArgument.getType() == ArgumentType.EXPRESSION);
QueryExpression filteredExpression = filteredArgument.getExpression();
- ArrayList<FunctionExpression> results = new ArrayList<>();
if (filteredExpression instanceof TargetLiteral) {
- TargetLiteral literalUniverseExpression = (TargetLiteral) filteredExpression;
- String absolutizedUniverseExpression =
- targetPatternParser.absolutize(literalUniverseExpression.getPattern());
- if (absolutizedUniverseExpression.equals(absoluteUniverseScopePattern)) {
- results.add(functionExpression);
+ Eligibility eligibility =
+ RdepsToAllRdepsQueryExpressionMapper.determineEligibility(
+ targetPatternParser,
+ absoluteUniverseScopePattern,
+ ((TargetLiteral) filteredExpression).getPattern());
+ if (eligibility == Eligibility.ELIGIBLE_AS_IS
+ || eligibility == Eligibility.ELIGIBLE_WITH_FILTERING) {
+ return new ExtractFilteringFunctionsResult(
+ ImmutableList.of(functionExpression), filteredArgument);
}
} else if (filteredExpression instanceof FunctionExpression) {
- List<FunctionExpression> nestedFilters = filteredExpression.accept(this);
- if (!nestedFilters.isEmpty()) {
- results.addAll(nestedFilters);
- results.add(functionExpression);
+ ExtractFilteringFunctionsResult recursiveResult = filteredExpression.accept(this);
+ if (recursiveResult.universeArgument != null) {
+ return new ExtractFilteringFunctionsResult(
+ /*filteringExpressions=*/ ImmutableList.<FunctionExpression>builder()
+ .addAll(recursiveResult.filteringExpressions)
+ .add(functionExpression)
+ .build(),
+ recursiveResult.universeArgument);
}
}
- return results;
+ return ExtractFilteringFunctionsResult.EMPTY;
}
@Override
- public List<FunctionExpression> visit(LetExpression letExpression, Void context) {
- return ImmutableList.of();
+ public ExtractFilteringFunctionsResult visit(LetExpression letExpression, Void context) {
+ return ExtractFilteringFunctionsResult.EMPTY;
}
@Override
- public List<FunctionExpression> visit(SetExpression setExpression, Void context) {
- return ImmutableList.of();
+ public ExtractFilteringFunctionsResult visit(SetExpression setExpression, Void context) {
+ return ExtractFilteringFunctionsResult.EMPTY;
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/query2/RdepsToAllRdepsQueryExpressionMapper.java b/src/main/java/com/google/devtools/build/lib/query2/RdepsToAllRdepsQueryExpressionMapper.java
index 3146ef6..5d834da 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/RdepsToAllRdepsQueryExpressionMapper.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/RdepsToAllRdepsQueryExpressionMapper.java
@@ -13,9 +13,12 @@
// limitations under the License.
package com.google.devtools.build.lib.query2;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.cmdline.TargetParsingException;
import com.google.devtools.build.lib.cmdline.TargetPattern;
import com.google.devtools.build.lib.query2.engine.AllRdepsFunction;
import com.google.devtools.build.lib.query2.engine.FunctionExpression;
+import com.google.devtools.build.lib.query2.engine.KindFunction;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Argument;
import com.google.devtools.build.lib.query2.engine.QueryExpression;
import com.google.devtools.build.lib.query2.engine.QueryExpressionMapper;
@@ -30,29 +33,106 @@
*/
class RdepsToAllRdepsQueryExpressionMapper extends QueryExpressionMapper<Void> {
private final TargetPattern.Parser targetPatternParser;
- private final String absoluteUniverseScopePattern;
+ private final TargetPattern absoluteUniverseScopePattern;
RdepsToAllRdepsQueryExpressionMapper(
- TargetPattern.Parser targetPatternParser, String universeScopePattern) {
+ TargetPattern.Parser targetPatternParser, TargetPattern absoluteUniverseScopePattern) {
this.targetPatternParser = targetPatternParser;
- this.absoluteUniverseScopePattern = targetPatternParser.absolutize(universeScopePattern);
+ this.absoluteUniverseScopePattern = absoluteUniverseScopePattern;
}
@Override
public QueryExpression visit(FunctionExpression functionExpression, Void context) {
if (functionExpression.getFunction().getName().equals(new RdepsFunction().getName())) {
List<Argument> args = functionExpression.getArgs();
- QueryExpression universeExpression = args.get(0).getExpression();
- if (universeExpression instanceof TargetLiteral) {
- TargetLiteral literalUniverseExpression = (TargetLiteral) universeExpression;
- String absolutizedUniverseExpression =
- targetPatternParser.absolutize(literalUniverseExpression.getPattern());
- if (absolutizedUniverseExpression.equals(absoluteUniverseScopePattern)) {
- List<Argument> argsTail = args.subList(1, functionExpression.getArgs().size());
- return new FunctionExpression(new AllRdepsFunction(), argsTail);
+ QueryExpression rdepsUniverseExpression = args.get(0).getExpression();
+ if (rdepsUniverseExpression instanceof TargetLiteral) {
+ Eligibility eligibility =
+ determineEligibility(
+ targetPatternParser,
+ absoluteUniverseScopePattern,
+ ((TargetLiteral) rdepsUniverseExpression).getPattern());
+ switch (eligibility) {
+ case ELIGIBLE_AS_IS:
+ return new FunctionExpression(
+ new AllRdepsFunction(), args.subList(1, functionExpression.getArgs().size()));
+ case ELIGIBLE_WITH_FILTERING:
+ return new FunctionExpression(
+ new KindFunction(),
+ ImmutableList.of(
+ Argument.of(" rule$"),
+ Argument.of(
+ new FunctionExpression(
+ new AllRdepsFunction(),
+ args.subList(1, functionExpression.getArgs().size())))));
+ default:
+ // Do nothing. The return statement at the bottom of the method is what we want.
}
}
}
return super.visit(functionExpression, context);
}
+
+ /**
+ * Describes how eligible, if at all, a `rdeps(pattern, E, d)` expression is for being transformed
+ * to one that uses `allrdeps`.
+ */
+ enum Eligibility {
+ NOT_ELIGIBLE,
+
+ ELIGIBLE_WITH_FILTERING,
+
+ ELIGIBLE_AS_IS,
+ }
+
+ static Eligibility determineEligibility(
+ TargetPattern.Parser targetPatternParser,
+ TargetPattern absoluteUniverseScopePattern,
+ String rdepsUniversePatternString) {
+ TargetPattern absoluteRdepsUniverseTargetPattern;
+ try {
+ absoluteRdepsUniverseTargetPattern =
+ targetPatternParser.parse(targetPatternParser.absolutize(rdepsUniversePatternString));
+ } catch (TargetParsingException e) {
+ return Eligibility.NOT_ELIGIBLE;
+ }
+
+ if (absoluteUniverseScopePattern.getType() != absoluteRdepsUniverseTargetPattern.getType()) {
+ return Eligibility.NOT_ELIGIBLE;
+ }
+
+ switch (absoluteUniverseScopePattern.getType()) {
+ case PATH_AS_TARGET:
+ case SINGLE_TARGET:
+ return absoluteUniverseScopePattern
+ .getOriginalPattern()
+ .equals(absoluteRdepsUniverseTargetPattern.getOriginalPattern())
+ ? Eligibility.ELIGIBLE_AS_IS
+ : Eligibility.NOT_ELIGIBLE;
+ case TARGETS_IN_PACKAGE:
+ case TARGETS_BELOW_DIRECTORY:
+ if (!absoluteUniverseScopePattern
+ .getDirectory()
+ .equals(absoluteRdepsUniverseTargetPattern.getDirectory())) {
+ return Eligibility.NOT_ELIGIBLE;
+ }
+
+ // Note: If we're here, both patterns are either TARGETS_IN_PACKAGE or
+ // TARGETS_BELOW_DIRECTORY, and are for the same directory.
+
+ if (absoluteUniverseScopePattern.getRulesOnly()
+ == absoluteRdepsUniverseTargetPattern.getRulesOnly()) {
+ return Eligibility.ELIGIBLE_AS_IS;
+ }
+
+ return absoluteUniverseScopePattern.getRulesOnly()
+ // If the actual universe is narrower, then allrdeps would be unsound because it may
+ // produce narrower results.
+ ? Eligibility.NOT_ELIGIBLE
+ // If the actual universe is wider, then allrdeps would produce wider results.
+ // Therefore, we'd want to filter those results.
+ : Eligibility.ELIGIBLE_WITH_FILTERING;
+ }
+ throw new IllegalStateException(absoluteUniverseScopePattern.getType().toString());
+ }
}
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 3c90bc5..3e561a2 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
@@ -376,12 +376,20 @@
return QueryExpressionMapper.identity();
}
TargetPattern.Parser targetPatternParser = new TargetPattern.Parser(parserPrefix);
- String universeScopePattern = Iterables.getOnlyElement(constantUniverseScopeList);
+ String universeScopePatternString = Iterables.getOnlyElement(constantUniverseScopeList);
+ TargetPattern absoluteUniverseScopePattern = null;
+ try {
+ absoluteUniverseScopePattern =
+ targetPatternParser.parse(targetPatternParser.absolutize(universeScopePatternString));
+ } catch (TargetParsingException e) {
+ return QueryExpressionMapper.identity();
+ }
return QueryExpressionMapper.compose(
ImmutableList.of(
- new RdepsToAllRdepsQueryExpressionMapper(targetPatternParser, universeScopePattern),
+ new RdepsToAllRdepsQueryExpressionMapper(
+ targetPatternParser, absoluteUniverseScopePattern),
new FilteredDirectRdepsInUniverseExpressionMapper(
- targetPatternParser, universeScopePattern)));
+ targetPatternParser, absoluteUniverseScopePattern)));
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/KindFunction.java b/src/main/java/com/google/devtools/build/lib/query2/engine/KindFunction.java
index 4030ceb..4d3f82a 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/KindFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/KindFunction.java
@@ -20,12 +20,13 @@
import java.util.List;
/**
- * A kind(pattern, argument) filter expression, which computes the set of subset
- * of nodes in 'argument' whose kind matches the unanchored regexp 'pattern'.
+ * A kind(pattern, argument) filter expression, which computes the set of subset of nodes in
+ * 'argument' whose kind matches the unanchored regexp 'pattern'.
*
* <pre>expr ::= KIND '(' WORD ',' expr ')'</pre>
*
* Example patterns:
+ *
* <pre>
* ' file' Match all file targets.
* 'source file' Match all test source file targets.
@@ -35,11 +36,11 @@
* 'test' Match all test (rule) targets.
* </pre>
*
- * Note, the space before "file" is needed to prevent unwanted matches against
- * (e.g.) "filegroup rule".
+ * Note, the space before "file" is needed to prevent unwanted matches against (e.g.) "filegroup
+ * rule".
*/
-class KindFunction extends RegexFilterExpression {
- KindFunction() {
+public class KindFunction extends RegexFilterExpression {
+ public KindFunction() {
super(/*invert=*/ false);
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeTargetPatternEvaluator.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeTargetPatternEvaluator.java
index 623044d..4b1094a 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeTargetPatternEvaluator.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeTargetPatternEvaluator.java
@@ -228,9 +228,7 @@
private SimpleLookup(String pattern, TargetPatternKey key) {
this(
- pattern,
- PackageValue.key(key.getParsedPattern().getDirectoryForTargetOrTargetsInPackage()),
- key.getParsedPattern());
+ pattern, PackageValue.key(key.getParsedPattern().getDirectory()), key.getParsedPattern());
}
private SimpleLookup(String pattern, PackageValue.Key key, TargetPattern targetPattern) {