From 7cc8efe44a167d818deb77e53f12cecc15b4e4aa Mon Sep 17 00:00:00 2001 From: cushon Date: Tue, 21 Nov 2017 13:17:49 -0800 Subject: Add support for negative package specifications Package specifications can now be prefixed with `-` to indicate negation: the specification `-//foo/bar/...` excludes all packages under `//foo/bar` that would otherwise have been matched. RELNOTES: Package specifications can now be prefixed with `-` to indicate negation PiperOrigin-RevId: 176551382 --- .../build/lib/packages/PackageSpecification.java | 77 +++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) (limited to 'src/main/java/com/google/devtools/build/lib/packages') diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageSpecification.java b/src/main/java/com/google/devtools/build/lib/packages/PackageSpecification.java index 63823fbcaa..56d59e09c2 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/PackageSpecification.java +++ b/src/main/java/com/google/devtools/build/lib/packages/PackageSpecification.java @@ -43,6 +43,7 @@ public abstract class PackageSpecification { private static final String PACKAGE_LABEL = "__pkg__"; private static final String SUBTREE_LABEL = "__subpackages__"; private static final String ALL_BENEATH_SUFFIX = "/..."; + private static final String NEGATIVE_PREFIX = "-"; /** Returns {@code true} if the package spec includes the provided {@code packageName}. */ protected abstract boolean containsPackage(PackageIdentifier packageName); @@ -56,6 +57,11 @@ public abstract class PackageSpecification { */ protected abstract String toStringWithoutRepository(); + /** Returns {@code true} if the package specification represents a negated match. */ + protected boolean negative() { + return false; + } + /** * Parses the provided {@link String} into a {@link PackageSpecification}. * @@ -81,6 +87,18 @@ public abstract class PackageSpecification { public static PackageSpecification fromString(RepositoryName repositoryName, String spec) throws InvalidPackageSpecificationException { String result = spec; + boolean negative = false; + if (result.startsWith(NEGATIVE_PREFIX)) { + negative = true; + result = result.substring(NEGATIVE_PREFIX.length()); + } + PackageSpecification packageSpecification = fromStringPositive(repositoryName, result); + return negative ? new NegativePackageSpecification(packageSpecification) : packageSpecification; + } + + private static PackageSpecification fromStringPositive(RepositoryName repositoryName, String spec) + throws InvalidPackageSpecificationException { + String result = spec; boolean allBeneath = false; if (result.endsWith(ALL_BENEATH_SUFFIX)) { allBeneath = true; @@ -225,6 +243,50 @@ public abstract class PackageSpecification { } } + /** A package specification for a negative match, e.g. {@code -//pkg/sub/...}. */ + private static class NegativePackageSpecification extends PackageSpecification { + + private final PackageSpecification delegate; + + private NegativePackageSpecification(PackageSpecification delegate) { + this.delegate = delegate; + } + + @Override + protected boolean negative() { + return true; + } + + @Override + protected boolean containsPackage(PackageIdentifier packageName) { + return delegate.containsPackage(packageName); + } + + @Override + protected String toStringWithoutRepository() { + return "-" + delegate.toStringWithoutRepository(); + } + + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + return obj instanceof NegativePackageSpecification + && delegate.equals(((NegativePackageSpecification) obj).delegate); + } + + @Override + public String toString() { + return "-" + delegate; + } + } + private static class AllPackages extends PackageSpecification { private static final PackageSpecification EVERYTHING = new AllPackages(); @@ -286,10 +348,21 @@ public abstract class PackageSpecification { /** * Returns {@code true} if the package specifications include the provided {@code packageName}. - * That is, at least one positive package specification matches. + * That is, at least one positive package specification matches, and no negative package + * specifications match. */ public boolean containsPackage(PackageIdentifier packageIdentifier) { - return packageSpecifications.stream().anyMatch(p -> p.containsPackage(packageIdentifier)); + boolean match = false; + for (PackageSpecification p : packageSpecifications) { + if (p.containsPackage(packageIdentifier)) { + if (p.negative()) { + return false; + } else { + match = true; + } + } + } + return match; } /** -- cgit v1.2.3