Fix configuration error handling for the interleaved case.
In the interleaved case, loading errors can occur during configuration creation
and we need to correctly report such errors in that case.
--
MOS_MIGRATED_REVID=113826273
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
index 8ab04b3..4452f3d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
@@ -32,6 +32,7 @@
import com.google.devtools.build.lib.analysis.config.PackageProviderForConfigurations;
import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses.CppTransition;
import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
@@ -62,7 +63,7 @@
Cache<String, BuildConfiguration> cache,
PackageProviderForConfigurations packageProvider,
BuildOptions buildOptions,
- EventHandler errorEventListener,
+ EventHandler eventHandler,
boolean performSanityCheck) throws InvalidConfigurationException {
// Target configuration
BuildConfiguration targetConfiguration = configurationFactory.getConfiguration(
@@ -107,28 +108,31 @@
return null;
}
-
// Sanity check that the implicit labels are all in the transitive closure of explicit ones.
// This also registers all targets in the cache entry and validates them on subsequent requests.
Set<Label> reachableLabels = new HashSet<>();
if (performSanityCheck) {
// We allow the package provider to be null for testing.
- for (Label label : buildOptions.getAllLabels().values()) {
+ for (Map.Entry<String, Label> entry : buildOptions.getAllLabels().entries()) {
+ Label label = entry.getValue();
try {
collectTransitiveClosure(packageProvider, reachableLabels, label);
} catch (NoSuchThingException e) {
- // We've loaded the transitive closure of the labels-to-load above, and made sure that
- // there are no errors loading it, so this can't happen.
- throw new IllegalStateException(e);
+ eventHandler.handle(Event.error(e.getMessage()));
+ throw new InvalidConfigurationException(
+ String.format("Failed to load required %s target: '%s'", entry.getKey(), label));
}
}
+ if (packageProvider.valuesMissing()) {
+ return null;
+ }
sanityCheckImplicitLabels(reachableLabels, targetConfiguration);
sanityCheckImplicitLabels(reachableLabels, hostConfiguration);
}
BuildConfiguration result = setupTransitions(
targetConfiguration, dataConfiguration, hostConfiguration, splitTransitionsTable);
- result.reportInvalidOptions(errorEventListener);
+ result.reportInvalidOptions(eventHandler);
return result;
}
diff --git a/src/main/java/com/google/devtools/build/lib/pkgcache/LegacyLoadingPhaseRunner.java b/src/main/java/com/google/devtools/build/lib/pkgcache/LegacyLoadingPhaseRunner.java
index 68e1a44..4aaf49d 100644
--- a/src/main/java/com/google/devtools/build/lib/pkgcache/LegacyLoadingPhaseRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/pkgcache/LegacyLoadingPhaseRunner.java
@@ -385,9 +385,10 @@
// Error out if any of the labels needed for the configuration could not be loaded.
Multimap<Label, Label> rootCauses = pkgLoader.getRootCauses();
for (Map.Entry<String, Label> entry : labelsToLoadUnconditionally.entries()) {
- if (rootCauses.containsKey(entry.getValue())) {
- throw new LoadingFailedException("Failed to load required " + entry.getKey()
- + " target: '" + entry.getValue() + "'");
+ Label label = entry.getValue();
+ if (rootCauses.containsKey(label)) {
+ throw new LoadingFailedException(
+ String.format("Failed to load required %s target: '%s'", entry.getKey(), label));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationCollectionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationCollectionFunction.java
index a5061b2..f39a9e3 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationCollectionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfigurationCollectionFunction.java
@@ -24,8 +24,8 @@
import com.google.devtools.build.lib.analysis.config.HostTransition;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.config.PackageProviderForConfigurations;
+import com.google.devtools.build.lib.events.ErrorSensingEventHandler;
import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.skyframe.ConfigurationCollectionValue.ConfigurationCollectionKey;
@@ -156,7 +156,7 @@
EventHandler originalEventListener,
PackageProviderForConfigurations loadedPackageProvider,
BuildOptions buildOptions, String cpuOverride) throws InvalidConfigurationException {
- StoredEventHandler errorEventListener = new StoredEventHandler();
+ ErrorSensingEventHandler eventHandler = new ErrorSensingEventHandler(originalEventListener);
if (cpuOverride != null) {
// TODO(bazel-team): Options classes should be immutable. This is a bit of a hack.
buildOptions = buildOptions.clone();
@@ -164,12 +164,13 @@
}
BuildConfiguration targetConfig = configurationFactory.get().createConfigurations(
- cache, loadedPackageProvider, buildOptions, errorEventListener);
+ cache, loadedPackageProvider, buildOptions, eventHandler);
if (targetConfig == null) {
return null;
}
- errorEventListener.replayOn(originalEventListener);
- if (errorEventListener.hasErrors()) {
+ // The ConfigurationFactory may report an error rather than throwing an exception to support
+ // --keep_going. If so, we throw an error here.
+ if (eventHandler.hasErrors()) {
throw new InvalidConfigurationException("Build options are invalid");
}
return targetConfig;
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
index ac289bb..1e9e882 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
@@ -1011,6 +1011,27 @@
.contains("execution phase succeeded, but there were loading phase errors");
}
+ @Test
+ public void testMissingLabelInConfiguration() throws Exception {
+ scratch.file("nobuild/BUILD",
+ "cc_library(name= 'lib')");
+ useConfiguration("--experimental_action_listener=//nobuild:bar");
+ reporter.removeHandler(failFastHandler);
+ String begin = String.format(
+ "Failed to load required %s target: '%s'", "action_listener", "//nobuild:bar");
+ try {
+ update(defaultFlags().with(Flag.KEEP_GOING), "//nobuild:lib");
+ fail();
+ } catch (InvalidConfigurationException e) {
+ // Interleaved loading and analysis - loading errors are found during configuration creation.
+ assertThat(e.getMessage()).startsWith(begin);
+ } catch (LoadingFailedException e) {
+ assertThat(e.getMessage()).startsWith(begin);
+ }
+ assertContainsEventWithFrequency(
+ "no such target '//nobuild:bar': target 'bar' not declared in package 'nobuild'", 1);
+ }
+
/** Runs the same test with the reduced loading phase. */
@TestSpec(size = Suite.SMALL_TESTS)
@RunWith(JUnit4.class)