Bring PackageSpecification docs up-to-date, clean up its interface
Focuses on documenting the Strings that PackageSpecifications can be
translated from and to.
--
MOS_MIGRATED_REVID=128195540
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index e7c4ac4..e3cf5f6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -80,7 +80,6 @@
import com.google.devtools.build.skyframe.WalkableGraph;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -91,7 +90,6 @@
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
-
import javax.annotation.Nullable;
/**
@@ -970,15 +968,20 @@
throws EvalException, InterruptedException {
BuildConfiguration targetConfig = target.getConfiguration();
return new RuleContext.Builder(
- env, (Rule) target.getTarget(), null, targetConfig, configurations.getHostConfiguration(),
- ruleClassProvider.getPrerequisiteValidator(),
- ((Rule) target.getTarget()).getRuleClassObject().getConfigurationFragmentPolicy())
- .setVisibility(NestedSetBuilder.<PackageSpecification>create(
- Order.STABLE_ORDER, PackageSpecification.EVERYTHING))
- .setPrerequisites(getPrerequisiteMapForTesting(eventHandler, target, configurations))
- .setConfigConditions(ImmutableMap.<Label, ConfigMatchingProvider>of())
- .setUniversalFragment(ruleClassProvider.getUniversalFragment())
- .build();
+ env,
+ (Rule) target.getTarget(),
+ null,
+ targetConfig,
+ configurations.getHostConfiguration(),
+ ruleClassProvider.getPrerequisiteValidator(),
+ ((Rule) target.getTarget()).getRuleClassObject().getConfigurationFragmentPolicy())
+ .setVisibility(
+ NestedSetBuilder.<PackageSpecification>create(
+ Order.STABLE_ORDER, PackageSpecification.everything()))
+ .setPrerequisites(getPrerequisiteMapForTesting(eventHandler, target, configurations))
+ .setConfigConditions(ImmutableMap.<Label, ConfigMatchingProvider>of())
+ .setUniversalFragment(ruleClassProvider.getUniversalFragment())
+ .build();
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index 0597e0b..c6a0f4f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -53,12 +53,10 @@
import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
-
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-
import javax.annotation.Nullable;
/**
@@ -87,7 +85,7 @@
if (ruleVisibility instanceof ConstantRuleVisibility) {
return ((ConstantRuleVisibility) ruleVisibility).isPubliclyVisible()
? NestedSetBuilder.<PackageSpecification>create(
- Order.STABLE_ORDER, PackageSpecification.EVERYTHING)
+ Order.STABLE_ORDER, PackageSpecification.everything())
: NestedSetBuilder.<PackageSpecification>emptySet(Order.STABLE_ORDER);
} else if (ruleVisibility instanceof PackageGroupsRuleVisibility) {
PackageGroupsRuleVisibility packageGroupsVisibility =
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageGroup.java b/src/main/java/com/google/devtools/build/lib/packages/PackageGroup.java
index 67ea4ed..bffd929 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageGroup.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageGroup.java
@@ -21,16 +21,15 @@
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.License.DistributionType;
-
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
- * This class represents a package group. It has a name and a set of packages
- * and can be asked if a specific package is included in it. The package set is
- * represented as a list of PathFragments.
+ * This class represents a package group BUILD target. It has a name, a list of {@link
+ * PackageSpecification}s, a list of {@link Label}s of other package groups this one includes, and
+ * can be asked if a specific package is included in it.
*/
public class PackageGroup implements Target {
private boolean containsErrors;
@@ -40,18 +39,25 @@
private final List<PackageSpecification> packageSpecifications;
private final List<Label> includes;
- public PackageGroup(Label label, Package pkg, Collection<String> packages,
- Collection<Label> includes, EventHandler eventHandler, Location location) {
+ public PackageGroup(
+ Label label,
+ Package pkg,
+ Collection<String> packageSpecifications,
+ Collection<Label> includes,
+ EventHandler eventHandler,
+ Location location) {
this.label = label;
this.location = location;
this.containingPackage = pkg;
this.includes = ImmutableList.copyOf(includes);
ImmutableList.Builder<PackageSpecification> packagesBuilder = ImmutableList.builder();
- for (String containedPackage : packages) {
+ for (String packageSpecification : packageSpecifications) {
PackageSpecification specification = null;
try {
- specification = PackageSpecification.fromString(label, containedPackage);
+ specification =
+ PackageSpecification.fromString(
+ label.getPackageIdentifier().getRepository(), packageSpecification);
} catch (PackageSpecification.InvalidPackageSpecificationException e) {
containsErrors = true;
eventHandler.handle(Event.error(location, e.getMessage()));
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 7d37f10..a50a810 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
@@ -17,62 +17,65 @@
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.vfs.PathFragment;
/**
- * A class that represents some packages that are included in the visibility list of a rule.
+ * Represents one of the following:
+ *
+ * <ul>
+ * <li>A single package (e.g. "//foo/bar")
+ * <li>All transitive subpackages of a package, inclusive (e.g. "//foo/bar/...", which includes
+ * "//foo/bar")
+ * <li>All packages (i.e. "//...")
+ * </ul>
+ *
+ * <p>Typically (exclusively?) used for package visibility, as part of a {@link PackageGroup}
+ * target.
+ *
+ * <p>A package specification is specific to a single {@link RepositoryName} unless it is the "all
+ * packages" specification.
*/
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 = "/...";
- public static final PackageSpecification EVERYTHING =
- new PackageSpecification() {
- @Override
- public boolean containsPackage(PackageIdentifier packageName) {
- return true;
- }
- @Override
- protected String asString() {
- return "//...";
- }
- };
-
+ /** Returns {@code true} if the package spec includes the provided {@code packageName}. */
public abstract boolean containsPackage(PackageIdentifier packageName);
- @Override
- public int hashCode() {
- return asString().hashCode();
- }
-
- @Override
- public final String toString() {
- return asString();
- }
-
- protected abstract String asString();
-
- @Override
- public boolean equals(Object that) {
- if (this == that) {
- return true;
- }
-
- if (!(that instanceof PackageSpecification)) {
- return false;
- }
-
- return this.asString().equals(((PackageSpecification) that).asString());
- }
+ /**
+ * Returns a {@link String} representation of the {@link PackageSpecification} of the same format
+ * accepted by {@link #fromString}.
+ *
+ * <p>The returned {@link String} is insensitive to the {@link RepositoryName} associated with the
+ * {@link PackageSpecification}.
+ */
+ public abstract String toStringWithoutRepository();
/**
- * Parses a string as a visibility specification.
- * Throws {@link InvalidPackageSpecificationException} if the label cannot be parsed.
+ * Parses the provided {@link String} into a {@link PackageSpecification}.
*
- * <p>Note that these strings are different from what {@link #fromLabel} understands.
+ * <p>The {@link String} must have one of the following forms:
+ *
+ * <ul>
+ * <li>The full name of a single package, without repository qualification, prefixed with "//"
+ * (e.g. "//foo/bar"). This results in a {@link PackageSpecification} that contains exactly
+ * the named package.
+ * <li>The full name of a single package, without repository qualification, prefixed with "//",
+ * and suffixed with "/..." (e.g. "//foo/bar/...") This results in a {@link
+ * PackageSpecification} that contains all transitive subpackages of that package, inclusive.
+ * <li>Exactly "//...". This results in a {@link PackageSpecification} that contains all packages.
+ * </ul>
+ *
+ * <p>If and only if the {@link String} is one of the first two forms, the returned {@link
+ * PackageSpecification} is specific to the provided {@link RepositoryName}. Note that it is not
+ * possible to construct a repository-specific {@link PackageSpecification} for all transitive
+ * subpackages of the root package (i.e. a repository-specific "//...").
+ *
+ * <p>Throws {@link InvalidPackageSpecificationException} if the {@link String} cannot be parsed.
*/
- public static PackageSpecification fromString(Label context, final String spec)
+ public static PackageSpecification fromString(RepositoryName repositoryName, String spec)
throws InvalidPackageSpecificationException {
String result = spec;
boolean allBeneath = false;
@@ -80,14 +83,14 @@
allBeneath = true;
result = result.substring(0, result.length() - ALL_BENEATH_SUFFIX.length());
if (result.equals("/")) {
- // Special case: //... will not end in /...
- return EVERYTHING;
+ // spec was "//...".
+ return AllPackages.EVERYTHING;
}
}
if (!spec.startsWith("//")) {
- throw new InvalidPackageSpecificationException("invalid package name '" + spec
- + "': must start with '//'");
+ throw new InvalidPackageSpecificationException(
+ String.format("invalid package name '%s': must start with '//'", spec));
}
PackageIdentifier packageId;
@@ -95,24 +98,33 @@
packageId = PackageIdentifier.parse(result);
} catch (LabelSyntaxException e) {
throw new InvalidPackageSpecificationException(
- "invalid package name '" + spec + "': " + e.getMessage());
+ String.format("invalid package name '%s': %s", spec, e.getMessage()));
}
-
Verify.verify(packageId.getRepository().isDefault());
- packageId = PackageIdentifier.create(
- context.getPackageIdentifier().getRepository(), packageId.getPackageFragment());
- return allBeneath ?
- new AllPackagesBeneath(packageId) :
- new SinglePackage(packageId);
+ PackageIdentifier packageIdForSpecifiedRepository =
+ PackageIdentifier.create(repositoryName, packageId.getPackageFragment());
+ return allBeneath
+ ? new AllPackagesBeneath(packageIdForSpecifiedRepository)
+ : new SinglePackage(packageIdForSpecifiedRepository);
}
/**
- * Parses a label as a visibility specification. returns null if the label cannot be parsed.
+ * Parses the provided {@link Label} into a {@link PackageSpecification} specific to the {@link
+ * RepositoryName} associated with the label.
*
- * <p>Note that these strings are different from what {@link #fromString} understands.
+ * <p>If {@code label.getName.equals("__pkg__")} then this results in a {@link
+ * PackageSpecification} that contains exactly the named package.
+ *
+ * <p>If {@code label.getName.equals("__subpackages__")} then this results in a {@link
+ * PackageSpecification} that contains all transitive subpackages of that package, inclusive.
+ *
+ * <p>If the label's name is neither "__pkg__" nor "__subpackages__", this returns {@code null}.
+ *
+ * <p>Note that there is no {@link Label} associated with the {@link RepositoryName}-agnostic "all
+ * packages" specification (corresponding to {@code #fromString(null, "//...")}).
*/
- public static PackageSpecification fromLabel(Label label) {
+ static PackageSpecification fromLabel(Label label) {
if (label.getName().equals(PACKAGE_LABEL)) {
return new SinglePackage(label.getPackageIdentifier());
} else if (label.getName().equals(SUBTREE_LABEL)) {
@@ -122,10 +134,14 @@
}
}
+ public static PackageSpecification everything() {
+ return AllPackages.EVERYTHING;
+ }
+
private static class SinglePackage extends PackageSpecification {
private PackageIdentifier singlePackageName;
- public SinglePackage(PackageIdentifier packageName) {
+ private SinglePackage(PackageIdentifier packageName) {
this.singlePackageName = packageName;
}
@@ -135,15 +151,37 @@
}
@Override
- protected String asString() {
+ public String toStringWithoutRepository() {
+ return "//" + singlePackageName.getPackageFragment().getPathString();
+ }
+
+ @Override
+ public String toString() {
return singlePackageName.toString();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof SinglePackage)) {
+ return false;
+ }
+ SinglePackage that = (SinglePackage) o;
+ return singlePackageName.equals(that.singlePackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return singlePackageName.hashCode();
+ }
}
private static class AllPackagesBeneath extends PackageSpecification {
private PackageIdentifier prefix;
- public AllPackagesBeneath(PackageIdentifier prefix) {
+ private AllPackagesBeneath(PackageIdentifier prefix) {
this.prefix = prefix;
}
@@ -154,16 +192,69 @@
}
@Override
- protected String asString() {
- return prefix.equals(new PathFragment("")) ? "..." : prefix + "/...";
+ public String toStringWithoutRepository() {
+ return "//" + prefix.getPackageFragment().getPathString() + ALL_BENEATH_SUFFIX;
+ }
+
+ @Override
+ public String toString() {
+ if (prefix.getPackageFragment().equals(PathFragment.EMPTY_FRAGMENT)) {
+ return "//...";
+ }
+ return prefix + "/...";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AllPackagesBeneath)) {
+ return false;
+ }
+ AllPackagesBeneath that = (AllPackagesBeneath) o;
+ return prefix.equals(that.prefix);
+ }
+
+ @Override
+ public int hashCode() {
+ return prefix.hashCode();
}
}
- /**
- * Exception class to be thrown when a specification cannot be parsed.
- */
- public static class InvalidPackageSpecificationException extends Exception {
- public InvalidPackageSpecificationException(String message) {
+ private static class AllPackages extends PackageSpecification {
+
+ private static final PackageSpecification EVERYTHING = new AllPackages();
+
+ @Override
+ public boolean containsPackage(PackageIdentifier packageName) {
+ return true;
+ }
+
+ @Override
+ public String toStringWithoutRepository() {
+ return "//...";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof AllPackages;
+ }
+
+ @Override
+ public int hashCode() {
+ return "//...".hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "//...";
+ }
+ }
+
+ /** Exception class to be thrown when a specification cannot be parsed. */
+ static class InvalidPackageSpecificationException extends Exception {
+ private InvalidPackageSpecificationException(String message) {
super(message);
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
index 8f993ab..404b7aa 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/PackageGroupStaticInitializationTest.java
@@ -16,18 +16,17 @@
import static org.junit.Assert.assertFalse;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.util.EventCollectionApparatus;
import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-
+import java.util.concurrent.SynchronousQueue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.SynchronousQueue;
-
/**
* Checks against a class initialization deadlock. "query sometimes hangs".
*
@@ -51,8 +50,11 @@
@Override
public void run() {
try {
- groupQueue.put(PackageSpecification.fromString(
- Label.parseAbsoluteUnchecked("//context"), "//fruits/..."));
+ RepositoryName defaultRepoName =
+ Label.parseAbsoluteUnchecked("//context")
+ .getPackageIdentifier()
+ .getRepository();
+ groupQueue.put(PackageSpecification.fromString(defaultRepoName, "//fruits/..."));
} catch (Exception e) {
// Can't throw from Runnable, but this will cause the test to timeout
// when the consumer can't take the object.
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 24002f2..50ca268 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
@@ -23,7 +23,6 @@
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -151,7 +150,7 @@
scratch.file("fruits/BUILD", "package_group(name = 'mango', packages = ['//...'])");
PackageGroup packageGroup = getPackageGroup("fruits", "mango");
assertThat(packageGroup.getPackageSpecifications())
- .containsExactlyElementsIn(ImmutableList.of(PackageSpecification.EVERYTHING));
+ .containsExactlyElementsIn(ImmutableList.of(PackageSpecification.everything()));
}
private Package getPackage(String packageName) throws Exception {