diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java index 5ccce705a0a798..e99cd0377e6947 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java @@ -39,6 +39,17 @@ public class AnalysisOptions extends OptionsBase { ) public boolean discardAnalysisCache; + @Option( + name = "allow_analysis_cache_discard", + defaultValue = "true", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.EAGERNESS_TO_EXIT}, + help = "If discarding the analysis cache due to a change in the build system, setting this option to false will cause bazel" + + " to exit, rather than continuing with the build. This option has no effect when 'discard_analysis_cache'" + + " is also set." + ) + public boolean allowAnalysisCacheDiscards; + @Option( name = "max_config_changes_to_show", defaultValue = "3", diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java index 2cecf163e5d1fe..b5e296beed4997 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java @@ -255,7 +255,7 @@ public AnalysisResult update( } skyframeBuildView.setConfiguration( - eventHandler, topLevelConfig, viewOptions.maxConfigChangesToShow); + eventHandler, topLevelConfig, viewOptions.maxConfigChangesToShow, viewOptions.allowAnalysisCacheDiscards); eventBus.post(new MakeEnvironmentEvent(topLevelConfig.getMakeEnvironment())); eventBus.post(topLevelConfig.toBuildEvent()); diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BUILD b/src/main/java/com/google/devtools/build/lib/buildeventstream/BUILD index 8a19572b9bb903..d538569e722eb0 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/BUILD +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BUILD @@ -28,6 +28,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/skyframe:build_configuration", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec", "//src/main/java/com/google/devtools/build/lib/util", + "//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code", "//src/main/java/com/google/devtools/build/lib/util:exit_code", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildCompletingEvent.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildCompletingEvent.java index 8390012989bfc8..0c0c1d516759e2 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildCompletingEvent.java +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildCompletingEvent.java @@ -16,8 +16,11 @@ import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId; +import com.google.devtools.build.lib.util.DetailedExitCode; import com.google.devtools.build.lib.util.ExitCode; import com.google.protobuf.util.Timestamps; + +import javax.annotation.Nullable; import java.util.Collection; /** @@ -27,6 +30,8 @@ * However, subclasses do not have to implement anything. */ public abstract class BuildCompletingEvent implements BuildEvent { + @Nullable + private final DetailedExitCode detailedExitCode; private final ExitCode exitCode; private final long finishTimeMillis; @@ -34,11 +39,20 @@ public abstract class BuildCompletingEvent implements BuildEvent { public BuildCompletingEvent( ExitCode exitCode, long finishTimeMillis, Collection children) { + this.detailedExitCode = null; this.exitCode = exitCode; this.finishTimeMillis = finishTimeMillis; this.children = children; } + public BuildCompletingEvent( + DetailedExitCode detailedExitCode, long finishTimeMillis, Collection children) { + this.detailedExitCode = detailedExitCode; + this.exitCode = detailedExitCode.getExitCode(); + this.finishTimeMillis = finishTimeMillis; + this.children = children; + } + public BuildCompletingEvent(ExitCode exitCode, long finishTimeMillis) { this(exitCode, finishTimeMillis, ImmutableList.of()); } @@ -65,13 +79,17 @@ public BuildEventStreamProtos.BuildEvent asStreamProto(BuildEventContext convert .setCode(exitCode.getNumericExitCode()) .build(); - BuildEventStreamProtos.BuildFinished finished = + BuildEventStreamProtos.BuildFinished.Builder finished = BuildEventStreamProtos.BuildFinished.newBuilder() .setOverallSuccess(ExitCode.SUCCESS.equals(exitCode)) .setExitCode(protoExitCode) .setFinishTime(Timestamps.fromMillis(finishTimeMillis)) - .setFinishTimeMillis(finishTimeMillis) - .build(); - return GenericBuildEvent.protoChaining(this).setFinished(finished).build(); + .setFinishTimeMillis(finishTimeMillis); + + if (detailedExitCode != null && detailedExitCode.getFailureDetail() != null) { + finished.setFailureDetail(detailedExitCode.getFailureDetail()); + } + + return GenericBuildEvent.protoChaining(this).setFinished(finished.build()).build(); } } diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto index e9b88ab1a72c8c..656f7a341be005 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto @@ -836,6 +836,9 @@ message BuildFinished { google.protobuf.Timestamp finish_time = 5; AnomalyReport anomaly_report = 4 [deprecated = true]; + + // Only populated if success = false, and sometimes not even then. + failure_details.FailureDetail failure_detail = 6; } message BuildMetrics { diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java index 2b77fa5b0d9b4d..ff631281bda0f1 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/buildevent/BuildCompleteEvent.java @@ -32,7 +32,7 @@ public final class BuildCompleteEvent extends BuildCompletingEvent { /** Construct the BuildCompleteEvent. */ public BuildCompleteEvent(BuildResult result, Collection children) { - super(result.getDetailedExitCode().getExitCode(), result.getStopTime(), children); + super(result.getDetailedExitCode(), result.getStopTime(), children); this.result = checkNotNull(result); } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java index 462e4d40a67bc7..5134ab573b0433 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java @@ -66,6 +66,7 @@ import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiff; import com.google.devtools.build.lib.analysis.config.ConfigConditions; +import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.StarlarkTransitionCache; import com.google.devtools.build.lib.analysis.test.AnalysisFailurePropagationException; import com.google.devtools.build.lib.analysis.test.CoverageActionFinishedEvent; @@ -89,6 +90,7 @@ import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.profiler.Profiler; import com.google.devtools.build.lib.profiler.SilentCloseable; +import com.google.devtools.build.lib.server.FailureDetails; import com.google.devtools.build.lib.skyframe.ArtifactConflictFinder.ConflictException; import com.google.devtools.build.lib.skyframe.AspectKeyCreator.AspectKey; import com.google.devtools.build.lib.skyframe.AspectKeyCreator.TopLevelAspectsKey; @@ -279,7 +281,7 @@ private ImmutableSet getNativeCacheInvalidatingDifferences( /** Sets the configuration. Not thread-safe. DO NOT CALL except from tests! */ @VisibleForTesting public void setConfiguration( - EventHandler eventHandler, BuildConfigurationValue configuration, int maxDifferencesToShow) { + EventHandler eventHandler, BuildConfigurationValue configuration, int maxDifferencesToShow, boolean allowAnalysisCacheDiscards) throws InvalidConfigurationException { if (skyframeAnalysisWasDiscarded) { eventHandler.handle( Event.warn( @@ -290,6 +292,11 @@ public void setConfiguration( } else { String diff = describeConfigurationDifference(configuration, maxDifferencesToShow); if (diff != null) { + if (!allowAnalysisCacheDiscards) { + String message = String.format("%s, analysis cache would have been discarded.", diff); + throw new InvalidConfigurationException( + message, FailureDetails.BuildConfiguration.Code.CONFIGURATION_DISCARDED_ANALYSIS_CACHE); + } eventHandler.handle( Event.warn( diff diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto index 64ba35d30999a1..37c922a289e8a8 100644 --- a/src/main/protobuf/failure_details.proto +++ b/src/main/protobuf/failure_details.proto @@ -608,6 +608,7 @@ message BuildConfiguration { // possibilities in Bazel, so we go with the more straightforward // command-line error exit code 2. INVALID_OUTPUT_DIRECTORY_MNEMONIC = 11 [(metadata) = { exit_code: 2 }]; + CONFIGURATION_DISCARDED_ANALYSIS_CACHE = 12 [(metadata) = { exit_code: 2 }]; } Code code = 1; @@ -1197,6 +1198,7 @@ message Analysis { CONFIGURED_VALUE_CREATION_FAILED = 18 [(metadata) = { exit_code: 1 }]; INCOMPATIBLE_TARGET_REQUESTED = 19 [(metadata) = { exit_code: 1 }]; ANALYSIS_FAILURE_PROPAGATION_FAILED = 20 [(metadata) = { exit_code: 1 }]; + ANALYSIS_CACHE_DISCARDED = 21 [(metadata) = { exit_code: 1 }]; } Code code = 1; diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisCachingTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisCachingTest.java index b0140032937192..864ed028fbd2ea 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisCachingTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisCachingTest.java @@ -16,6 +16,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -25,6 +26,7 @@ import com.google.devtools.build.lib.analysis.config.BuildOptionsView; import com.google.devtools.build.lib.analysis.config.Fragment; import com.google.devtools.build.lib.analysis.config.FragmentOptions; +import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.RequiresOptions; import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition; @@ -1163,4 +1165,16 @@ public void cacheClearMessageAfterDiscardAnalysisCacheBuildWithRelevantOptionCha assertDoesNotContainEvent("Build option"); assertContainsEvent("discarding analysis cache"); } + + @Test + public void throwsIfAnalysisCacheIsDiscardedWhenOptionSet() throws Exception { + setupDiffResetTesting(); + scratch.file("test/BUILD", "load(':lib.bzl', 'normal_lib')", "normal_lib(name='top')"); + useConfiguration("--definitely_relevant=old"); + update("//test:top"); + useConfiguration("--noallow_analysis_cache_discard", "--definitely_relevant=new"); + + Throwable t = assertThrows(InvalidConfigurationException.class, () -> update("//test:top")); + assertTrue(t.getMessage().contains("analysis cache would have been discarded")); + } } diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/BUILD index 0d845604d07a9f..43db508da69255 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/BUILD +++ b/src/test/java/com/google/devtools/build/lib/analysis/BUILD @@ -85,6 +85,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment", "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options", "//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_registry", + "//src/main/java/com/google/devtools/build/lib/analysis:config/invalid_configuration_exception", "//src/main/java/com/google/devtools/build/lib/analysis:config/per_label_options", "//src/main/java/com/google/devtools/build/lib/analysis:config/run_under", "//src/main/java/com/google/devtools/build/lib/analysis:config/run_under_converter", diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java index 95ca620a76b7fe..aeed8d04ed15f7 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java @@ -258,7 +258,13 @@ public AnalysisResult update( /** Sets the configuration. Not thread-safe. */ public void setConfigurationForTesting( EventHandler eventHandler, BuildConfigurationValue configuration) { - skyframeBuildView.setConfiguration(eventHandler, configuration, /* maxDifferencesToShow= */ -1); + try { + skyframeBuildView.setConfiguration(eventHandler, configuration, + /* maxDifferencesToShow= */ -1, /* allowAnalysisCacheDiscards */ true); + } catch (InvalidConfigurationException e) { + throw new UnsupportedOperationException("InvalidConfigurationException was thrown and caught during a test, " + + "this case is not yet handled", e); + } } public ArtifactFactory getArtifactFactory() {