Error-out if the label in a 'load' statement crosses a subpackage boundary. This behavior is guarded by a flag (default value is "don't error out"), and will eventually become the only behavior.
This implements the feature in #6408.
RELNOTES: None
PiperOrigin-RevId: 217402217
diff --git a/src/test/java/com/google/devtools/build/lib/cmdline/LabelTest.java b/src/test/java/com/google/devtools/build/lib/cmdline/LabelTest.java
index fdfb30bd..6ea333f 100644
--- a/src/test/java/com/google/devtools/build/lib/cmdline/LabelTest.java
+++ b/src/test/java/com/google/devtools/build/lib/cmdline/LabelTest.java
@@ -469,4 +469,16 @@
label = Label.parseAbsolute("@repo//bar/baz", ImmutableMap.of());
assertThat(label.getWorkspaceRoot()).isEqualTo("external/repo");
}
+
+ @Test
+ public void testGetContainingDirectory() {
+ assertThat(Label.getContainingDirectory(Label.parseAbsoluteUnchecked("//a:b")))
+ .isEqualTo(PathFragment.create("a"));
+ assertThat(Label.getContainingDirectory(Label.parseAbsoluteUnchecked("//a/b:c")))
+ .isEqualTo(PathFragment.create("a/b"));
+ assertThat(Label.getContainingDirectory(Label.parseAbsoluteUnchecked("//a:b/c")))
+ .isEqualTo(PathFragment.create("a/b"));
+ assertThat(Label.getContainingDirectory(Label.parseAbsoluteUnchecked("//a/b/c")))
+ .isEqualTo(PathFragment.create("a/b/c"));
+ }
}
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index 8246bf7..661ab40 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -137,6 +137,7 @@
"--incompatible_disallow_dict_plus=" + rand.nextBoolean(),
"--incompatible_disallow_filetype=" + rand.nextBoolean(),
"--incompatible_disallow_legacy_javainfo=" + rand.nextBoolean(),
+ "--incompatible_disallow_load_labels_to_cross_package_boundaries=" + rand.nextBoolean(),
"--incompatible_disallow_old_style_args_add=" + rand.nextBoolean(),
"--incompatible_disallow_slash_operator=" + rand.nextBoolean(),
"--incompatible_expand_directories=" + rand.nextBoolean(),
@@ -180,6 +181,7 @@
.incompatibleDisallowDictPlus(rand.nextBoolean())
.incompatibleDisallowFileType(rand.nextBoolean())
.incompatibleDisallowLegacyJavaInfo(rand.nextBoolean())
+ .incompatibleDisallowLoadLabelsToCrossPackageBoundaries(rand.nextBoolean())
.incompatibleDisallowOldStyleArgsAdd(rand.nextBoolean())
.incompatibleDisallowSlashOperator(rand.nextBoolean())
.incompatibleExpandDirectories(rand.nextBoolean())
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
index dc61c61..93cf9e8 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.skyframe;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
@@ -25,6 +26,7 @@
import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException;
import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.Path;
@@ -262,4 +264,160 @@
assertThat(result.hasError()).isFalse();
}
+
+ @Test
+ public void testLoadUsingLabelThatDoesntCrossesBoundaryOfPackage()
+ throws Exception {
+ scratch.file("a/BUILD");
+ scratch.file("a/a.bzl", "load('//a:b/b.bzl', 'b')");
+ scratch.file("a/b/b.bzl", "b = 42");
+
+ checkSuccessfulLookup("//a:a.bzl");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Allow_OfSamePkg()
+ throws Exception {
+ scratch.file("a/BUILD");
+ scratch.file("a/a.bzl", "load('//a:b/b.bzl', 'b')");
+ scratch.file("a/b/BUILD", "");
+ scratch.file("a/b/b.bzl", "b = 42");
+
+ checkSuccessfulLookup("//a:a.bzl");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Disallow_OfSamePkg()
+ throws Exception {
+ setSkylarkSemanticsOptions("--incompatible_disallow_load_labels_to_cross_package_boundaries");
+
+ scratch.file("a/BUILD");
+ scratch.file("a/a.bzl", "load('//a:b/b.bzl', 'b')");
+ scratch.file("a/b/BUILD", "");
+ scratch.file("a/b/b.bzl", "b = 42");
+
+ SkyKey skylarkImportLookupKey =
+ SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//a:a.bzl"), false);
+ EvaluationResult<SkylarkImportLookupValue> result =
+ SkyframeExecutorTestUtils.evaluate(
+ getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
+ assertThat(result.hasError()).isTrue();
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .isInstanceOf(SkylarkImportFailedException.class);
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .hasMessageThat()
+ .contains(
+ "Label '//a:b/b.bzl' crosses boundary of subpackage 'a/b' (perhaps you meant to put "
+ + "the colon here: '//a/b:b.bzl'?)");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Allow_OfDifferentPkgUnder()
+ throws Exception {
+ scratch.file("a/BUILD");
+ scratch.file("a/a.bzl", "load('//a/b:c/c.bzl', 'c')");
+ scratch.file("a/b/BUILD", "");
+ scratch.file("a/b/c/BUILD", "");
+ scratch.file("a/b/c/c.bzl", "c = 42");
+
+ checkSuccessfulLookup("//a:a.bzl");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Disallow_OfDifferentPkgUnder()
+ throws Exception {
+ setSkylarkSemanticsOptions("--incompatible_disallow_load_labels_to_cross_package_boundaries");
+
+ scratch.file("a/BUILD");
+ scratch.file("a/a.bzl", "load('//a/b:c/c.bzl', 'c')");
+ scratch.file("a/b/BUILD", "");
+ scratch.file("a/b/c/BUILD", "");
+ scratch.file("a/b/c/c.bzl", "c = 42");
+
+ SkyKey skylarkImportLookupKey =
+ SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//a:a.bzl"), false);
+ EvaluationResult<SkylarkImportLookupValue> result =
+ SkyframeExecutorTestUtils.evaluate(
+ getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
+ assertThat(result.hasError()).isTrue();
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .isInstanceOf(SkylarkImportFailedException.class);
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .hasMessageThat()
+ .contains(
+ "Label '//a/b:c/c.bzl' crosses boundary of subpackage 'a/b/c' (perhaps you meant to "
+ + "put the colon here: '//a/b/c:c.bzl'?)");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Allow_OfDifferentPkgAbove()
+ throws Exception {
+ scratch.file("a/b/BUILD");
+ scratch.file("a/b/b.bzl", "load('//a/c:c/c.bzl', 'c')");
+ scratch.file("a/BUILD");
+ scratch.file("a/c/c/c.bzl", "c = 42");
+
+ // With the default of
+ // --incompatible_disallow_load_labels_to_cross_subpackage_boundaries=false,
+ // SkylarkImportLookupValue(//a/b:b.bzl) has an error because ASTFileLookupValue(//a/c:c/c.bzl)
+ // because package //a/c doesn't exist. The behavior with
+ // --incompatible_disallow_load_labels_to_cross_subpackage_boundaries=true is stricter, but we
+ // still have an explicit test for this case so that way we don't forget to think about it when
+ // we address the TODO in ASTFuleLookupFunction.
+
+ SkyKey skylarkImportLookupKey =
+ SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//a/b:b.bzl"), false);
+ EvaluationResult<SkylarkImportLookupValue> result =
+ SkyframeExecutorTestUtils.evaluate(
+ getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
+ assertThat(result.hasError()).isTrue();
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .isInstanceOf(SkylarkImportFailedException.class);
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .hasMessageThat()
+ .contains(
+ "Extension file not found. Unable to load package for '//a/c:c/c.bzl': BUILD file not "
+ + "found on package path");
+ }
+
+ @Test
+ public void testLoadUsingLabelThatCrossesBoundaryOfPackage_Disallow_OfDifferentPkgAbove()
+ throws Exception {
+ setSkylarkSemanticsOptions("--incompatible_disallow_load_labels_to_cross_package_boundaries");
+
+ scratch.file("a/b/BUILD");
+ scratch.file("a/b/b.bzl", "load('//a/c:c/c.bzl', 'c')");
+ scratch.file("a/BUILD");
+ scratch.file("a/c/c/c.bzl", "c = 42");
+
+ SkyKey skylarkImportLookupKey =
+ SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//a/b:b.bzl"), false);
+ EvaluationResult<SkylarkImportLookupValue> result =
+ SkyframeExecutorTestUtils.evaluate(
+ getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
+ assertThat(result.hasError()).isTrue();
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .isInstanceOf(SkylarkImportFailedException.class);
+ assertThatEvaluationResult(result)
+ .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+ .hasExceptionThat()
+ .hasMessageThat()
+ .contains(
+ "Label '//a/c:c/c.bzl' crosses boundary of package 'a' (perhaps you meant to put the "
+ + "colon here: '//a:c/c/c.bzl'?)");
+ }
}