aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib
diff options
context:
space:
mode:
authorGravatar buchgr <buchgr@google.com>2017-04-24 17:34:20 +0200
committerGravatar Vladimir Moskva <vladmos@google.com>2017-04-24 18:00:29 +0200
commit3d596d63f883fff56001ed7b2e5cf51dba45f082 (patch)
treec3e488c85eaa26ab83176750727429d325aaa1ef /src/main/java/com/google/devtools/build/lib
parent29aee67c0c95af2840faa47a071b72adf4b734ce (diff)
BEP: Show protocol upload in the UI
The BEP (build event protocol) upload may at times take longer than the build itself. This is especially true for cached builds, where there is little build work, but the protocol still needs to be uploaded. In this case the bazel UI will now display that it's waiting for BEP upload, both in the current and the new experimental UI (--experimental_ui). When executing a run command, blaze waits for the BEP upload to finish before it runs the target. Major Modifications: - The BuildEventTransport interface now also has a name() method that returns a string. When waiting for a transport to finish the BEP upload, this string is displayed in the UI. - The BuildEventStreamer now emits two new events AnnounceBuildEventTransportsEvent and BuildEventTransportClosed on the event bus. This is how the experimental UI is informed about BEP upload progress. RELNOTES: None PiperOrigin-RevId: 154052401
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/AnnounceBuildEventTransportsEvent.java38
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransport.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransportClosedEvent.java35
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java5
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java87
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java3
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java41
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/ExperimentalStateTracker.java76
10 files changed, 283 insertions, 20 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/AnnounceBuildEventTransportsEvent.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/AnnounceBuildEventTransportsEvent.java
new file mode 100644
index 0000000000..e27f0b13ec
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/AnnounceBuildEventTransportsEvent.java
@@ -0,0 +1,38 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.buildeventstream;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * An event announcing a list of all active {@link BuildEventTransport}s.
+ */
+public class AnnounceBuildEventTransportsEvent implements Postable {
+
+ private final Set<BuildEventTransport> transports;
+
+ public AnnounceBuildEventTransportsEvent(Collection<BuildEventTransport> transports) {
+ this.transports = ImmutableSet.copyOf(transports);
+ }
+
+ /**
+ * Returns a list of all active build event transports.
+ */
+ public Set<BuildEventTransport> transports() {
+ return transports;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransport.java
index dba5a123d2..84845ab80c 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransport.java
@@ -13,7 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.buildeventstream;
-import java.util.concurrent.Future;
+import com.google.common.util.concurrent.ListenableFuture;
import javax.annotation.concurrent.ThreadSafe;
/**
@@ -28,6 +28,11 @@ import javax.annotation.concurrent.ThreadSafe;
public interface BuildEventTransport {
/**
+ * The name of this transport as can be displayed to a user.
+ */
+ String name();
+
+ /**
* Writes a build event to an endpoint. This method will always return quickly and will not
* wait for the write to complete.
*
@@ -49,5 +54,5 @@ public interface BuildEventTransport {
*
* <p>This method should not throw any exceptions.
*/
- Future<Void> close();
+ ListenableFuture<Void> close();
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransportClosedEvent.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransportClosedEvent.java
new file mode 100644
index 0000000000..813e4f4a1c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventTransportClosedEvent.java
@@ -0,0 +1,35 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.buildeventstream;
+
+import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
+
+/**
+ * An event signaling that a {@link BuildEventTransport} has been closed.
+ */
+public class BuildEventTransportClosedEvent implements Postable {
+
+ private final BuildEventTransport transport;
+
+ public BuildEventTransportClosedEvent(BuildEventTransport transport) {
+ this.transport = transport;
+ }
+
+ /**
+ * Returns the {@link BuildEventTransport} that has been closed.
+ */
+ public BuildEventTransport transport() {
+ return transport;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
index 9de20b7175..6173e9febc 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java
@@ -44,6 +44,11 @@ public final class BinaryFormatFileTransport extends FileTransport {
}
@Override
+ public String name() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
public synchronized void sendBuildEvent(BuildEvent event, final ArtifactGroupNamer namer) {
BuildEventConverters converters =
new BuildEventConverters() {
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
index 6744df3ef6..3482cadd4b 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/FileTransport.java
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
@@ -27,7 +28,6 @@ import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
-import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -87,7 +87,7 @@ abstract class FileTransport implements BuildEventTransport {
}
@Override
- public synchronized Future<Void> close() {
+ public synchronized ListenableFuture<Void> close() {
if (closing()) {
return closeFuture;
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
index cf892955a7..dc7c2ade65 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransport.java
@@ -38,6 +38,11 @@ public final class TextFormatFileTransport extends FileTransport {
}
@Override
+ public String name() {
+ return this.getClass().getSimpleName();
+ }
+
+ @Override
public synchronized void sendBuildEvent(BuildEvent event, final ArtifactGroupNamer namer) {
BuildEventConverters converters =
new BuildEventConverters() {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
index 63583149fc..90242a8e7e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
@@ -14,23 +14,31 @@
package com.google.devtools.build.lib.runtime;
+import static com.google.devtools.build.lib.events.Event.of;
+import static com.google.devtools.build.lib.events.EventKind.PROGRESS;
+import static com.google.devtools.build.lib.util.Preconditions.checkArgument;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.eventbus.Subscribe;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import com.google.devtools.build.lib.actions.ActionExecutedEvent;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.EventReportingArtifacts;
import com.google.devtools.build.lib.analysis.NoBuildEvent;
import com.google.devtools.build.lib.buildeventstream.AbortedEvent;
+import com.google.devtools.build.lib.buildeventstream.AnnounceBuildEventTransportsEvent;
import com.google.devtools.build.lib.buildeventstream.ArtifactGroupNamer;
import com.google.devtools.build.lib.buildeventstream.BuildEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEventId;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Aborted.AbortReason;
import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.NamedSetOfFilesId;
import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
+import com.google.devtools.build.lib.buildeventstream.BuildEventTransportClosedEvent;
import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint;
import com.google.devtools.build.lib.buildeventstream.ProgressEvent;
import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
@@ -39,6 +47,7 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetView;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.rules.extra.ExtraAction;
import java.util.ArrayList;
import java.util.Collection;
@@ -47,13 +56,26 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.Future;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
-/** Listen for {@link BuildEvent} and stream them to the provided {@link BuildEventTransport}. */
+/**
+ * Listens for {@link BuildEvent}s and streams them to the provided {@link BuildEventTransport}s.
+ *
+ * <p>The streamer takes care of closing all {@link BuildEventTransport}s. It does so after having
+ * received a {@link BuildCompleteEvent}. Furthermore, it emits two event types to the
+ * {@code eventBus}. After having received the first {@link BuildEvent} it emits a
+ * {@link AnnounceBuildEventTransportsEvent} that contains a list of all its transports.
+ * Furthermore, after a transport has been closed, it emits
+ * a {@link BuildEventTransportClosedEvent}.
+ */
public class BuildEventStreamer implements EventHandler {
private final Collection<BuildEventTransport> transports;
+ private final Reporter reporter;
private Set<BuildEventId> announcedEvents;
private final Set<BuildEventId> postedEvents = new HashSet<>();
private final Multimap<BuildEventId, BuildEvent> pendingEvents = HashMultimap.create();
@@ -93,8 +115,10 @@ public class BuildEventStreamer implements EventHandler {
}
}
- public BuildEventStreamer(Collection<BuildEventTransport> transports) {
+ public BuildEventStreamer(Collection<BuildEventTransport> transports, Reporter reporter) {
+ checkArgument(transports.size() > 0);
this.transports = transports;
+ this.reporter = reporter;
this.announcedEvents = null;
this.progressCount = 0;
}
@@ -119,6 +143,10 @@ public class BuildEventStreamer implements EventHandler {
announcedEvents.addAll(linkEvent.getChildrenEvents());
postedEvents.add(linkEvent.getEventId());
}
+
+ if (reporter != null) {
+ reporter.post(new AnnounceBuildEventTransportsEvent(transports));
+ }
} else {
if (!announcedEvents.contains(id)) {
linkEvent = ProgressEvent.progressChainIn(progressCount, id);
@@ -165,20 +193,57 @@ public class BuildEventStreamer implements EventHandler {
}
}
- private void close() {
- List<Future<Void>> shutdownFutures = new ArrayList<>(transports.size());
+ private ScheduledFuture<?> bepUploadWaitEvent(ScheduledExecutorService executor) {
+ final long startNanos = System.nanoTime();
+ return executor.scheduleAtFixedRate(new Runnable() {
+ @Override
+ public void run() {
+ long deltaNanos = System.nanoTime() - startNanos;
+ long deltaSeconds = TimeUnit.NANOSECONDS.toSeconds(deltaNanos);
+ Event waitEvt =
+ of(PROGRESS, null, "Waiting for build event protocol upload: " + deltaSeconds + "s");
+ if (reporter != null) {
+ reporter.handle(waitEvt);
+ }
+ }
+ }, 0, 1, TimeUnit.SECONDS);
+ }
- for (BuildEventTransport transport : transports) {
- shutdownFutures.add(transport.close());
- }
+ private void close() {
+ ScheduledExecutorService executor = null;
+ try {
+ executor = Executors.newSingleThreadScheduledExecutor();
+ List<ListenableFuture<Void>> closeFutures = new ArrayList<>(transports.size());
+ for (final BuildEventTransport transport : transports) {
+ ListenableFuture<Void> closeFuture = transport.close();
+ closeFuture.addListener(new Runnable() {
+ @Override
+ public void run() {
+ if (reporter != null) {
+ reporter.post(new BuildEventTransportClosedEvent(transport));
+ }
+ }
+ }, executor);
+ closeFutures.add(closeFuture);
+ }
- // Wait for all transports to close.
- for (Future<Void> f : shutdownFutures) {
try {
- f.get();
+ if (closeFutures.isEmpty()) {
+ // Don't spam events if there is nothing to close.
+ return;
+ }
+
+ ScheduledFuture<?> f = bepUploadWaitEvent(executor);
+ // Wait for all transports to close.
+ Futures.allAsList(closeFutures).get();
+ f.cancel(true);
} catch (Exception e) {
log.severe("Failed to close a build event transport: " + e);
}
+ } finally {
+ if (executor != null) {
+ executor.shutdown();
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java
index 983aeb38e5..846c8aa302 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java
@@ -108,7 +108,8 @@ public class BuildEventStreamerModule extends BlazeModule {
ImmutableSet<BuildEventTransport> buildEventTransports
= createFromOptions(besOptions, pathConverter);
if (!buildEventTransports.isEmpty()) {
- BuildEventStreamer streamer = new BuildEventStreamer(buildEventTransports);
+ BuildEventStreamer streamer = new BuildEventStreamer(buildEventTransports,
+ commandEnvironment != null ? commandEnvironment.getReporter() : null);
return Optional.of(streamer);
}
} catch (IOException e) {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
index ce182be035..fc5eca4344 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalEventHandler.java
@@ -13,6 +13,8 @@
// limitations under the License.
package com.google.devtools.build.lib.runtime;
+import static com.google.devtools.build.lib.util.Preconditions.checkState;
+
import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.Subscribe;
import com.google.common.primitives.Bytes;
@@ -22,6 +24,8 @@ import com.google.devtools.build.lib.actions.ActionStartedEvent;
import com.google.devtools.build.lib.actions.ActionStatusMessage;
import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent;
import com.google.devtools.build.lib.analysis.NoBuildEvent;
+import com.google.devtools.build.lib.buildeventstream.AnnounceBuildEventTransportsEvent;
+import com.google.devtools.build.lib.buildeventstream.BuildEventTransportClosedEvent;
import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
import com.google.devtools.build.lib.buildtool.buildevent.ExecutionProgressReceiverAvailableEvent;
@@ -85,6 +89,8 @@ public class ExperimentalEventHandler implements EventHandler {
private long mustRefreshAfterMillis;
private int numLinesProgressBar;
private boolean buildComplete;
+ // Number of open build even protocol transports.
+ private int openBepTransports;
private boolean progressBarNeedsRefresh;
private Thread updateThread;
private byte[] stdoutBuffer;
@@ -320,7 +326,7 @@ public class ExperimentalEventHandler implements EventHandler {
@Subscribe
public void buildComplete(BuildCompleteEvent event) {
// The final progress bar will flow into the scroll-back buffer, to if treat
- // it as an event and add a time stamp, if events are supposed to have a time stmap.
+ // it as an event and add a timestamp, if events are supposed to have a timestmap.
synchronized (this) {
if (showTimestamp) {
stateTracker.buildComplete(event, TIMESTAMP_FORMAT.print(clock.currentTimeMillis()));
@@ -329,10 +335,15 @@ public class ExperimentalEventHandler implements EventHandler {
}
ignoreRefreshLimitOnce();
refresh();
- buildComplete = true;
+
+ // After a build has completed, only stop updating the UI if there is no more BEP
+ // upload happening.
+ if (openBepTransports == 0) {
+ buildComplete = true;
+ stopUpdateThread();
+ flushStdOutStdErrBuffers();
+ }
}
- stopUpdateThread();
- flushStdOutStdErrBuffers();
}
@Subscribe
@@ -434,6 +445,28 @@ public class ExperimentalEventHandler implements EventHandler {
}
}
+ @Subscribe
+ public synchronized void buildEventTransportsAnnounced(AnnounceBuildEventTransportsEvent event) {
+ openBepTransports = event.transports().size();
+ stateTracker.buildEventTransportsAnnounced(event);
+ }
+
+ @Subscribe
+ public synchronized void buildEventTransportClosed(BuildEventTransportClosedEvent event) {
+ checkState(openBepTransports > 0);
+ openBepTransports--;
+ stateTracker.buildEventTransportClosed(event);
+
+ if (openBepTransports == 0) {
+ stopUpdateThread();
+ flushStdOutStdErrBuffers();
+ ignoreRefreshLimitOnce();
+ refresh();
+ } else {
+ refresh();
+ }
+ }
+
private void refresh() {
if (showProgress) {
progressBarNeedsRefresh = true;
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalStateTracker.java b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalStateTracker.java
index 73f32e493c..e81ce32e61 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalStateTracker.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/ExperimentalStateTracker.java
@@ -13,12 +13,17 @@
// limitations under the License.
package com.google.devtools.build.lib.runtime;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionCompletionEvent;
import com.google.devtools.build.lib.actions.ActionStartedEvent;
import com.google.devtools.build.lib.actions.ActionStatusMessage;
import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.buildeventstream.AnnounceBuildEventTransportsEvent;
+import com.google.devtools.build.lib.buildeventstream.BuildEventTransport;
+import com.google.devtools.build.lib.buildeventstream.BuildEventTransportClosedEvent;
import com.google.devtools.build.lib.buildtool.ExecutionProgressReceiver;
import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
@@ -36,7 +41,9 @@ import com.google.devtools.build.lib.util.io.PositionAwareAnsiTerminalWriter;
import com.google.devtools.build.lib.view.test.TestStatus.BlazeTestStatus;
import java.io.IOException;
import java.util.ArrayDeque;
+import java.util.Collections;
import java.util.Deque;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
@@ -93,10 +100,16 @@ class ExperimentalStateTracker {
private TestSummary mostRecentTest;
private int failedTests;
private boolean ok;
+ private boolean buildComplete;
private ExecutionProgressReceiver executionProgressReceiver;
private PackageProgressReceiver packageProgressReceiver;
+ // Set of build event protocol transports that need yet to be closed.
+ private Set<BuildEventTransport> bepOpenTransports = Collections.emptySet();
+ // The point in time when closing of BEP transports was started.
+ private long bepTransportClosingStartTimeMillis;
+
ExperimentalStateTracker(Clock clock, int targetWidth) {
this.runningActions = new ArrayDeque<>();
this.actions = new TreeMap<>();
@@ -167,6 +180,10 @@ class ExperimentalStateTracker {
}
void buildComplete(BuildCompleteEvent event, String additionalInfo) {
+ buildComplete = true;
+ // Build event protocol transports are closed right after the build complete event.
+ bepTransportClosingStartTimeMillis = clock.currentTimeMillis();
+
if (event.getResult().getSuccess()) {
status = "INFO";
if (failedTests == 0) {
@@ -307,6 +324,13 @@ class ExperimentalStateTracker {
return label.toString();
}
+ private String shortenedString(String s, int maxWidth) {
+ if (maxWidth <= 3 * ELLIPSIS.length() || s.length() <= maxWidth) {
+ return s;
+ }
+ return s.substring(0, maxWidth - ELLIPSIS.length()) + ELLIPSIS;
+ }
+
// Describe a group of actions running for the same test.
private String describeTestGroup(
Label owner, long nanoTime, int desiredWidth, Set<String> allActions) {
@@ -471,6 +495,14 @@ class ExperimentalStateTracker {
}
}
+ synchronized void buildEventTransportsAnnounced(AnnounceBuildEventTransportsEvent event) {
+ this.bepOpenTransports = new HashSet<BuildEventTransport>(event.transports());
+ }
+
+ synchronized void buildEventTransportClosed(BuildEventTransportClosedEvent event) {
+ bepOpenTransports.remove(event.transport());
+ }
+
/***
* Predicate indicating whether the contents of the progress bar can change, if the
* only thing that happens is that time passes; this is the case, e.g., if the progress
@@ -483,6 +515,9 @@ class ExperimentalStateTracker {
if (runningDownloads.size() >= 1) {
return true;
}
+ if (buildComplete && !bepOpenTransports.isEmpty()) {
+ return true;
+ }
if (status != null) {
return false;
}
@@ -606,6 +641,46 @@ class ExperimentalStateTracker {
}
}
+ /**
+ * Display any BEP transports that are still open after the build. Most likely, because
+ * uploading build events takes longer than the build itself.
+ */
+ private void maybeReportBepTransports(PositionAwareAnsiTerminalWriter terminalWriter)
+ throws IOException {
+ if (!buildComplete || bepOpenTransports.isEmpty()) {
+ return;
+ }
+ long sinceSeconds =
+ MILLISECONDS.toSeconds(clock.currentTimeMillis() - bepTransportClosingStartTimeMillis);
+ if (sinceSeconds == 0) {
+ // Special case for when bazel was interrupted, in which case we don't want to have
+ // a BEP upload message.
+ return;
+ }
+ int count = bepOpenTransports.size();
+ // Can just use targetWidth, because we always write to a new line
+ int maxWidth = targetWidth;
+
+ String waitMessage = "Waiting for build events upload: ";
+ String name = bepOpenTransports.iterator().next().name();
+ String line = waitMessage + name + " " + sinceSeconds + "s";
+
+ if (count == 1 && line.length() <= maxWidth) {
+ terminalWriter.newline().append(line);
+ } else if (count == 1) {
+ waitMessage = "Waiting for: ";
+ String waitSecs = " " + sinceSeconds + "s";
+ int maxNameWidth = maxWidth - waitMessage.length() - waitSecs.length();
+ terminalWriter.newline().append(waitMessage + shortenedString(name, maxNameWidth) + waitSecs);
+ } else {
+ terminalWriter.newline().append(waitMessage + sinceSeconds + "s");
+ for (BuildEventTransport transport : bepOpenTransports) {
+ name = " " + transport.name();
+ terminalWriter.newline().append(shortenedString(name, maxWidth));
+ }
+ }
+ }
+
synchronized void writeProgressBar(AnsiTerminalWriter rawTerminalWriter, boolean shortVersion)
throws IOException {
PositionAwareAnsiTerminalWriter terminalWriter =
@@ -626,6 +701,7 @@ class ExperimentalStateTracker {
}
if (!shortVersion) {
reportOnDownloads(terminalWriter);
+ maybeReportBepTransports(terminalWriter);
}
return;
}