package_group: Add "public"/"private" constants, fix "//..." meaning
This CL does three things:
1. It adds the special package specifications "public" and "private, guarded behind the flag `--incompatible_package_group_has_public_syntax`.
2. It fixes the behavior of package specification "//..." to mean "all packages in the current repo" rather than "all packages in any repo", guarded behind the flag `--incompatible_fix_package_group_reporoot_syntax`.
3. It fixes the behavior of `bazel query --output=proto` (and `--output=xml`) so that the `packages` attribute is serialized without dropping the leading `//` from package names -- it is now `"//foo/bar/..."` instead of `"foo/bar/..."`. This behavioral change is guarded behind `--incompatible_package_group_includes_double_slash`.
The .bzl `visibility()` builtin always acts as if the first two flags are enabled.
Work toward #11261.
Work toward #16323.
Work toward #16355.
Work toward #16391.
RELNOTES: Added three `package_group`-related flags: `--incompatible_package_group_includes_double_slash` (#16391), `--incompatible_package_group_has_public_syntax` (#16355), and `--incompatible_fix_package_group_reporoot_syntax` (#16323). With these flags, `package_group` can now easily specify "all packages", "no packages", and "all packages in the current repo".
In PackageSpecification:
- fromString(), fromStringPositive(): rewrote docstring, added bool args for flags, renamed or removed vars for simplicity, added handling of public/private and flag behavior
- AllPackagesBeneath(): fixed asStringWithoutRepository() for case of //..., which wasn't likely to arise before.
- normalize order of equals(), hashCode(), etc.
- AllPackages: change hash, and change str representation to "public"
- Add NoPackages, symmetric to AllPackages
- NegativePackageSpecification: don't reuse the delegate's hash as our own hash verbatim
In PackageGroupTest:
- Move `testEverythingSpecificationWorks` to above `testNegativeEverything`, and made it use "public" syntax in place of "//...".
- Add test for "private" syntax.
- Add test for flag guarding "public"/"private".
- Add tests for old and new "//..." behavior.
- Add tests that you can't negate "public" or "private".
- Add test for illegal combination of flags.
- Add assertions for stringification of "public"/"private".
Other changes:
- Update XmlOutputFormatterTest and ProtoOutputFormatterTest with test cases for the flag. In the latter case, parameterize a helper to allow setting per-test-case flags.
- Add test cases to BzlLoadFunctionTest for bzl visibility accepting the list forms `["public"]`/`["private"]`, and for its behavior being unaffected by the new flags.
PiperOrigin-RevId: 479347358
Change-Id: I124ca5406bff15615adaa1256e2d7bef69b9d9a5
diff --git a/site/en/concepts/visibility.md b/site/en/concepts/visibility.md
index 89efcfb..596e838 100644
--- a/site/en/concepts/visibility.md
+++ b/site/en/concepts/visibility.md
@@ -45,7 +45,8 @@
* Package groups use a different syntax for specifying packages. Within a
package group, the forms `"//foo/bar:__pkg__"` and
`"//foo/bar:__subpackages__"` are respectively replaced by `"//foo/bar"`
- and `"//foo/bar/..."`.
+ and `"//foo/bar/..."`. Likewise, `"//visibility:public"` and
+ `"//visibility:private"` are just `"public"` and `"private"`.
For example, if `//some/package:mytarget` has its `visibility` set to
`[":__subpackages__", "//tests:__pkg__"]`, then it could be used by any target
diff --git a/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm b/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
index 164ece7..c8c64fd 100644
--- a/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
+++ b/src/main/java/com/google/devtools/build/docgen/templates/be/functions.vm
@@ -178,13 +178,18 @@
<li>As above, but with a trailing <code>/...</code>. For example, <code>
//foo/...</code> specifies the set of <code>//foo</code> and all its
- subpackages. As a special case, <code>//...</code> specifies all
- packages in any repository (in other words, public).
+ subpackages. <code>//...</code> specifies all packages in the current
+ repository.
+
+ <li>The strings <code>public</code> or <code>private</code>, which
+ respectively specify every package or no package. (This form requires
+ the flag <code>--incompatible_package_group_has_public_syntax</code> to
+ be set.)
</ol>
- <p>In addition, a package specification may also be prefixed with
- <code>-</code> to indicate that it is negated.</p>
+ <p>In addition, the first two kinds of package specifications may also
+ be prefixed with <code>-</code> to indicate that they are negated.</p>
<p>The package group contains any package that matches at least one of
its positive specifications and none of its negative specifications
@@ -193,11 +198,24 @@
subpackages of <code>//foo/tests</code>. (<code>//foo</code> itself is
included while </code>//foo/tests</code> itself is not.)</p>
- <p>Aside from <code>//...</code>, there is no way to directly specify
+ <p>Aside from public visibility, there is no way to directly specify
packages outside the current repository.</p>
<p>If this attribute is missing, it is the same as setting it to an
- empty list (in other words, private).
+ empty list, which is also the same as setting it to a list containing
+ only <code>private</code>.
+
+ <p><i>Note:</i> The specification <code>//...</code> has a legacy behavior
+ of being the same as <code>public</code>. This behavior is disabled when
+ <code>--incompatible_fix_package_group_reporoot_syntax</code> is
+ enabled.</p>
+
+ <p><i>Note:</i> As a legacy behavior, when this attribute is serialized as
+ part of <code>bazel query --output=proto</code> (or
+ <code>--output=xml</code>), the leading slashes are omitted if
+ <code>--incompatible_package_group_includes_double_slash</code> is not
+ enabled. For instance, <code>//pkg/foo/...</code> will output as
+ <code>\"pkg/foo/...\"</code>.</p>
</td>
</tr>
<tr>
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index f9dec2a..a73a3d3 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -1652,14 +1652,26 @@
return Label.create(pkg.getPackageIdentifier(), targetName);
}
- /**
- * Adds a package group to the package.
- */
- void addPackageGroup(String name, Collection<String> packages, Collection<Label> includes,
- EventHandler eventHandler, Location location)
+ /** Adds a package group to the package. */
+ void addPackageGroup(
+ String name,
+ Collection<String> packages,
+ Collection<Label> includes,
+ boolean allowPublicPrivate,
+ boolean repoRootMeansCurrentRepo,
+ EventHandler eventHandler,
+ Location location)
throws NameConflictException, LabelSyntaxException {
PackageGroup group =
- new PackageGroup(createLabel(name), pkg, packages, includes, eventHandler, location);
+ new PackageGroup(
+ createLabel(name),
+ pkg,
+ packages,
+ includes,
+ allowPublicPrivate,
+ repoRootMeansCurrentRepo,
+ eventHandler,
+ location);
Target existing = targets.get(group.getName());
if (existing != null) {
throw nameConflict(group, existing);
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 ebad8d7..f9f9a79 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
@@ -47,6 +47,8 @@
Package pkg,
Collection<String> packageSpecifications,
Collection<Label> includes,
+ boolean allowPublicPrivate,
+ boolean repoRootMeansCurrentRepo,
EventHandler eventHandler,
Location location) {
this.label = label;
@@ -61,7 +63,11 @@
PackageSpecification specification = null;
try {
specification =
- PackageSpecification.fromString(label.getRepository(), packageSpecification);
+ PackageSpecification.fromString(
+ label.getRepository(),
+ packageSpecification,
+ allowPublicPrivate,
+ repoRootMeansCurrentRepo);
} catch (PackageSpecification.InvalidPackageSpecificationException e) {
errorsFound = true;
eventHandler.handle(
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 8c5433d..51696ae 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
@@ -47,11 +47,15 @@
* packages" specification.
*/
public abstract class PackageSpecification {
- private static final String PACKAGE_LABEL = "__pkg__";
- private static final String SUBTREE_LABEL = "__subpackages__";
+ private static final String PUBLIC_VISIBILITY = "public";
+ private static final String PRIVATE_VISIBILITY = "private";
private static final String ALL_BENEATH_SUFFIX = "/...";
private static final String NEGATIVE_PREFIX = "-";
+ // Used for interpreting `visibility` labels.
+ private static final String PACKAGE_LABEL = "__pkg__";
+ private static final String SUBTREE_LABEL = "__subpackages__";
+
/** Returns {@code true} if the package spec includes the provided {@code packageName}. */
protected abstract boolean containsPackage(PackageIdentifier packageName);
@@ -82,9 +86,13 @@
* additional context. But, for instance, if interpreted with respect to a {@code package_group}'s
* {@code packages} attribute, the strings always have the same repository as the package group.
*/
- // TODO(brandjon): This method's main benefit is that it's round-trippable. We could eliminate
- // it in favor of asString() if we provided a public variant of fromString() that tolerates
- // repositories.
+ // TODO(brandjon): This method's main benefit is that it's round-trippable. But we can already
+ // achieve the same thing with asString(), if the caller parses out the repo to pass to
+ // fromString() as a separate arg. It'd be nice to eliminate this method in favor of asString()
+ // and make a version of fromString() that can parse repo names in the label. We'd just have to
+ // mimic the Label parsing of repo (we can't reuse Label parsing directly since it won't like the
+ // `/...` syntax). Repo remapping shouldn't come up, since the names we get from stringification
+ // ought to already be canonical/absolute.
protected abstract String asStringWithoutRepository();
@Override
@@ -93,71 +101,124 @@
}
/**
- * Parses the provided {@link String} into a {@link PackageSpecification}.
+ * Parses the string {@code spec} into a {@link PackageSpecification}, within the context of the
+ * given repository name.
*
- * <p>The {@link String} must have one of the following forms:
+ * <p>{@code spec} may have 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>
+ * <ol>
+ * <li>The full name of a single package, without repository qualification, prefixed with "//"
+ * (e.g. "//foo/bar"). The resulting specification contains exactly that package.
+ * <li>The same, but suffixed with "/..." for a non-root package ("//foo/bar/...") or "..." for
+ * the root package ("//..."). The resulting specification contains that package and all its
+ * subpackages.
+ * <li>The string constants "public" or "private". The resulting specification contains either
+ * all packages or no packages, respectively.
+ * </ol>
*
- * <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 "//...").
+ * In the first two cases, the repository of the given package name is taken to be {@code
+ * repositoryName}. In the third case the repository name is ignored.
*
- * <p>Throws {@link InvalidPackageSpecificationException} if the {@link String} cannot be parsed.
+ * <p>In the first two cases, {@code spec} may also be prefixed by a "-". The resulting
+ * specification contains the same set of packages but is marked as being negated. (Negation logic
+ * is applied at the level of {@link PackageGroupContents}.)
+ *
+ * <p>Setting {@code allowPublicPrivate} to false disallows the string constants "public" and
+ * "private". Note that if {@link #asString} is called with {@code includeDoubleSlash} set to
+ * false, the stringification of "public" and "//public" is ambiguous (likewise for private),
+ * hence why it might be appropriate to prohibit these forms.
+ *
+ * <p>Setting {@code repoRootMeansCurrentRepo} to false restores the following legacy behavior: In
+ * the specific case where {@code spec} is "//..." (or its negation), the package specification
+ * contains <i>all</i> packages (possibly marked as negated) rather than just those packages in
+ * {@code repositoryName}. In other words, "//..." behaves the same as "public". However, "//"
+ * still represents just the root package of {@code repositoryName}.
+ *
+ * <p>To protect against requiring users to update to a disallowed syntax, it is illegal to
+ * specify {@code repoRootMeansCurrentRepo} without also specifying {@code allowPublicPrivate}.
+ *
+ * @throws InvalidPackageSpecificationException if the string does not fit one of these forms
*/
- public static PackageSpecification fromString(RepositoryName repositoryName, String spec)
+ // TODO(#16365): Remove allowPublicPrivate.
+ // TODO(#16324): Remove legacy behavior and repoRootMeansCurrentRepo param.
+ public static PackageSpecification fromString(
+ RepositoryName repositoryName,
+ String spec,
+ boolean allowPublicPrivate,
+ boolean repoRootMeansCurrentRepo)
throws InvalidPackageSpecificationException {
- String result = spec;
- boolean negative = false;
- if (result.startsWith(NEGATIVE_PREFIX)) {
- negative = true;
- result = result.substring(NEGATIVE_PREFIX.length());
+ if (repoRootMeansCurrentRepo && !allowPublicPrivate) {
+ throw new InvalidPackageSpecificationException(
+ "Cannot use new \"//...\" meaning without allowing new \"public\" syntax. Try enabling"
+ + " --incompatible_package_group_has_public_syntax or disabling"
+ + " --incompatible_fix_package_group_reporoot_syntax.");
}
- PackageSpecification packageSpecification = fromStringPositive(repositoryName, result);
+ if (!allowPublicPrivate
+ && (spec.equals(PUBLIC_VISIBILITY) || spec.equals(PRIVATE_VISIBILITY))) {
+ throw new InvalidPackageSpecificationException(
+ String.format(
+ "Use of \"%s\" package specification requires enabling"
+ + " --incompatible_package_group_has_public_syntax",
+ spec));
+ }
+ boolean negative = false;
+ if (spec.startsWith(NEGATIVE_PREFIX)) {
+ negative = true;
+ spec = spec.substring(NEGATIVE_PREFIX.length());
+ if (spec.equals(PUBLIC_VISIBILITY) || spec.equals(PRIVATE_VISIBILITY)) {
+ throw new InvalidPackageSpecificationException(
+ String.format("Cannot negate \"%s\" package specification", spec));
+ }
+ }
+ PackageSpecification packageSpecification =
+ fromStringPositive(repositoryName, spec, repoRootMeansCurrentRepo);
return negative ? new NegativePackageSpecification(packageSpecification) : packageSpecification;
}
- private static PackageSpecification fromStringPositive(RepositoryName repositoryName, String spec)
+ private static PackageSpecification fromStringPositive(
+ RepositoryName repositoryName, String spec, boolean repoRootMeansCurrentRepo)
throws InvalidPackageSpecificationException {
- String result = spec;
- boolean allBeneath = false;
- if (result.endsWith(ALL_BENEATH_SUFFIX)) {
- allBeneath = true;
- result = result.substring(0, result.length() - ALL_BENEATH_SUFFIX.length());
- if (result.equals("/")) {
- // spec was "//...".
- return AllPackages.EVERYTHING;
- }
+ if (spec.equals(PUBLIC_VISIBILITY)) {
+ return AllPackages.INSTANCE;
+ } else if (spec.equals(PRIVATE_VISIBILITY)) {
+ return NoPackages.INSTANCE;
}
-
if (!spec.startsWith("//")) {
throw new InvalidPackageSpecificationException(
- String.format("invalid package name '%s': must start with '//'", spec));
+ String.format(
+ "invalid package name '%s': must start with '//' or be 'public' or 'private'", spec));
}
- PackageIdentifier packageId;
+ String pkgPath;
+ boolean allBeneath = false;
+ if (spec.endsWith(ALL_BENEATH_SUFFIX)) {
+ allBeneath = true;
+ pkgPath = spec.substring(0, spec.length() - ALL_BENEATH_SUFFIX.length());
+ if (pkgPath.equals("/")) {
+ // spec was "//...".
+ if (repoRootMeansCurrentRepo) {
+ pkgPath = "//";
+ } else {
+ // Legacy behavior: //... is "public".
+ return AllPackages.INSTANCE;
+ }
+ }
+ } else {
+ pkgPath = spec;
+ }
+
+ PackageIdentifier unqualifiedPkgId;
try {
- packageId = PackageIdentifier.parse(result);
+ unqualifiedPkgId = PackageIdentifier.parse(pkgPath);
} catch (LabelSyntaxException e) {
throw new InvalidPackageSpecificationException(
String.format("invalid package name '%s': %s", spec, e.getMessage()));
}
- Verify.verify(packageId.getRepository().isMain());
+ Verify.verify(unqualifiedPkgId.getRepository().isMain());
- PackageIdentifier packageIdForSpecifiedRepository =
- PackageIdentifier.create(repositoryName, packageId.getPackageFragment());
- return allBeneath
- ? new AllPackagesBeneath(packageIdForSpecifiedRepository)
- : new SinglePackage(packageIdForSpecifiedRepository);
+ PackageIdentifier pkgId =
+ PackageIdentifier.create(repositoryName, unqualifiedPkgId.getPackageFragment());
+ return allBeneath ? new AllPackagesBeneath(pkgId) : new SinglePackage(pkgId);
}
/**
@@ -165,13 +226,21 @@
*
* <p>This rejects negative package patterns, and translates the exception type into {@code
* EvalException}.
+ *
+ * <p>Note that .bzl visibility package specifications always behave as if {@code
+ * --incompatible_package_group_has_public_syntax} and {@code
+ * --incompatible_fix_package_group_reporoot_syntax} are enabled.
*/
- // TODO(b/22193153): Support negatives too.
public static PackageSpecification fromStringForBzlVisibility(
RepositoryName repositoryName, String spec) throws EvalException {
PackageSpecification result;
try {
- result = fromString(repositoryName, spec);
+ result =
+ fromString(
+ repositoryName,
+ spec,
+ /*allowPublicPrivate=*/ true,
+ /*repoRootMeansCurrentRepo=*/ true);
} catch (InvalidPackageSpecificationException e) {
throw new EvalException(e.getMessage());
}
@@ -193,8 +262,8 @@
*
* <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, "//...")}).
+ * <p>Note that there is no {@link Label} associated with the {@link RepositoryName}-agnostic
+ * "public" specification ("//..." under legacy semantics).
*/
@Nullable
static PackageSpecification fromLabel(Label label) {
@@ -208,7 +277,11 @@
}
public static PackageSpecification everything() {
- return AllPackages.EVERYTHING;
+ return AllPackages.INSTANCE;
+ }
+
+ public static PackageSpecification nothing() {
+ return NoPackages.INSTANCE;
}
private static final class SinglePackage extends PackageSpecification {
@@ -291,7 +364,12 @@
@Override
protected String asStringWithoutRepository() {
- return "//" + prefix.getPackageFragment().getPathString() + ALL_BENEATH_SUFFIX;
+ if (prefix.getPackageFragment().equals(PathFragment.EMPTY_FRAGMENT)) {
+ // Special case: Emit "//..." rather than suffixing "/...", which would yield "///...".
+ return "//...";
+ } else {
+ return "//" + prefix.getPackageFragment().getPathString() + ALL_BENEATH_SUFFIX;
+ }
}
@Override
@@ -336,11 +414,6 @@
}
@Override
- public int hashCode() {
- return delegate.hashCode();
- }
-
- @Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
@@ -348,12 +421,17 @@
return obj instanceof NegativePackageSpecification
&& delegate.equals(((NegativePackageSpecification) obj).delegate);
}
+
+ @Override
+ public int hashCode() {
+ return NegativePackageSpecification.class.hashCode() ^ delegate.hashCode();
+ }
}
@VisibleForSerialization
static final class AllPackages extends PackageSpecification {
@SerializationConstant @VisibleForSerialization
- static final PackageSpecification EVERYTHING = new AllPackages();
+ static final PackageSpecification INSTANCE = new AllPackages();
@Override
protected boolean containsPackage(PackageIdentifier packageName) {
@@ -362,14 +440,15 @@
@Override
protected String asString(boolean includeDoubleSlash) {
- // Note that "//..." is the desired result, not "...", even under the legacy behavior of
- // includeDoubleSlash=false.
- return "//...";
+ // Under legacy formatting rules, use legacy syntax. This avoids ambiguity between "public"
+ // and "//public", and ensures that AllPackages is round-trippable when the value of
+ // includeDoubleSlash matches allowPublicPrivate.
+ return includeDoubleSlash ? "public" : "//...";
}
@Override
protected String asStringWithoutRepository() {
- return "//...";
+ return "public";
}
@Override
@@ -379,7 +458,38 @@
@Override
public int hashCode() {
- return "//...".hashCode();
+ return AllPackages.class.hashCode();
+ }
+ }
+
+ @VisibleForSerialization
+ static final class NoPackages extends PackageSpecification {
+ @SerializationConstant @VisibleForSerialization
+ static final PackageSpecification INSTANCE = new NoPackages();
+
+ @Override
+ protected boolean containsPackage(PackageIdentifier packageName) {
+ return false;
+ }
+
+ @Override
+ protected String asString(boolean includeDoubleSlash) {
+ return "private";
+ }
+
+ @Override
+ protected String asStringWithoutRepository() {
+ return "private";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof NoPackages;
+ }
+
+ @Override
+ public int hashCode() {
+ return NoPackages.class.hashCode();
}
}
@@ -437,7 +547,11 @@
for (PackageSpecification spec : packageSpecifications) {
if (spec instanceof SinglePackage) {
singlePositives.put(((SinglePackage) spec).singlePackageName, spec);
- } else if (spec instanceof AllPackages || spec instanceof AllPackagesBeneath) {
+ } else if (spec instanceof AllPackages
+ || spec instanceof AllPackagesBeneath
+ // We can't drop NoPackages because it still needs to be serialized, e.g. in bazel query
+ // output.
+ || spec instanceof NoPackages) {
otherPositives.add(spec);
} else if (spec instanceof NegativePackageSpecification) {
negatives.add(spec);
@@ -483,6 +597,10 @@
*
* <p>Note that strings for specs that cross repositories can't be reparsed using {@link
* PackageSpecification#fromString}.
+ *
+ * <p>The special public constant will serialize as {@code "public"} if {@code
+ * includeDoubleSlash} is true, and {@code "//..."} otherwise. The private constant will always
+ * serialize as {@code "private"},
*/
public Stream<String> streamPackageStrings(boolean includeDoubleSlash) {
return streamSpecs().map(spec -> spec.asString(includeDoubleSlash));
@@ -492,6 +610,9 @@
* Maps {@link PackageSpecification#asStringWithoutRepository} to the component package specs.
*
* <p>Note that this is ambiguous w.r.t. specs that reference other repositories.
+ *
+ * <p>The special public and private constants will serialize as {@code "public"} and {@code
+ * "private"} respectively.
*/
public Stream<String> streamPackageStringsWithoutRepository() {
return streamSpecs().map(PackageSpecification::asStringWithoutRepository);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
index f45c882..703b9de 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
@@ -542,7 +542,18 @@
Location loc = thread.getCallerLocation();
try {
- context.pkgBuilder.addPackageGroup(name, packages, includes, context.eventHandler, loc);
+ context.pkgBuilder.addPackageGroup(
+ name,
+ packages,
+ includes,
+ /*allowPublicPrivate=*/ thread
+ .getSemantics()
+ .getBool(BuildLanguageOptions.INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX),
+ /*repoRootMeansCurrentRepo=*/ thread
+ .getSemantics()
+ .getBool(BuildLanguageOptions.INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX),
+ context.eventHandler,
+ loc);
return Starlark.NONE;
} catch (LabelSyntaxException e) {
throw Starlark.errorf("package group has invalid name: %s: %s", name, e.getMessage());
diff --git a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
index a3518ad..4605061 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/semantics/BuildLanguageOptions.java
@@ -443,6 +443,31 @@
public boolean incompatibleDisallowStructProviderSyntax;
@Option(
+ name = "incompatible_package_group_has_public_syntax",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+ effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+ metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
+ help =
+ "In package_group's `packages` attribute, allows writing \"public\" or \"private\" to"
+ + " refer to all packages or no packages respectively.")
+ public boolean incompatiblePackageGroupHasPublicSyntax;
+
+ @Option(
+ name = "incompatible_fix_package_group_reporoot_syntax",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+ effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+ metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
+ help =
+ "In package_group's `packages` attribute, changes the meaning of the value \"//...\" to"
+ + " refer to all packages in the current repository instead of all packages in any"
+ + " repository. You can use the special value \"public\" in place of \"//...\" to"
+ + " obtain the old behavior. This flag requires"
+ + " that --incompatible_package_group_has_public_syntax also be enabled.")
+ public boolean incompatibleFixPackageGroupReporootSyntax;
+
+ @Option(
name = "incompatible_visibility_private_attributes_at_definition",
defaultValue = "false",
documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
@@ -675,6 +700,12 @@
.setBool(
INCOMPATIBLE_DISALLOW_STRUCT_PROVIDER_SYNTAX,
incompatibleDisallowStructProviderSyntax)
+ .setBool(
+ INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX,
+ incompatiblePackageGroupHasPublicSyntax)
+ .setBool(
+ INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX,
+ incompatibleFixPackageGroupReporootSyntax)
.setBool(INCOMPATIBLE_JAVA_COMMON_PARAMETERS, incompatibleJavaCommonParameters)
.setBool(INCOMPATIBLE_NEW_ACTIONS_API, incompatibleNewActionsApi)
.setBool(INCOMPATIBLE_NO_ATTR_LICENSE, incompatibleNoAttrLicense)
@@ -761,6 +792,10 @@
public static final String INCOMPATIBLE_DISALLOW_EMPTY_GLOB = "-incompatible_disallow_empty_glob";
public static final String INCOMPATIBLE_DISALLOW_STRUCT_PROVIDER_SYNTAX =
"-incompatible_disallow_struct_provider_syntax";
+ public static final String INCOMPATIBLE_PACKAGE_GROUP_HAS_PUBLIC_SYNTAX =
+ "-incompatible_package_group_has_public_syntax";
+ public static final String INCOMPATIBLE_FIX_PACKAGE_GROUP_REPOROOT_SYNTAX =
+ "-incompatible_fix_package_group_reporoot_syntax";
public static final String INCOMPATIBLE_DO_NOT_SPLIT_LINKING_CMDLINE =
"+incompatible_do_not_split_linking_cmdline";
public static final String INCOMPATIBLE_JAVA_COMMON_PARAMETERS =
diff --git a/src/main/java/com/google/devtools/build/lib/query2/common/CommonQueryOptions.java b/src/main/java/com/google/devtools/build/lib/query2/common/CommonQueryOptions.java
index 5bd8261..eb44883 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/common/CommonQueryOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/common/CommonQueryOptions.java
@@ -137,6 +137,17 @@
+ "query: no-op (aspects are always followed).")
public boolean useAspects;
+ @Option(
+ name = "incompatible_package_group_includes_double_slash",
+ defaultValue = "false",
+ documentationCategory = OptionDocumentationCategory.QUERY,
+ effectTags = {OptionEffectTag.TERMINAL_OUTPUT},
+ metadataTags = {OptionMetadataTag.INCOMPATIBLE_CHANGE},
+ help =
+ "If enabled, when outputting package_group's `packages` attribute, the leading `//`"
+ + " will not be omitted.")
+ public boolean incompatiblePackageGroupIncludesDoubleSlash;
+
/** Return the current options as a set of QueryEnvironment settings. */
public Set<Setting> toSettings() {
Set<Setting> settings = EnumSet.noneOf(Setting.class);
diff --git a/src/main/java/com/google/devtools/build/lib/query2/query/output/ProtoOutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/query/output/ProtoOutputFormatter.java
index d3f85d8..3c1eb27 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/query/output/ProtoOutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/query/output/ProtoOutputFormatter.java
@@ -101,6 +101,7 @@
private AspectResolver aspectResolver;
private DependencyFilter dependencyFilter;
+ private boolean packageGroupIncludesDoubleSlash;
private boolean relativeLocations;
private boolean displaySourceFileLocation;
private boolean includeDefaultValues = true;
@@ -126,6 +127,7 @@
super.setOptions(options, aspectResolver, hashFunction);
this.aspectResolver = aspectResolver;
this.dependencyFilter = FormatUtils.getDependencyFilter(options);
+ this.packageGroupIncludesDoubleSlash = options.incompatiblePackageGroupIncludesDoubleSlash;
this.relativeLocations = options.relativeLocations;
this.displaySourceFileLocation = options.displaySourceFileLocation;
this.includeDefaultValues = options.protoIncludeDefaultValues;
@@ -344,9 +346,8 @@
PackageGroup packageGroup = (PackageGroup) target;
Build.PackageGroup.Builder packageGroupPb =
Build.PackageGroup.newBuilder().setName(packageGroup.getLabel().toString());
- // TODO(b/77598306): Migrate to format with leading double slash
for (String containedPackage :
- packageGroup.getContainedPackages(/*includeDoubleSlash=*/ false)) {
+ packageGroup.getContainedPackages(packageGroupIncludesDoubleSlash)) {
packageGroupPb.addContainedPackage(containedPackage);
}
for (Label include : packageGroup.getIncludes()) {
diff --git a/src/main/java/com/google/devtools/build/lib/query2/query/output/XmlOutputFormatter.java b/src/main/java/com/google/devtools/build/lib/query2/query/output/XmlOutputFormatter.java
index bcac1c6..1fff552 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/query/output/XmlOutputFormatter.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/query/output/XmlOutputFormatter.java
@@ -61,6 +61,7 @@
private AspectResolver aspectResolver;
private DependencyFilter dependencyFilter;
+ private boolean packageGroupIncludesDoubleSlash;
private boolean relativeLocations;
private boolean displaySourceFileLocation;
private QueryOptions queryOptions;
@@ -83,6 +84,7 @@
super.setOptions(options, aspectResolver, hashFunction);
this.aspectResolver = aspectResolver;
this.dependencyFilter = FormatUtils.getDependencyFilter(options);
+ this.packageGroupIncludesDoubleSlash = options.incompatiblePackageGroupIncludesDoubleSlash;
this.relativeLocations = options.relativeLocations;
this.displaySourceFileLocation = options.displaySourceFileLocation;
@@ -208,8 +210,7 @@
createValueElement(
doc,
Type.STRING_LIST,
- // TODO(b/77598306): Migrate to format with leading double slash
- packageGroup.getContainedPackages(/*includeDoubleSlash=*/ false));
+ packageGroup.getContainedPackages(packageGroupIncludesDoubleSlash));
packages.setAttribute("name", "packages");
elem.appendChild(packages);
} else if (target instanceof OutputFile) {
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 ed83559..07c9a77 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
@@ -43,7 +43,12 @@
try {
RepositoryName defaultRepoName =
Label.parseAbsoluteUnchecked("//context").getRepository();
- groupQueue.put(PackageSpecification.fromString(defaultRepoName, "//fruits/..."));
+ groupQueue.put(
+ PackageSpecification.fromString(
+ defaultRepoName,
+ "//fruits/...",
+ /*allowPublicPrivate=*/ true,
+ /*repoRootMeansCurrentRepo=*/ true));
} 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 b6baeaa..f91a849 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
@@ -218,7 +218,133 @@
}
@Test
- public void testNegative_everything() throws Exception {
+ public void testEverythingSpecificationWorks() throws Exception {
+ setBuildLanguageOptions("--incompatible_package_group_has_public_syntax=true");
+
+ scratch.file(
+ "fruits/BUILD", //
+ "package_group(",
+ " name = 'mango',",
+ " packages = ['public'],",
+ ")");
+ PackageGroup grp = getPackageGroup("fruits", "mango");
+
+ // Assert that we're using the right package spec.
+ assertThat(grp.getContainedPackages(/*includeDoubleSlash=*/ true)).containsExactly("public");
+ // Assert that this package spec contains packages from both inside and outside the main repo.
+ assertThat(grp.contains(pkgId("pkg"))).isTrue();
+ assertThat(grp.contains(pkgId("somerepo", "pkg"))).isTrue();
+ }
+
+ @Test
+ public void testNothingSpecificationWorks() throws Exception {
+ setBuildLanguageOptions("--incompatible_package_group_has_public_syntax=true");
+
+ scratch.file(
+ "fruits/BUILD", //
+ "package_group(",
+ " name = 'mango',",
+ " packages = ['private'],",
+ ")");
+ PackageGroup grp = getPackageGroup("fruits", "mango");
+
+ // Assert that we're using the right package spec.
+ assertThat(grp.getContainedPackages(/*includeDoubleSlash=*/ true)).containsExactly("private");
+ assertThat(grp.contains(pkgId("anything"))).isFalse();
+ }
+
+ @Test
+ public void testPublicPrivateAreNotAccessibleWithoutFlag() throws Exception {
+ setBuildLanguageOptions("--incompatible_package_group_has_public_syntax=false");
+
+ scratch.file(
+ "foo/BUILD", //
+ "package_group(",
+ " name = 'grp1',",
+ " packages = ['public'],",
+ ")");
+ scratch.file(
+ "bar/BUILD", //
+ "package_group(",
+ " name = 'grp2',",
+ " packages = ['private'],",
+ ")");
+
+ reporter.removeHandler(failFastHandler);
+ getPackageGroup("foo", "grp1");
+ assertContainsEvent(
+ "Use of \"public\" package specification requires enabling"
+ + " --incompatible_package_group_has_public_syntax");
+ getPackageGroup("bar", "grp2");
+ assertContainsEvent(
+ "Use of \"private\" package specification requires enabling"
+ + " --incompatible_package_group_has_public_syntax");
+ }
+
+ @Test
+ public void testRepoRootSubpackagesIsPublic_withoutFlag() throws Exception {
+ setBuildLanguageOptions("--incompatible_fix_package_group_reporoot_syntax=false");
+
+ scratch.file(
+ "fruits/BUILD", //
+ "package_group(",
+ " name = 'mango',",
+ " packages = ['//...'],",
+ ")");
+ PackageGroup grp = getPackageGroup("fruits", "mango");
+
+ // Use includeDoubleSlash=true to make package spec stringification distinguish AllPackages from
+ // AllPackagesBeneath with empty package path.
+ assertThat(grp.getContainedPackages(/*includeDoubleSlash=*/ true))
+ // Assert that "//..." gave us AllPackages.
+ .containsExactly("public");
+ assertThat(grp.contains(pkgId("pkg"))).isTrue();
+ assertThat(grp.contains(pkgId("somerepo", "pkg"))).isTrue();
+ }
+
+ @Test
+ public void testRepoRootSubpackagesIsNotPublic_withFlag() throws Exception {
+ setBuildLanguageOptions(
+ "--incompatible_package_group_has_public_syntax=true",
+ "--incompatible_fix_package_group_reporoot_syntax=true");
+
+ scratch.file(
+ "fruits/BUILD", //
+ "package_group(",
+ " name = 'mango',",
+ " packages = ['//...'],",
+ ")");
+ PackageGroup grp = getPackageGroup("fruits", "mango");
+
+ // Use includeDoubleSlash=true to make package spec stringification distinguish AllPackages from
+ // AllPackagesBeneath with empty package path.
+ assertThat(grp.getContainedPackages(/*includeDoubleSlash=*/ true))
+ // Assert that "//..." gave us AllPackagesBeneath.
+ .containsExactly("//...");
+ assertThat(grp.contains(pkgId("pkg"))).isTrue();
+ assertThat(grp.contains(pkgId("somerepo", "pkg"))).isFalse();
+ }
+
+ @Test
+ public void testCannotUseNewRepoRootSyntaxWithoutPublicSyntax() throws Exception {
+ setBuildLanguageOptions(
+ "--incompatible_package_group_has_public_syntax=false",
+ "--incompatible_fix_package_group_reporoot_syntax=true");
+
+ scratch.file(
+ "fruits/BUILD", //
+ "package_group(",
+ " name = 'mango',",
+ " packages = ['//something'],",
+ ")");
+
+ reporter.removeHandler(failFastHandler);
+ getPackageGroup("fruits", "mango");
+ assertContainsEvent("Cannot use new \"//...\" meaning without allowing new \"public\" syntax.");
+ }
+
+ @Test
+ public void testNegative_repoRootSubpackages() throws Exception {
scratch.file(
"test/BUILD",
"package_group(",
@@ -236,20 +362,35 @@
}
@Test
- public void testEverythingSpecificationWorks() throws Exception {
- scratch.file(
- "fruits/BUILD", //
- "package_group(",
- " name = 'mango',",
- " packages = ['//...'],",
- ")");
- PackageGroup grp = getPackageGroup("fruits", "mango");
+ public void testNegative_public() throws Exception {
+ setBuildLanguageOptions("--incompatible_package_group_has_public_syntax=true");
- // Assert that we're actually using the "everything" package specification, and assert that this
- // means we include packages from the main repo but also from other repos.
- assertThat(grp.getContainedPackages(/*includeDoubleSlash=*/ false)).containsExactly("//...");
- assertThat(grp.contains(pkgId("pkg"))).isTrue();
- assertThat(grp.contains(pkgId("somerepo", "pkg"))).isTrue();
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(",
+ " name = 'apple',",
+ " packages = ['-public'],",
+ ")");
+
+ reporter.removeHandler(failFastHandler);
+ getPackageGroup("fruits", "apple");
+ assertContainsEvent("Cannot negate \"public\" package specification");
+ }
+
+ @Test
+ public void testNegative_private() throws Exception {
+ setBuildLanguageOptions("--incompatible_package_group_has_public_syntax=true");
+
+ scratch.file(
+ "fruits/BUILD",
+ "package_group(",
+ " name = 'apple',",
+ " packages = ['-private'],",
+ ")");
+
+ reporter.removeHandler(failFastHandler);
+ getPackageGroup("fruits", "apple");
+ assertContainsEvent("Cannot negate \"private\" package specification");
}
@Test
@@ -275,16 +416,18 @@
PackageGroupContents contents =
PackageGroupContents.create(
ImmutableList.of(
- PackageSpecification.fromString(main, "//a"),
- PackageSpecification.fromString(main, "//a/b/..."),
- PackageSpecification.fromString(main, "-//c"),
- PackageSpecification.fromString(main, "-//c/d/..."),
- PackageSpecification.fromString(main, "//..."),
- PackageSpecification.fromString(main, "-//..."),
- PackageSpecification.fromString(main, "//"),
- PackageSpecification.fromString(main, "-//"),
- PackageSpecification.fromString(other, "//z"),
- PackageSpecification.fromString(other, "//...")));
+ pkgSpec(main, "//a"),
+ pkgSpec(main, "//a/b/..."),
+ pkgSpec(main, "-//c"),
+ pkgSpec(main, "-//c/d/..."),
+ pkgSpec(main, "//..."),
+ pkgSpec(main, "-//..."),
+ pkgSpec(main, "//"),
+ pkgSpec(main, "-//"),
+ pkgSpec(other, "//z"),
+ pkgSpec(other, "//..."),
+ pkgSpec(main, "public"),
+ pkgSpec(main, "private")));
assertThat(
contents.streamPackageStrings(/*includeDoubleSlash=*/ false).collect(toImmutableList()))
.containsExactly(
@@ -297,8 +440,9 @@
"",
"-",
"@other//z",
- // TODO(#16323): When parsing is fixed, change this "//..." to "@other//...".
- "//...");
+ "@other//...",
+ "//...", // legacy syntax for public
+ "private");
assertThat(
contents.streamPackageStrings(/*includeDoubleSlash=*/ true).collect(toImmutableList()))
.containsExactly(
@@ -311,8 +455,9 @@
"//",
"-//",
"@other//z",
- // TODO(#16323): When parsing is fixed, change this "//..." to "@other//...".
- "//...");
+ "@other//...",
+ "public",
+ "private");
assertThat(contents.streamPackageStringsWithoutRepository().collect(toImmutableList()))
.containsExactly(
"//a",
@@ -324,7 +469,15 @@
"//",
"-//",
"//z",
- "//...");
+ "//...",
+ "public",
+ "private");
+ }
+
+ /** Convenience method for obtaining a PackageSpecification. */
+ private PackageSpecification pkgSpec(RepositoryName repository, String spec) throws Exception {
+ return PackageSpecification.fromString(
+ repository, spec, /*allowPublicPrivate=*/ true, /*repoRootMeansCurrentRepo=*/ true);
}
/** Convenience method for obtaining a PackageIdentifier. */
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java
index f0ac79e..c6c712a 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java
@@ -601,6 +601,54 @@
}
@Test
+ public void testBzlVisibility_publicListElement() throws Exception {
+ setBuildLanguageOptions(
+ "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone");
+
+ scratch.file("a/BUILD");
+ scratch.file(
+ "a/foo.bzl", //
+ "load(\"//b:bar.bzl\", \"x\")");
+ scratch.file("b/BUILD");
+ scratch.file(
+ "b/bar.bzl", //
+ // Tests "public" as a list item, and alongside other list items.
+ "visibility([\"public\", \"//c\"])",
+ "x = 1");
+
+ checkSuccessfulLookup("//a:foo.bzl");
+ assertNoEvents();
+ }
+
+ @Test
+ public void testBzlVisibility_privateListElement() throws Exception {
+ setBuildLanguageOptions(
+ "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone");
+
+ scratch.file("a1/BUILD");
+ scratch.file(
+ "a1/foo.bzl", //
+ "load(\"//b:bar.bzl\", \"x\")");
+ scratch.file("a2/BUILD");
+ scratch.file(
+ "a2/foo.bzl", //
+ "load(\"//b:bar.bzl\", \"x\")");
+ scratch.file("b/BUILD");
+ scratch.file(
+ "b/bar.bzl", //
+ // Tests "private" as a list item, and alongside other list items.
+ "visibility([\"private\", \"//a1\"])",
+ "x = 1");
+
+ checkSuccessfulLookup("//a1:foo.bzl");
+ assertNoEvents();
+ reporter.removeHandler(failFastHandler);
+ checkFailingLookup(
+ "//a2:foo.bzl", "module //a2:foo.bzl contains .bzl load-visibility violations");
+ assertContainsEvent("Starlark file //b:bar.bzl is not visible for loading from package //a2.");
+ }
+
+ @Test
public void testBzlVisibility_failureInDependency() throws Exception {
setBuildLanguageOptions(
"--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone");
@@ -743,6 +791,65 @@
"Starlark file @repo//lib:bar.bzl is not visible for loading from package //pkg.");
}
+ // TODO(#16365): This test case can be deleted once --incompatible_package_group_has_public_syntax
+ // is deleted (not just flipped).
+ @Test
+ public void testBzlVisibility_canUsePublicPrivate_regardlessOfFlag() throws Exception {
+ setBuildLanguageOptions(
+ "--experimental_bzl_visibility=true",
+ "--experimental_bzl_visibility_allowlist=everyone",
+ // Test that we can use "public" and "private" visibility for .bzl files even when the
+ // incompatible flag is disabled.
+ "--incompatible_package_group_has_public_syntax=false");
+
+ scratch.file("a/BUILD");
+ scratch.file(
+ "a/foo1.bzl", //
+ "visibility(\"public\")");
+ scratch.file(
+ "a/foo2.bzl", //
+ "visibility(\"private\")");
+
+ checkSuccessfulLookup("//a:foo1.bzl");
+ checkSuccessfulLookup("//a:foo2.bzl");
+ assertNoEvents();
+ }
+
+ // TODO(#16324): Once --incompatible_fix_package_group_reporoot_syntax is deleted (not just
+ // flipped), this test case will be redundant with tests for //... in PackageGroupTest. At that
+ // point we'll just delete this test case.
+ @Test
+ public void testBzlVisibility_repoRootSubpackagesIsNotPublic_regardlessOfFlag() throws Exception {
+ setBuildLanguageOptions(
+ "--experimental_bzl_visibility=true",
+ "--experimental_bzl_visibility_allowlist=everyone",
+ // Test that we get the fixed behavior even when the incompatible flag is disabled.
+ "--incompatible_fix_package_group_reporoot_syntax=false");
+
+ scratch.overwriteFile(
+ "WORKSPACE", //
+ "local_repository(",
+ " name = 'repo',",
+ " path = 'repo'",
+ ")");
+ scratch.file("repo/WORKSPACE");
+ scratch.file("repo/a/BUILD");
+ scratch.file(
+ "repo/a/foo.bzl", //
+ "load(\"@//b:bar.bzl\", \"x\")");
+ scratch.file("b/BUILD");
+ scratch.file(
+ "b/bar.bzl", //
+ "visibility([\"//...\"])",
+ "x = 1");
+
+ reporter.removeHandler(failFastHandler);
+ checkFailingLookup(
+ "@repo//a:foo.bzl", "module @repo//a:foo.bzl contains .bzl load-visibility violations");
+ assertContainsEvent(
+ "Starlark file //b:bar.bzl is not visible for loading from package @repo//a.");
+ }
+
@Test
public void testBzlVisibility_disallowsSubpackagesWithoutWildcard() throws Exception {
setBuildLanguageOptions(