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
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
index 4c48f0e..12f0eaf 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupTest.java
@@ -144,6 +144,93 @@
   }
 
   @Test
+  public void testNegative() throws Exception {
+    scratch.file("one/BUILD");
+    scratch.file("two/BUILD");
+    scratch.file("three/BUILD");
+    scratch.file("four/BUILD");
+    scratch.file(
+        "test/BUILD",
+        "package_group(",
+        "  name = 'packages',",
+        "    packages = [",
+        "        '//one',",
+        "        '//two',",
+        "        '-//three',",
+        "        '-//four',",
+        "    ],",
+        ")");
+
+    PackageGroup grp = getPackageGroup("test", "packages");
+    assertThat(grp.contains(getPackage("one"))).isTrue();
+    assertThat(grp.contains(getPackage("two"))).isTrue();
+    assertThat(grp.contains(getPackage("three"))).isFalse();
+    assertThat(grp.contains(getPackage("four"))).isFalse();
+  }
+
+  @Test
+  public void testNegative_noSubpackages() throws Exception {
+    scratch.file("pkg/BUILD");
+    scratch.file("pkg/one/BUILD");
+    scratch.file("pkg/one/two/BUILD");
+    scratch.file(
+        "test/BUILD",
+        "package_group(",
+        "  name = 'packages',",
+        "    packages = [",
+        "        '//pkg/...',",
+        "        '-//pkg/one',",
+        "    ],",
+        ")");
+
+    PackageGroup grp = getPackageGroup("test", "packages");
+    assertThat(grp.contains(getPackage("pkg"))).isTrue();
+    assertThat(grp.contains(getPackage("pkg/one"))).isFalse();
+    assertThat(grp.contains(getPackage("pkg/one/two"))).isTrue();
+  }
+
+  @Test
+  public void testNegative_subpackages() throws Exception {
+    scratch.file("pkg/BUILD");
+    scratch.file("pkg/one/BUILD");
+    scratch.file("pkg/one/two/BUILD");
+    scratch.file(
+        "test/BUILD",
+        "package_group(",
+        "  name = 'packages',",
+        "    packages = [",
+        "        '//pkg/...',",
+        "        '-//pkg/one/...',",
+        "    ],",
+        ")");
+
+    PackageGroup grp = getPackageGroup("test", "packages");
+    assertThat(grp.contains(getPackage("pkg"))).isTrue();
+    assertThat(grp.contains(getPackage("pkg/one"))).isFalse();
+    assertThat(grp.contains(getPackage("pkg/one/two"))).isFalse();
+  }
+
+  @Test
+  public void testNegative_everything() throws Exception {
+    scratch.file("pkg/BUILD");
+    scratch.file("pkg/one/BUILD");
+    scratch.file("pkg/one/two/BUILD");
+    scratch.file(
+        "test/BUILD",
+        "package_group(",
+        "  name = 'packages',",
+        "    packages = [",
+        "        '-//...',",
+        "    ],",
+        ")");
+
+    PackageGroup grp = getPackageGroup("test", "packages");
+    assertThat(grp.contains(getPackage("pkg"))).isFalse();
+    assertThat(grp.contains(getPackage("pkg/one"))).isFalse();
+    assertThat(grp.contains(getPackage("pkg/one/two"))).isFalse();
+  }
+
+  @Test
   public void testEverythingSpecificationWorks() throws Exception {
     scratch.file("fruits/BUILD", "package_group(name = 'mango', packages = ['//...'])");
     PackageGroup packageGroup = getPackageGroup("fruits", "mango");