Fix cycle detection between .bzl files.
The code assumed that the last element before a cycle was a BUILD file. It can
also be a .bzl file.
--
PiperOrigin-RevId: 143673940
MOS_MIGRATED_REVID=143673940
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
index d66bd57..849fe1e 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.skyframe.CycleInfo;
@@ -63,23 +64,32 @@
if (alreadyReported) {
return true;
} else if (Iterables.all(cycle, IS_SKYLARK_MODULE_SKY_KEY)
- // The last element of the path to the cycle has to be a PackageFunction.
- && IS_PACKAGE_SKY_KEY.apply(lastPathElement)) {
- StringBuilder cycleMessage =
- new StringBuilder()
- .append(lastPathElement.argument()).append("/BUILD: ")
- .append("cycle in referenced extension files: ");
+ // The last element before the cycle has to be a PackageFunction or SkylarkModule.
+ && (IS_PACKAGE_SKY_KEY.apply(lastPathElement)
+ || IS_SKYLARK_MODULE_SKY_KEY.apply(lastPathElement))) {
- AbstractLabelCycleReporter.printCycle(
- cycleInfo.getCycle(),
- cycleMessage,
+ Function printer =
new Function<SkyKey, String>() {
@Override
public String apply(SkyKey input) {
- return ((SkylarkImportLookupValue.SkylarkImportLookupKey) input.argument())
- .importLabel.toString();
+ if (input.argument() instanceof SkylarkImportLookupValue.SkylarkImportLookupKey) {
+ return ((SkylarkImportLookupValue.SkylarkImportLookupKey) input.argument())
+ .importLabel.toString();
+ } else if (input.argument() instanceof PackageIdentifier) {
+ return ((PackageIdentifier) input.argument()) + "/BUILD";
+ } else {
+ throw new UnsupportedOperationException();
+ }
}
- });
+ };
+
+ StringBuilder cycleMessage =
+ new StringBuilder()
+ .append("cycle detected in extension files: ")
+ .append("\n ")
+ .append(printer.apply(lastPathElement));
+
+ AbstractLabelCycleReporter.printCycle(cycleInfo.getCycle(), cycleMessage, printer);
// TODO(bazel-team): it would be nice to pass the Location of the load Statement in the
// BUILD file.
eventHandler.handle(Event.error(null, cycleMessage.toString()));
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index bf6fc78..9ebc6d5 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -1120,7 +1120,6 @@
getConfiguredTarget("//test/skylark:cr4");
}
-
@Test
public void testRecursiveImport() throws Exception {
scratch.file("test/skylark/ext2.bzl", "load('/test/skylark/ext1', 'symbol2')");
@@ -1140,13 +1139,42 @@
// This is expected
}
assertContainsEvent(
- "test/skylark/BUILD: cycle in referenced extension files: \n"
+ "cycle detected in extension files: \n"
+ + " test/skylark/BUILD\n"
+ ".-> //test/skylark:ext1.bzl\n"
+ "| //test/skylark:ext2.bzl\n"
+ "`-- //test/skylark:ext1.bzl");
}
@Test
+ public void testRecursiveImport2() throws Exception {
+ scratch.file("test/skylark/ext1.bzl", "load('//test/skylark:ext2.bzl', 'symbol2')");
+ scratch.file("test/skylark/ext2.bzl", "load('//test/skylark:ext3.bzl', 'symbol3')");
+ scratch.file("test/skylark/ext3.bzl", "load('//test/skylark:ext4.bzl', 'symbol4')");
+ scratch.file("test/skylark/ext4.bzl", "load('//test/skylark:ext2.bzl', 'symbol2')");
+
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('//test/skylark:ext1.bzl', 'custom_rule')",
+ "genrule(name = 'rule')");
+
+ reporter.removeHandler(failFastHandler);
+ try {
+ getTarget("//test/skylark:rule");
+ fail();
+ } catch (BuildFileContainsErrorsException e) {
+ // This is expected
+ }
+ assertContainsEvent(
+ "cycle detected in extension files: \n"
+ + " //test/skylark:ext1.bzl\n"
+ + ".-> //test/skylark:ext2.bzl\n"
+ + "| //test/skylark:ext3.bzl\n"
+ + "| //test/skylark:ext4.bzl\n"
+ + "`-- //test/skylark:ext2.bzl");
+ }
+
+ @Test
public void testSymbolPropagateThroughImports() throws Exception {
scratch.file("test/skylark/implementation.bzl", "def custom_rule_impl(ctx):", " return None");
@@ -1226,5 +1254,35 @@
assertContainsEvent("Loading of target '//test/skylark:rule' failed; build aborted");
assertThat(eventCollector).hasSize(1);
}
+
+ @Override
+ @Test
+ public void testRecursiveImport2() throws Exception {
+ scratch.file("test/skylark/ext1.bzl", "load('//test/skylark:ext2.bzl', 'symbol2')");
+ scratch.file("test/skylark/ext2.bzl", "load('//test/skylark:ext3.bzl', 'symbol3')");
+ scratch.file("test/skylark/ext3.bzl", "load('//test/skylark:ext4.bzl', 'symbol4')");
+ scratch.file("test/skylark/ext4.bzl", "load('//test/skylark:ext2.bzl', 'symbol2')");
+
+ scratch.file(
+ "test/skylark/BUILD",
+ "load('//test/skylark:ext1.bzl', 'custom_rule')",
+ "genrule(name = 'rule')");
+
+ reporter.removeHandler(failFastHandler);
+ try {
+ ensureTargetsVisited("//test/skylark:rule");
+ getTarget("//test/skylark:rule");
+ fail();
+ } catch (BuildFileContainsErrorsException e) {
+ // This is expected
+ }
+ assertContainsEvent("//test/skylark:ext2.bzl");
+ assertContainsEvent("//test/skylark:ext3.bzl");
+ assertContainsEvent("//test/skylark:ext4.bzl");
+ assertContainsEvent("Skylark import cycle");
+ assertContainsEvent("Loading of target '//test/skylark:rule' failed; build aborted");
+ assertThat(eventCollector).hasSize(1);
+ }
+
}
}