diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/BazelBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/BazelBuildApiGlobals.java index 645b423f02b254..41af6c00a1662c 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/BazelBuildApiGlobals.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/BazelBuildApiGlobals.java @@ -14,14 +14,13 @@ package com.google.devtools.build.lib.analysis.starlark; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; 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.packages.BazelStarlarkContext; import com.google.devtools.build.lib.packages.BzlInitThreadContext; import com.google.devtools.build.lib.packages.BzlVisibility; +import com.google.devtools.build.lib.packages.PackageSpecification; import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; import com.google.devtools.build.lib.starlarkbuildapi.StarlarkBuildApiGlobals; import java.util.List; @@ -68,33 +67,21 @@ public void visibility(Object value, StarlarkThread thread) throws EvalException } // `visibility(["//pkg1", "//pkg2", ...])` } else if (value instanceof StarlarkList) { - List packageStrings = Sequence.cast(value, String.class, "visibility list"); - ImmutableList.Builder packages = - ImmutableList.builderWithExpectedSize(packageStrings.size()); - for (String packageString : packageStrings) { - PackageIdentifier packageId; - // Disallow "@foo//pkg", or even "@//pkg" or "@the_current_repo//pkg". - if (packageString.startsWith("@")) { - throw Starlark.errorf("package specifiers cannot begin with '@'"); - } - try { - packageId = PackageIdentifier.parse(packageString); - } catch (LabelSyntaxException ex) { - throw Starlark.errorf("Invalid package: %s", ex.getMessage()); - } - // PackageIdentifier.parse() on a string without a repo qualifier returns an identifier in - // the main repo. Substitute it with our own repo. - Preconditions.checkState(packageId.getRepository().equals(RepositoryName.MAIN)); - packages.add( - PackageIdentifier.create( - context.getBzlFile().getRepository(), packageId.getPackageFragment())); + List specStrings = Sequence.cast(value, String.class, "visibility list"); + ImmutableList.Builder specs = + ImmutableList.builderWithExpectedSize(specStrings.size()); + for (String specString : specStrings) { + PackageSpecification spec = + PackageSpecification.fromStringForBzlVisibility( + context.getBzlFile().getRepository(), specString); + specs.add(spec); } - bzlVisibility = new BzlVisibility.PackageListBzlVisibility(packages.build()); + bzlVisibility = new BzlVisibility.PackageListBzlVisibility(specs.build()); } if (bzlVisibility == null) { throw Starlark.errorf( - "Invalid bzl-visibility: got '%s', want \"public\", \"private\", or list of package path" - + " strings", + "Invalid bzl-visibility: got '%s', want \"public\", \"private\", or list of package" + + " specification strings", Starlark.type(value)); } context.setBzlVisibility(bzlVisibility); @@ -109,21 +96,36 @@ private void checkVisibilityAllowlist(PackageIdentifier pkgId, List allo // small, and calls to visibility() are relatively infrequent. boolean foundMatch = false; for (String allowedPkgString : allowlist) { - // TODO(b/22193153): This seems incorrect since parse doesn't take into account any repository - // map. (This shouldn't matter within Google's monorepo, which doesn't use a repo map.) - try { - // Special constant to disable allowlisting. For migration to enable the feature globally. - if (allowedPkgString.equals("everyone")) { - foundMatch = true; - break; - } - PackageIdentifier allowedPkgId = PackageIdentifier.parse(allowedPkgString); - if (pkgId.equals(allowedPkgId)) { - foundMatch = true; - break; + // Special constant to disable allowlisting. For migration to enable the feature globally. + if (allowedPkgString.equals("everyone")) { + foundMatch = true; + break; + } + // The wildcard syntax /... is not valid for PackageIdentifiers, so we extract it first. + boolean allBeneath = allowedPkgString.endsWith("/..."); + if (allBeneath) { + allowedPkgString = allowedPkgString.substring(0, allowedPkgString.length() - 4); + if (allowedPkgString.equals("/")) { + // was "//..." + allowedPkgString = "//"; } + } + PackageIdentifier allowedPkgId; + try { + // TODO(b/22193153): This seems incorrect since parse doesn't take into account any + // repository map. (This shouldn't matter within Google's monorepo, which doesn't use a repo + // map.) + allowedPkgId = PackageIdentifier.parse(allowedPkgString); } catch (LabelSyntaxException ex) { - throw new EvalException("Invalid bzl-visibility allowlist", ex); + throw Starlark.errorf("Invalid bzl-visibility allowlist: %s", ex.getMessage()); + } + + if (pkgId.equals(allowedPkgId) + || (allBeneath + // Again, we're erroneously ignoring repo. + && pkgId.getPackageFragment().startsWith(allowedPkgId.getPackageFragment()))) { + foundMatch = true; + break; } } if (!foundMatch) { diff --git a/src/main/java/com/google/devtools/build/lib/packages/BzlVisibility.java b/src/main/java/com/google/devtools/build/lib/packages/BzlVisibility.java index c2df2314ae6c57..a4746a5dafc534 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/BzlVisibility.java +++ b/src/main/java/com/google/devtools/build/lib/packages/BzlVisibility.java @@ -52,19 +52,28 @@ public boolean allowsPackage(PackageIdentifier pkg) { }; /** - * A visibility that enumerates the packages whose BUILD and .bzl files may load the .bzl. - * Subpackages are not implicitly included. + * A visibility that decides whether a package's BUILD and .bzl files may load the .bzl by + * checking the package against a set of package specifications. + * + *

Package specifications may have the form {@code //foo/bar} or {@code //foo/bar/...}. Negated + * package specifications are not yet supported. */ + // TODO(b/22193153): Negation. public static class PackageListBzlVisibility extends BzlVisibility { - private final ImmutableList packages; + private final ImmutableList specs; - public PackageListBzlVisibility(List packages) { - this.packages = ImmutableList.copyOf(packages); + public PackageListBzlVisibility(List specs) { + this.specs = ImmutableList.copyOf(specs); } @Override public boolean allowsPackage(PackageIdentifier pkg) { - return packages.contains(pkg); + for (PackageSpecification spec : specs) { + if (spec.containsPackage(pkg)) { + return true; + } + } + return false; } } } 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 a0d0cb86a64311..0e8aefe677d9e9 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 @@ -27,6 +27,8 @@ import java.util.LinkedHashMap; import java.util.stream.Stream; import javax.annotation.Nullable; +import net.starlark.java.eval.EvalException; +import net.starlark.java.eval.Starlark; /** * Represents one of the following: @@ -130,6 +132,27 @@ private static PackageSpecification fromStringPositive(RepositoryName repository : new SinglePackage(packageIdForSpecifiedRepository); } + /** + * Parses a string to a {@code PackageSpecification} for use with .bzl visibility. + * + *

This rejects negative package patterns, and translates the exception type into {@code + * EvalException}. + */ + // TODO(b/22193153): Support negatives too. + public static PackageSpecification fromStringForBzlVisibility( + RepositoryName repositoryName, String spec) throws EvalException { + PackageSpecification result; + try { + result = fromString(repositoryName, spec); + } catch (InvalidPackageSpecificationException e) { + throw new EvalException(e.getMessage()); + } + if (result instanceof NegativePackageSpecification) { + throw Starlark.errorf("Cannot use negative package patterns here"); + } + return result; + } + /** * Parses the provided {@link Label} into a {@link PackageSpecification} specific to the {@link * RepositoryName} associated with the label. 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 2bdc4c7254c8a2..12fe14c0ac650a 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 @@ -156,9 +156,10 @@ public final class BuildLanguageOptions extends OptionsBase { help = "A comma-separated list of packages (sans \"//\") which, if --experimental_bzl_visibility" + " is enabled, are permitted to contain .bzl files that set a bzl-visibility by" - + " calling the `visibility()` function. (Known issue: This flag may not work" - + " correctly in the presence of repository remapping, which is used by bzlmod.)" - + " If the list includes the special item \"everyone\", all packages are permitted.") + + " calling the `visibility()` function. Subpackages may also be included by" + + " appending `/...`. (Known issue: This flag may not work correctly in the presence" + + " of repository remapping, which is used by bzlmod.) If the list includes the" + + " special item \"everyone\", all packages are permitted.") public List experimentalBzlVisibilityAllowlist; @Option( diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkBuildApiGlobals.java index dd3404ec534cdf..2e2a1d38e98112 100644 --- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkBuildApiGlobals.java +++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/StarlarkBuildApiGlobals.java @@ -40,10 +40,12 @@ public interface StarlarkBuildApiGlobals { + "

  • \"public\" (default): the .bzl can be loaded anywhere." + "
  • \"private\": the .bzl can only be loaded by files in the same" + " package (subpackages are excluded)." - + "
  • a list of package paths (e.g. [\"//pkg1\", \"//pkg2/subpkg\"," - + " ...]): the .bzl can be loaded by files in any of the listed packages. Only" - + " packages in the current repository may be specified; the repository \"@\" syntax" - + " is disallowed. Subpackage globs (\"//pkg/...\") are not supported." + + "
  • a list of package specifications (e.g. [\"//pkg1\"," + + "\"//pkg2/subpkg/...\"]): the .bzl can be loaded by files in any package" + + " matching one of the listed specifications. Package specifications may be package" + + " paths, or package paths with a trailing \"/...\" to include all" + + " subpackages; negated patterns are not currently supported. All package" + + " specifications are within the current repository; the \"@\" syntax is disallowed." + "" + "

    Generally, visibility() is called at the top of the .bzl file," + " immediately after its load() statements. (It is poor style to put" diff --git a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java index 2ed91de20dbcaa..5e843acc4a262e 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/OsPathPolicy.java @@ -64,14 +64,14 @@ public interface OsPathPolicy { /** * Returns whether the passed string starts with the given prefix, given the OS case sensitivity. * - *

    This is a pure string operation and doesn't need to worry about matching path segments. + *

    This is a pure string operation and doesn't account for path separators. */ boolean startsWith(String path, String prefix); /** - * Returns whether the passed string ends with the given prefix, given the OS case sensitivity. + * Returns whether the passed string ends with the given suffix, given the OS case sensitivity. * - *

    This is a pure string operation and doesn't need to worry about matching path segments. + *

    This is a pure string operation and doesn't account for path separators. */ boolean endsWith(String path, String suffix); diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java index 2c5a25f31582c0..4fa15c60e24007 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java @@ -338,7 +338,7 @@ public PathFragment relativeTo(String base) { } /** - * Returns whether this path is an ancestor of another path. + * Returns true iff {@code other} is an ancestor of this path. * *

    If this == other, true is returned. * @@ -361,8 +361,8 @@ public boolean startsWith(PathFragment other) { } /** - * Returns true iff {@code suffix}, considered as a list of path segments, is relative and a - * suffix of {@code this}, or both are absolute and equal. + * Returns true iff {@code other}, considered as a list of path segments, is relative and a suffix + * of {@code this}, or both are absolute and equal. * *

    This is a reflexive, transitive, anti-symmetric relation (i.e. a partial order) */ 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 4eea2f0021cecf..14bbfeb045dc65 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 @@ -415,7 +415,7 @@ public void testBzlVisibility_disabledWithoutAllowlist() throws Exception { scratch.file("b/BUILD"); scratch.file( "b/bar.bzl", // - "visibility(\"private\")", + "visibility(\"public\")", "x = 1"); reporter.removeHandler(failFastHandler); @@ -423,6 +423,66 @@ public void testBzlVisibility_disabledWithoutAllowlist() throws Exception { assertContainsEvent("`visibility() is not enabled for package //b"); } + @Test + public void testBzlVisibility_allowlistAcceptsLeadingDoubleSlash() throws Exception { + setBuildLanguageOptions( + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=//b"); + + scratch.file("a/BUILD"); + scratch.file( + "a/foo.bzl", // + "load(\"//b:bar.bzl\", \"x\")"); + scratch.file("b/BUILD"); + scratch.file( + "b/bar.bzl", // + "visibility(\"public\")", + "x = 1"); + + checkSuccessfulLookup("//a:foo.bzl"); + assertNoEvents(); + } + + @Test + public void testBzlVisibility_allowlistExcludesSubpackagesWithoutWildcard() throws Exception { + setBuildLanguageOptions( + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + + scratch.file("a/BUILD"); + scratch.file( + "a/foo.bzl", // + "load(\"//b/subpkg:bar.bzl\", \"x\")"); + scratch.file("b/BUILD"); + scratch.file("b/subpkg/BUILD"); + scratch.file( + "b/subpkg/bar.bzl", // + "visibility(\"public\")", + "x = 1"); + + reporter.removeHandler(failFastHandler); + checkFailingLookup("//a:foo.bzl", "initialization of module 'b/subpkg/bar.bzl' failed"); + assertContainsEvent("`visibility() is not enabled for package //b/subpkg"); + } + + @Test + public void testBzlVisibility_allowlistIncludesSubpackagesWithWildcard() throws Exception { + setBuildLanguageOptions( + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b/..."); + + scratch.file("a/BUILD"); + scratch.file( + "a/foo.bzl", // + "load(\"//b/subpkg:bar.bzl\", \"x\")"); + scratch.file("b/BUILD"); + scratch.file("b/subpkg/BUILD"); + scratch.file( + "b/subpkg/bar.bzl", // + "visibility(\"public\")", + "x = 1"); + + checkSuccessfulLookup("//a:foo.bzl"); + assertNoEvents(); + } + @Test public void testBzlVisibility_enabledWhenAllowlistDisabled() throws Exception { setBuildLanguageOptions( @@ -465,7 +525,7 @@ public void testBzlVisibility_malformedAllowlist() throws Exception { @Test public void testBzlVisibility_publicExplicit() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -484,7 +544,7 @@ public void testBzlVisibility_publicExplicit() throws Exception { @Test public void testBzlVisibility_publicImplicit() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -503,7 +563,7 @@ public void testBzlVisibility_publicImplicit() throws Exception { @Test public void testBzlVisibility_privateSamePackage() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=a"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -511,7 +571,7 @@ public void testBzlVisibility_privateSamePackage() throws Exception { "load(\"//a:bar.bzl\", \"x\")"); scratch.file( "a/bar.bzl", // - "visibility(\"public\")", + "visibility(\"private\")", "x = 1"); checkSuccessfulLookup("//a:foo.bzl"); @@ -521,7 +581,7 @@ public void testBzlVisibility_privateSamePackage() throws Exception { @Test public void testBzlVisibility_privateDifferentPackage() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -542,9 +602,7 @@ public void testBzlVisibility_privateDifferentPackage() throws Exception { @Test public void testBzlVisibility_failureInDependency() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", - // While we're here, test that we can write either "pkg" or "//pkg" in the allowlist. - "--experimental_bzl_visibility_allowlist=b,//c"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -572,7 +630,7 @@ public void testBzlVisibility_failureInDependency() throws Exception { @Test public void testBzlVisibility_setNonlocally() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); // Checks a case where visibility() is called in a different package than the module that is // actually being initialized. (This is bad style in practice, but it's semantically simpler to @@ -605,7 +663,7 @@ public void testBzlVisibility_setNonlocally() throws Exception { @Test public void testBzlVisibility_cannotBeSetTwice() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=a"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -621,7 +679,7 @@ public void testBzlVisibility_cannotBeSetTwice() throws Exception { @Test public void testBzlVisibility_enumeratedPackages() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=b"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a1/BUILD"); scratch.file( @@ -649,7 +707,7 @@ public void testBzlVisibility_enumeratedPackages() throws Exception { @Test public void testBzlVisibility_enumeratedPackagesMultipleRepos() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=@repo//lib"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); // @repo//pkg:foo1.bzl and @//pkg:foo2.bzl both try to access @repo//lib:bar.bzl. Test that when // bar.bzl declares a visibility allowing "//pkg", it means @repo//pkg and *not* @//pkg. @@ -684,10 +742,66 @@ public void testBzlVisibility_enumeratedPackagesMultipleRepos() throws Exception "Starlark file @repo//lib:bar.bzl is not visible for loading from package //pkg."); } + @Test + public void testBzlVisibility_disallowsSubpackagesWithoutWildcard() throws Exception { + setBuildLanguageOptions( + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); + + scratch.file("a/BUILD"); + scratch.file( + "a/foo1.bzl", // + "load(\"//b:bar.bzl\", \"x\")"); + scratch.file("a/subpkg/BUILD"); + scratch.file( + "a/subpkg/foo2.bzl", // + "load(\"//b:bar.bzl\", \"x\")"); + scratch.file("b/BUILD"); + scratch.file( + "b/bar.bzl", // + "visibility([\"//a\"])", + "x = 1"); + + checkSuccessfulLookup("//a:foo1.bzl"); + assertNoEvents(); + + reporter.removeHandler(failFastHandler); + checkFailingLookup( + "//a/subpkg:foo2.bzl", + "module //a/subpkg:foo2.bzl contains .bzl load-visibility violations"); + assertContainsEvent( + "Starlark file //b:bar.bzl is not visible for loading from package //a/subpkg."); + } + + @Test + public void testBzlVisibility_allowsSubpackagesWithWildcard() throws Exception { + setBuildLanguageOptions( + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); + + scratch.file("a/BUILD"); + scratch.file( + "a/foo1.bzl", // + "load(\"//b:bar.bzl\", \"x\")"); + scratch.file("a/subpkg/BUILD"); + scratch.file( + "a/subpkg/foo2.bzl", // + "load(\"//b:bar.bzl\", \"x\")"); + scratch.file("b/BUILD"); + scratch.file( + "b/bar.bzl", // + "visibility([\"//a/...\"])", + "x = 1"); + + checkSuccessfulLookup("//a:foo1.bzl"); + assertNoEvents(); + + checkSuccessfulLookup("//a/subpkg:foo2.bzl"); + assertNoEvents(); + } + @Test public void testBzlVisibility_invalid_badType() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=a"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -697,14 +811,14 @@ public void testBzlVisibility_invalid_badType() throws Exception { reporter.removeHandler(failFastHandler); checkFailingLookup("//a:foo.bzl", "initialization of module 'a/foo.bzl' failed"); assertContainsEvent( - "Invalid bzl-visibility: got 'int', want \"public\", \"private\", or list of package path" - + " strings"); + "Invalid bzl-visibility: got 'int', want \"public\", \"private\", or list of package" + + " specification strings"); } @Test public void testBzlVisibility_invalid_badElementType() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=a"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -719,7 +833,7 @@ public void testBzlVisibility_invalid_badElementType() throws Exception { @Test public void testBzlVisibility_invalid_packageOutsideRepo() throws Exception { setBuildLanguageOptions( - "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=a"); + "--experimental_bzl_visibility=true", "--experimental_bzl_visibility_allowlist=everyone"); scratch.file("a/BUILD"); scratch.file( @@ -728,7 +842,7 @@ public void testBzlVisibility_invalid_packageOutsideRepo() throws Exception { reporter.removeHandler(failFastHandler); checkFailingLookup("//a:foo.bzl", "initialization of module 'a/foo.bzl' failed"); - assertContainsEvent("package specifiers cannot begin with '@'"); + assertContainsEvent("invalid package name '@repo//b': must start with '//'"); } @Test