Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 939b396

Browse files
gregestrencopybara-github
authored andcommittedJun 17, 2025·
build event streamer: emit exactly one CanonicalCommandLineEvent
Context: https://bazel.build/remote/bep `PROJECT.scl` may add additional canonical command line flags after initial flag parsing. We don't want to risk no canonical command reporting if the build exits or crashes before this logic kicks in. But we also don't want to emit two canonical command events (the initial pre-`PROJECT.scl` one then the updated one). That would confuse BEP consumers who expect a single canonical command. This change adds the concept of a *replaceable* build event. It buffers until either replaced by a new one or the build finishes for any reason (success, fail, crash). If the replacement isn't posted, the original is posted instead. PiperOrigin-RevId: 772634646 Change-Id: I30e975f41c6e68d4e4b1b9590f2ef04c1c02f7cd
1 parent 338ce8a commit 939b396

File tree

10 files changed

+271
-24
lines changed

10 files changed

+271
-24
lines changed
 

‎src/main/java/com/google/devtools/build/lib/buildeventstream/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ java_library(
2727
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
2828
"//src/main/java/com/google/devtools/build/lib/events",
2929
"//src/main/java/com/google/devtools/build/lib/skyframe/config",
30-
"//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
3130
"//src/main/java/com/google/devtools/build/lib/util",
3231
"//src/main/java/com/google/devtools/build/lib/util:detailed_exit_code",
3332
"//src/main/java/com/google/devtools/build/lib/util:exit_code",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2025 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.devtools.build.lib.buildeventstream;
16+
17+
/**
18+
* A {@link BuildEvent} that buffers until either a non-replaceable version (with the same {@link
19+
* BuildEventId}) is posted to replace it or the build ends without the other version happening.
20+
*
21+
* <p>If the non-replaceable version posts later or was posted before this one, this one is
22+
* discarded without posting.
23+
*
24+
* <p>If the non-replaceable version isn't posted for any reason - including build completion, build
25+
* error, or a crash - this version posts.
26+
*
27+
* <p>This lets builds guarantee exactly once instance of an event gets posted. This is useful for
28+
* single-instance events that might be updated later in the build.
29+
*/
30+
public interface ReplaceableBuildEvent extends BuildEvent {
31+
/**
32+
* Is this event replaceable? If so, a non-replaceable version with the same {@link BuildEventId}
33+
* can replace it.
34+
*/
35+
boolean replaceable();
36+
}

‎src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent;
6363
import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
6464
import com.google.devtools.build.lib.buildtool.buildevent.NoExecutionEvent;
65+
import com.google.devtools.build.lib.buildtool.buildevent.ReleaseReplaceableBuildEvent;
6566
import com.google.devtools.build.lib.buildtool.buildevent.StartingAqueryDumpAfterBuildEvent;
6667
import com.google.devtools.build.lib.buildtool.buildevent.UpdateOptionsEvent;
6768
import com.google.devtools.build.lib.cmdline.Label;
@@ -84,6 +85,7 @@
8485
import com.google.devtools.build.lib.runtime.BlazeOptionHandler.SkyframeExecutorTargetLoader;
8586
import com.google.devtools.build.lib.runtime.BlazeRuntime;
8687
import com.google.devtools.build.lib.runtime.CommandEnvironment;
88+
import com.google.devtools.build.lib.runtime.CommandLineEvent;
8789
import com.google.devtools.build.lib.runtime.CommandLineEvent.CanonicalCommandLineEvent;
8890
import com.google.devtools.build.lib.runtime.CommandLineEvent.OriginalCommandLineEvent;
8991
import com.google.devtools.build.lib.runtime.ExecRootEvent;
@@ -304,8 +306,18 @@ public void buildTargets(
304306
optionsParser.getExplicitStarlarkOptions(
305307
OriginalCommandLineEvent::commandLinePriority),
306308
optionsParser.getStarlarkOptions(),
307-
optionsParser.asListOfCanonicalOptions()));
309+
optionsParser.asListOfCanonicalOptions(),
310+
// This replaces the tentative CanonicalCommandLineEvent posted earlier in the
311+
// build in BlazeCommandDispatcher.
312+
/* replaceable= */ false));
308313
env.getEventBus().post(new UpdateOptionsEvent(optionsParser));
314+
} else {
315+
// No PROJECT.scl flag updates. Release the original CanonicalCommandLineEvent for posting.
316+
env.getEventBus()
317+
.post(
318+
new ReleaseReplaceableBuildEvent(
319+
BuildEventIdUtil.structuredCommandlineId(
320+
CommandLineEvent.CanonicalCommandLineEvent.LABEL)));
309321
}
310322
buildOptions = runtime.createBuildOptions(optionsParser);
311323
var analysisCachingDeps =
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2025 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package com.google.devtools.build.lib.buildtool.buildevent;
15+
16+
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
17+
18+
/**
19+
* Posts a pending {@link ReplaceableBuildEvent} immediately. It no longer waits for either the
20+
* build to end or a non-replaceable version to replace it.
21+
*/
22+
public final class ReleaseReplaceableBuildEvent {
23+
private final BuildEventId eventId;
24+
25+
public ReleaseReplaceableBuildEvent(BuildEventId eventId) {
26+
this.eventId = eventId;
27+
}
28+
29+
public BuildEventId getEventId() {
30+
return eventId;
31+
}
32+
}

‎src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -702,9 +702,6 @@ private BlazeCommandResult execExclusively(
702702
options.getExplicitStarlarkOptions(
703703
CommandLineEvent.OriginalCommandLineEvent::commandLinePriority),
704704
startupOptionsTaggedWithBazelRc);
705-
// If flagsets are applied, a CanonicalCommandLineEvent is also emitted by
706-
// BuildTool.buildTargets(). This is a duplicate event, and consumers are expected to
707-
// handle it correctly, by accepting the last event.
708705
CommandLineEvent canonicalCommandLineEvent =
709706
new CommandLineEvent.CanonicalCommandLineEvent(
710707
runtime,
@@ -714,7 +711,14 @@ private BlazeCommandResult execExclusively(
714711
options.getExplicitStarlarkOptions(
715712
CommandLineEvent.OriginalCommandLineEvent::commandLinePriority),
716713
options.getStarlarkOptions(),
717-
options.asListOfCanonicalOptions());
714+
options.asListOfCanonicalOptions(),
715+
// If this is a command that analyzes with BuildTool, PROJECT.scl might set extra
716+
// canonical flags. In that case give BuildTool a chance to post a final updated
717+
// CanonicalCommandLineEvent. Then this one is dropped. But if that event doesn't post
718+
// for any reason, including a build error or crash, post this one so the build still
719+
// registers a canonical command line. That guarantees BuildEventStream always posts
720+
// exactly one CanonicalCommandLineEvent message for all builds.
721+
/* replaceable= */ commandAnnotation.buildPhase().analyzes());
718722
OriginalUnstructuredCommandLineEvent unstructuredServerCommandLineEvent;
719723
if (commandName.equals("run") && !includeResidueInRunBepEvent) {
720724
unstructuredServerCommandLineEvent =

‎src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static com.google.common.base.Strings.nullToEmpty;
1919

2020
import com.google.common.annotations.VisibleForTesting;
21+
import com.google.common.base.Verify;
2122
import com.google.common.collect.ImmutableList;
2223
import com.google.common.collect.ImmutableMap;
2324
import com.google.common.collect.ImmutableSet;
@@ -43,7 +44,6 @@
4344
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
4445
import com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil;
4546
import com.google.devtools.build.lib.buildeventstream.BuildEventProtocolOptions.OutputGroupFileModes;
46-
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Aborted;
4747
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Aborted.AbortReason;
4848
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
4949
import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
@@ -53,13 +53,15 @@
5353
import com.google.devtools.build.lib.buildeventstream.LastBuildEvent;
5454
import com.google.devtools.build.lib.buildeventstream.NullConfiguration;
5555
import com.google.devtools.build.lib.buildeventstream.ProgressEvent;
56+
import com.google.devtools.build.lib.buildeventstream.ReplaceableBuildEvent;
5657
import com.google.devtools.build.lib.buildeventstream.transports.BuildEventStreamOptions;
5758
import com.google.devtools.build.lib.buildtool.BuildRequest;
5859
import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
5960
import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent;
6061
import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
6162
import com.google.devtools.build.lib.buildtool.buildevent.NoAnalyzeEvent;
6263
import com.google.devtools.build.lib.buildtool.buildevent.NoExecutionEvent;
64+
import com.google.devtools.build.lib.buildtool.buildevent.ReleaseReplaceableBuildEvent;
6365
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
6466
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
6567
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
@@ -88,8 +90,13 @@
8890
public class BuildEventStreamer {
8991
/** Return value for {@link #routeBuildEvent}. */
9092
private enum RetentionDecision {
93+
// Delay posting this event until other events post.
9194
BUFFERED,
95+
// Only post this event if the build ends before an event that replaces it is posted.
96+
BUFFERED_FOR_REPLACEMENT,
97+
// Don't post this event.
9298
DISCARD,
99+
// Post this event immediately.
93100
POST
94101
}
95102

@@ -401,11 +408,19 @@ private synchronized void clearMissingStartEvent(BuildEventId id) {
401408
}
402409
}
403410

404-
/** Clear pending events by generating aborted events for all their requisits. */
411+
/** Clear pending events by generating aborted events for all their requests. */
405412
private synchronized void clearPendingEvents() {
406413
while (!pendingEvents.isEmpty()) {
407414
BuildEventId id = pendingEvents.keySet().iterator().next();
408-
buildEvent(new AbortedEvent(id, getLastAbortReason(), getAbortReasonDetails()));
415+
boolean bufferedEventsPendingOnThisType =
416+
releaseReplaceableBuildEvent(new ReleaseReplaceableBuildEvent(id));
417+
if (bufferedEventsPendingOnThisType) {
418+
// Replaceable (BUFERED_FOR_REPLACEMENT) events finally trigger on build abort, so
419+
// we don't need a distinct AbortedEvent to acknowledge them. Normal buffered events
420+
// don't trigger because their trigger event never happened, so they need an
421+
// AbortedEvent.
422+
buildEvent(new AbortedEvent(id, getLastAbortReason(), getAbortReasonDetails()));
423+
}
409424
}
410425
}
411426

@@ -519,6 +534,44 @@ public void noExecution(NoExecutionEvent event) {
519534
addAbortReason(AbortReason.NO_BUILD);
520535
}
521536

537+
/**
538+
* Posts a {@code RetensionDecision#BUFFERED_FOR_REPLACEMENT} build event without waiting for its
539+
* replacement.
540+
*
541+
* <p>Does nothing if no replaceable event is pending for this event type. Note there can be at
542+
* most one pending replaceable event for any build type.
543+
*
544+
* @param event event id of the replaceable event to post
545+
* @return true iff normal buffered events are also waiting on this event id. This is useful to
546+
* distinguish from replaceable events because when builds abort, pending replaceable events
547+
* trigger while normal buffered events are dropped and noted with a matching AbortedEvent.
548+
*/
549+
@Subscribe
550+
public boolean releaseReplaceableBuildEvent(ReleaseReplaceableBuildEvent event) {
551+
BuildEvent replaceable = null;
552+
boolean bufferedEventsAlsoPending = false;
553+
synchronized (this) {
554+
var pendingEventsThisType = pendingEvents.get(event.getEventId()).iterator();
555+
while (pendingEventsThisType.hasNext()) {
556+
BuildEvent pendingEvent = pendingEventsThisType.next();
557+
if (pendingEvent instanceof ReplaceableBuildEvent) {
558+
Verify.verify(
559+
replaceable == null,
560+
"Multiple replaceable events not supported for %s ",
561+
event.getEventId());
562+
replaceable = pendingEvent;
563+
pendingEventsThisType.remove();
564+
} else {
565+
bufferedEventsAlsoPending = true;
566+
}
567+
}
568+
}
569+
if (replaceable != null) {
570+
post(replaceable);
571+
}
572+
return bufferedEventsAlsoPending;
573+
}
574+
522575
@Subscribe
523576
@AllowConcurrentEvents
524577
public void buildEvent(BuildEvent event) {
@@ -540,7 +593,11 @@ public void buildEvent(BuildEvent event) {
540593
return; // bail: we're dropping this event
541594
case BUFFERED:
542595
// Bail: the event was buffered and the BuildEventStreamer is now responsible for eventually
543-
// posting (or discarding) it
596+
// posting it.
597+
return;
598+
case BUFFERED_FOR_REPLACEMENT:
599+
// Bail: the event was buffered to possibly be replaced with an updated version. The
600+
// BuildEventStreamer is now responsible for eventually posting or discarding it.
544601
return;
545602
case POST:
546603
break; // proceed
@@ -585,7 +642,10 @@ public void buildEvent(BuildEvent event) {
585642
blockedEventsFifo = pendingEvents.removeAll(event.getEventId());
586643
}
587644
for (BuildEvent freedEvent : blockedEventsFifo) {
588-
buildEvent(freedEvent);
645+
// Replaceable events have been replaced, so can be silently dropped.
646+
if (!(freedEvent instanceof ReplaceableBuildEvent)) {
647+
buildEvent(freedEvent);
648+
}
589649
}
590650

591651
// Special-case handling for subclasses of `BuildCompletingEvent`.
@@ -830,6 +890,11 @@ private RetentionDecision routeBuildEvent(BuildEvent event) {
830890
return RetentionDecision.DISCARD;
831891
}
832892

893+
RetentionDecision replaceableDecision = decideBufferedForReplacementEvent(event);
894+
if (replaceableDecision != null) {
895+
return replaceableDecision;
896+
}
897+
833898
if (bufferUntilPrerequisitesReceived(event)) {
834899
return RetentionDecision.BUFFERED;
835900
}
@@ -873,6 +938,32 @@ private boolean shouldPublishActionExecutedEvent(ActionExecutedEvent event) {
873938
return event.getAction() instanceof ExtraAction;
874939
}
875940

941+
@Nullable
942+
private synchronized RetentionDecision decideBufferedForReplacementEvent(BuildEvent event) {
943+
if (!(event instanceof ReplaceableBuildEvent replaceableBuiltEvent)) {
944+
return null;
945+
}
946+
if (!replaceableBuiltEvent.replaceable()) {
947+
// The event's class is replaceable but this instance isn't. Treat it normally.
948+
return RetentionDecision.POST;
949+
}
950+
if (postedEvents.contains(event.getEventId())) {
951+
// This event type has already been posted, so the replaceable event is outdated.
952+
return RetentionDecision.DISCARD;
953+
}
954+
synchronized (this) {
955+
Verify.verify(
956+
pendingEvents.get(event.getEventId()).stream()
957+
.filter(e -> e instanceof ReplaceableBuildEvent)
958+
.findFirst()
959+
.isEmpty(),
960+
"Multiple replaceable events not supported for %s",
961+
event.getEventId());
962+
pendingEvents.put(event.getEventId(), event);
963+
}
964+
return RetentionDecision.BUFFERED_FOR_REPLACEMENT;
965+
}
966+
876967
private synchronized boolean bufferUntilPrerequisitesReceived(BuildEvent event) {
877968
if (!(event instanceof BuildEventWithOrderConstraint buildEventWithOrderConstraint)) {
878969
return false;

‎src/main/java/com/google/devtools/build/lib/runtime/CommandLineEvent.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId;
2525
import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint;
2626
import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
27+
import com.google.devtools.build.lib.buildeventstream.ReplaceableBuildEvent;
2728
import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.ChunkList;
2829
import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.CommandLine;
2930
import com.google.devtools.build.lib.runtime.proto.CommandLineOuterClass.CommandLineSection;
@@ -329,11 +330,13 @@ public BuildEventStreamProtos.BuildEvent asStreamProto(BuildEventContext convert
329330
}
330331

331332
/** This reports the canonical form of the command line. */
332-
public static class CanonicalCommandLineEvent extends BazelCommandLineEvent {
333+
public static class CanonicalCommandLineEvent extends BazelCommandLineEvent
334+
implements ReplaceableBuildEvent {
333335
public static final String LABEL = "canonical";
334336
protected final Map<String, Object> explicitStarlarkOptions;
335337
protected final Map<String, Object> starlarkOptions;
336338
protected final List<ParsedOptionDescription> canonicalOptions;
339+
private final boolean replaceable;
337340

338341
public CanonicalCommandLineEvent(
339342
BlazeRuntime runtime,
@@ -342,7 +345,8 @@ public CanonicalCommandLineEvent(
342345
boolean includeResidueInRunBepEvent,
343346
Map<String, Object> explicitStarlarkOptions,
344347
Map<String, Object> starlarkOptions,
345-
List<ParsedOptionDescription> canonicalOptions) {
348+
List<ParsedOptionDescription> canonicalOptions,
349+
boolean replaceable) {
346350
this(
347351
runtime.getProductName(),
348352
runtime.getStartupOptionsProvider(),
@@ -351,7 +355,8 @@ public CanonicalCommandLineEvent(
351355
includeResidueInRunBepEvent,
352356
explicitStarlarkOptions,
353357
starlarkOptions,
354-
canonicalOptions);
358+
canonicalOptions,
359+
replaceable);
355360
}
356361

357362
@VisibleForTesting
@@ -363,18 +368,25 @@ public CanonicalCommandLineEvent(
363368
boolean includeResidueInRunBepEvent,
364369
Map<String, Object> explicitStarlarkOptions,
365370
Map<String, Object> starlarkOptions,
366-
List<ParsedOptionDescription> canonicalOptions) {
371+
List<ParsedOptionDescription> canonicalOptions,
372+
boolean replaceable) {
367373
super(productName, activeStartupOptions, commandName, residue, includeResidueInRunBepEvent);
368374
this.explicitStarlarkOptions = explicitStarlarkOptions;
369375
this.starlarkOptions = starlarkOptions;
370376
this.canonicalOptions = canonicalOptions;
377+
this.replaceable = replaceable;
371378
}
372379

373380
@Override
374381
public BuildEventId getEventId() {
375382
return BuildEventIdUtil.structuredCommandlineId(LABEL);
376383
}
377384

385+
@Override
386+
public boolean replaceable() {
387+
return replaceable;
388+
}
389+
378390
/**
379391
* Returns the effective startup options.
380392
*

‎src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
7272
import com.google.devtools.build.lib.buildeventstream.PathConverter;
7373
import com.google.devtools.build.lib.buildeventstream.ProgressEvent;
74+
import com.google.devtools.build.lib.buildeventstream.ReplaceableBuildEvent;
7475
import com.google.devtools.build.lib.buildeventstream.transports.BuildEventStreamOptions;
7576
import com.google.devtools.build.lib.buildtool.BuildRequest;
7677
import com.google.devtools.build.lib.buildtool.BuildResult;
@@ -1769,6 +1770,57 @@ public void oomAbortReason_includesOomMessage() {
17691770
.build());
17701771
}
17711772

1773+
private static final class ReplaceableTestBuildEvent extends GenericBuildEvent
1774+
implements ReplaceableBuildEvent {
1775+
private final boolean replaceable;
1776+
1777+
ReplaceableTestBuildEvent(BuildEventId id, boolean replaceable) {
1778+
super(id, ImmutableSet.of());
1779+
this.replaceable = replaceable;
1780+
}
1781+
1782+
@Override
1783+
public boolean replaceable() {
1784+
return replaceable;
1785+
}
1786+
}
1787+
1788+
@Test
1789+
public void replaceableEvent_doesNotPostBecauseisReplaced() {
1790+
BuildEventId buildEventId = testId("replaceable_event");
1791+
BuildEvent replaceable = new ReplaceableTestBuildEvent(buildEventId, /* replaceable= */ true);
1792+
BuildEvent replacedBy = new ReplaceableTestBuildEvent(buildEventId, /* replaceable= */ false);
1793+
1794+
streamer.buildEvent(replaceable);
1795+
assertThat(transport.getEvents()).isEmpty();
1796+
streamer.buildEvent(replacedBy);
1797+
assertThat(transport.getEvents()).doesNotContain(replaceable);
1798+
assertThat(transport.getEvents()).contains(replacedBy);
1799+
}
1800+
1801+
@Test
1802+
public void replaceableEvent_postsBecauseisNotReplacedAndBuildAborts() {
1803+
BuildEventId buildEventId = testId("replaceable_event");
1804+
BuildEvent replaceable = new ReplaceableTestBuildEvent(buildEventId, /* replaceable= */ true);
1805+
1806+
streamer.buildEvent(replaceable);
1807+
assertThat(transport.getEvents()).isEmpty();
1808+
streamer.noAnalyze(new NoAnalyzeEvent());
1809+
streamer.close();
1810+
assertThat(transport.getEvents()).contains(replaceable);
1811+
}
1812+
1813+
@Test
1814+
public void replaceableEvent_postsBecauseisNotReplacedAndBuildCompletes() {
1815+
BuildEventId buildEventId = testId("replaceable_event");
1816+
BuildEvent replaceable = new ReplaceableTestBuildEvent(buildEventId, /* replaceable= */ true);
1817+
1818+
streamer.buildEvent(replaceable);
1819+
assertThat(transport.getEvents()).isEmpty();
1820+
streamer.buildEvent(new BuildCompleteEvent(new BuildResult(0)));
1821+
assertThat(transport.getEvents()).contains(replaceable);
1822+
}
1823+
17721824
@Nullable
17731825
private BuildEventStreamProtos.BuildEvent getBepEvent(BuildEventId buildEventId) {
17741826
return transport.getEventProtos().stream()

‎src/test/java/com/google/devtools/build/lib/runtime/CommandLineEventTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ public void testMostlyEmpty_canonicalCommandLine() {
9999
false,
100100
ImmutableSortedMap.of(),
101101
ImmutableSortedMap.of(),
102-
fakeCommandOptions.asListOfCanonicalOptions())
102+
fakeCommandOptions.asListOfCanonicalOptions(),
103+
/* replaceable= */ false)
103104
.asStreamProto(null)
104105
.getStructuredCommandLine();
105106

@@ -218,7 +219,8 @@ public void testBazelrcs_canonicalCommandLine() throws OptionsParsingException {
218219
false,
219220
ImmutableSortedMap.of(),
220221
ImmutableSortedMap.of(),
221-
fakeCommandOptions.asListOfCanonicalOptions())
222+
fakeCommandOptions.asListOfCanonicalOptions(),
223+
/* replaceable= */ false)
222224
.asStreamProto(null)
223225
.getStructuredCommandLine();
224226

@@ -308,7 +310,8 @@ public void testOptionsAtVariousPriorities_canonicalCommandLine() throws Options
308310
false,
309311
ImmutableSortedMap.of(),
310312
ImmutableSortedMap.of(),
311-
fakeCommandOptions.asListOfCanonicalOptions())
313+
fakeCommandOptions.asListOfCanonicalOptions(),
314+
/* replaceable= */ false)
312315
.asStreamProto(null)
313316
.getStructuredCommandLine();
314317

@@ -385,7 +388,8 @@ public void testExpansionOption_canonicalCommandLine() throws OptionsParsingExce
385388
false,
386389
ImmutableSortedMap.of(),
387390
ImmutableSortedMap.of(),
388-
fakeCommandOptions.asListOfCanonicalOptions())
391+
fakeCommandOptions.asListOfCanonicalOptions(),
392+
/* replaceable= */ false)
389393
.asStreamProto(null)
390394
.getStructuredCommandLine();
391395

@@ -466,7 +470,8 @@ public void testOptionWithImplicitRequirement_canonicalCommandLine()
466470
false,
467471
ImmutableSortedMap.of(),
468472
ImmutableSortedMap.of(),
469-
fakeCommandOptions.asListOfCanonicalOptions())
473+
fakeCommandOptions.asListOfCanonicalOptions(),
474+
/* replaceable= */ false)
470475
.asStreamProto(null)
471476
.getStructuredCommandLine();
472477

@@ -623,7 +628,8 @@ public void redactedResidual_includesTarget_canonicalCommandLine()
623628
false,
624629
ImmutableSortedMap.of(),
625630
ImmutableSortedMap.of(),
626-
fakeCommandOptions.asListOfCanonicalOptions())
631+
fakeCommandOptions.asListOfCanonicalOptions(),
632+
/* replaceable= */ false)
627633
.asStreamProto(null)
628634
.getStructuredCommandLine();
629635

‎src/test/java/com/google/devtools/build/lib/runtime/StarlarkOptionCommandLineEventTest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ public void testStarlarkOptions_canonical() throws Exception {
151151
optionsParser.getExplicitStarlarkOptions(
152152
OriginalCommandLineEvent::commandLinePriority),
153153
optionsParser.getStarlarkOptions(),
154-
optionsParser.asListOfCanonicalOptions())
154+
optionsParser.asListOfCanonicalOptions(),
155+
/* replaceable= */ false)
155156
.asStreamProto(null)
156157
.getStructuredCommandLine();
157158
assertThat(line.getCommandLineLabel()).isEqualTo("canonical");
@@ -188,7 +189,8 @@ public void testStarlarkOptions_canonical_defaultValue() throws Exception {
188189
optionsParser.getExplicitStarlarkOptions(
189190
OriginalCommandLineEvent::commandLinePriority),
190191
optionsParser.getStarlarkOptions(),
191-
optionsParser.asListOfCanonicalOptions())
192+
optionsParser.asListOfCanonicalOptions(),
193+
/* replaceable= */ false)
192194
.asStreamProto(null)
193195
.getStructuredCommandLine();
194196
assertThat(line.getCommandLineLabel()).isEqualTo("canonical");
@@ -250,7 +252,8 @@ def _build_setting_impl(ctx):
250252
optionsParser.getExplicitStarlarkOptions(
251253
OriginalCommandLineEvent::commandLinePriority),
252254
optionsParser.getStarlarkOptions(),
253-
optionsParser.asListOfCanonicalOptions())
255+
optionsParser.asListOfCanonicalOptions(),
256+
/* replaceable= */ false)
254257
.asStreamProto(null)
255258
.getStructuredCommandLine();
256259

0 commit comments

Comments
 (0)
Please sign in to comment.