Fix crash on graph cycle in configuration loading

PiperOrigin-RevId: 153473961
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index f572c26..fe5b1d4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -1048,10 +1048,15 @@
     EvaluationResult<ConfigurationCollectionValue> result =
         buildDriver.evaluate(Arrays.asList(skyKey), keepGoing, DEFAULT_THREAD_COUNT, eventHandler);
     if (result.hasError()) {
-      Throwable e = result.getError(skyKey).getException();
+      ErrorInfo error = result.getError(skyKey);
+      Throwable e = error.getException();
       // Wrap loading failed exceptions
       if (e instanceof NoSuchThingException) {
         e = new InvalidConfigurationException(e);
+      } else if (e == null && !Iterables.isEmpty(error.getCycleInfo())) {
+        getCyclesReporter().reportCycles(error.getCycleInfo(), skyKey, eventHandler);
+        e = new InvalidConfigurationException(
+            "cannot load build configuration because of this cycle");
       }
       Throwables.propagateIfInstanceOf(e, InvalidConfigurationException.class);
       throw new IllegalStateException(
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java
index d273f8c..fe17b47 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildConfigurationTest.java
@@ -26,6 +26,8 @@
 import com.google.devtools.build.lib.analysis.util.ConfigurationTestCase;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.rules.cpp.CppConfiguration;
 import com.google.devtools.build.lib.rules.java.JavaConfiguration;
 import com.google.devtools.build.lib.rules.objc.J2ObjcConfiguration;
@@ -372,4 +374,60 @@
     BuildConfiguration cfg = createHost("--define=foo=bar");
     assertThat(cfg.getCommandLineBuildVariables().get("foo")).isEqualTo("bar");
   }
+
+  /**
+   * Returns a mock config fragment that loads the given label and does nothing else.
+   */
+  private static ConfigurationFragmentFactory createMockFragmentWithLabelDep(final String label) {
+    return new ConfigurationFragmentFactory() {
+      @Override
+      public Fragment create(ConfigurationEnvironment env, BuildOptions buildOptions)
+          throws InvalidConfigurationException, InterruptedException {
+        try {
+          env.getTarget(Label.parseAbsoluteUnchecked(label));
+        } catch (NoSuchPackageException e) {
+          fail("cannot load mock fragment's dep label " + label + ": " + e.getMessage());
+        } catch (NoSuchTargetException e) {
+          fail("cannot load mock fragment's dep label " + label + ": " + e.getMessage());
+        }
+        return new Fragment() {};
+      }
+
+      @Override
+      public Class<? extends Fragment> creates() {
+        return CppConfiguration.class;
+      }
+
+      @Override
+      public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
+        return ImmutableSet.<Class<? extends FragmentOptions>>of();
+      }
+    };
+  }
+
+  @Test
+  public void depLabelCycleOnConfigurationLoading() throws Exception {
+    configurationFactory =
+        new ConfigurationFactory(
+            analysisMock.createConfigurationCollectionFactory(),
+            createMockFragmentWithLabelDep("//foo"));
+    getScratch().file("foo/BUILD",
+        "load('//skylark:one.bzl', 'one')",
+        "cc_library(name = 'foo')");
+    getScratch().file("skylark/BUILD");
+    getScratch().file("skylark/one.bzl",
+        "load('//skylark:two.bzl', 'two')",
+        "def one():",
+        "  pass");
+    getScratch().file("skylark/two.bzl",
+        "load('//skylark:one.bzl', 'one')",
+        "def two():",
+        "  pass");
+    checkError(String.join("\n",
+        "ERROR <no location>: cycle detected in extension files: ",
+        "    foo/BUILD",
+        ".-> //skylark:one.bzl",
+        "|   //skylark:two.bzl",
+        "`-- //skylark:one.bzl"));
+  }
 }