| // Copyright 2015 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.runtime; |
| |
| import com.google.common.eventbus.Subscribe; |
| import com.google.devtools.build.lib.actions.ActionCompletionEvent; |
| import com.google.devtools.build.lib.actions.ActionStartedEvent; |
| import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent; |
| 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; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.EventKind; |
| import com.google.devtools.build.lib.pkgcache.LoadingPhaseCompleteEvent; |
| import com.google.devtools.build.lib.util.io.AnsiTerminal; |
| import com.google.devtools.build.lib.util.io.LineCountingAnsiTerminalWriter; |
| import com.google.devtools.build.lib.util.io.LineWrappingAnsiTerminalWriter; |
| import com.google.devtools.build.lib.util.io.OutErr; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.util.logging.Logger; |
| |
| /** |
| * An experimental new output stream. |
| */ |
| public class ExperimentalEventHandler extends BlazeCommandEventHandler { |
| private static Logger LOG = Logger.getLogger(ExperimentalEventHandler.class.getName()); |
| |
| private final AnsiTerminal terminal; |
| private final boolean debugAllEvents; |
| private final ExperimentalStateTracker stateTracker; |
| private int numLinesProgressBar; |
| private boolean buildComplete; |
| private boolean progressBarNeedsRefresh; |
| |
| public final int terminalWidth; |
| |
| public ExperimentalEventHandler(OutErr outErr, BlazeCommandEventHandler.Options options) { |
| super(outErr, options); |
| this.terminal = new AnsiTerminal(outErr.getErrorStream()); |
| this.terminalWidth = (options.terminalColumns > 0 ? options.terminalColumns : 80); |
| this.debugAllEvents = options.experimentalUiDebugAllEvents; |
| this.stateTracker = new ExperimentalStateTracker(); |
| this.numLinesProgressBar = 0; |
| } |
| |
| @Override |
| public synchronized void handle(Event event) { |
| try { |
| if (debugAllEvents) { |
| // Debugging only: show all events visible to the new UI. |
| clearProgressBar(); |
| terminal.flush(); |
| outErr.getOutputStream().write((event + "\n").getBytes(StandardCharsets.UTF_8)); |
| outErr.getOutputStream().flush(); |
| addProgressBar(); |
| terminal.flush(); |
| } else { |
| switch (event.getKind()) { |
| case STDOUT: |
| case STDERR: |
| if (!buildComplete) { |
| clearProgressBar(); |
| terminal.flush(); |
| } |
| OutputStream stream = |
| event.getKind() == EventKind.STDOUT |
| ? outErr.getOutputStream() |
| : outErr.getErrorStream(); |
| stream.write(event.getMessageBytes()); |
| if (!buildComplete) { |
| stream.write(new byte[] {10, 13}); |
| } |
| stream.flush(); |
| if (!buildComplete) { |
| addProgressBar(); |
| terminal.flush(); |
| } |
| break; |
| case ERROR: |
| case WARNING: |
| case INFO: |
| clearProgressBar(); |
| setEventKindColor(event.getKind()); |
| terminal.writeString(event.getKind() + ": "); |
| terminal.resetTerminal(); |
| if (event.getLocation() != null) { |
| terminal.writeString(event.getLocation() + ": "); |
| } |
| if (event.getMessage() != null) { |
| terminal.writeString(event.getMessage()); |
| } |
| crlf(); |
| addProgressBar(); |
| terminal.flush(); |
| break; |
| } |
| } |
| } catch (IOException e) { |
| LOG.warning("IO Error writing to output stream: " + e); |
| } |
| } |
| |
| private void setEventKindColor(EventKind kind) throws IOException { |
| switch (kind) { |
| case ERROR: |
| terminal.textRed(); |
| terminal.textBold(); |
| break; |
| case WARNING: |
| terminal.textMagenta(); |
| break; |
| case INFO: |
| terminal.textGreen(); |
| break; |
| } |
| } |
| |
| @Subscribe |
| public void buildStarted(BuildStartingEvent event) { |
| stateTracker.buildStarted(event); |
| refresh(); |
| } |
| |
| @Subscribe |
| public void loadingComplete(LoadingPhaseCompleteEvent event) { |
| stateTracker.loadingComplete(event); |
| refresh(); |
| } |
| |
| @Subscribe |
| public void analysisComplete(AnalysisPhaseCompleteEvent event) { |
| stateTracker.analysisComplete(event); |
| refresh(); |
| } |
| |
| @Subscribe |
| public void progressReceiverAvailable(ExecutionProgressReceiverAvailableEvent event) { |
| stateTracker.progressReceiverAvailable(event); |
| } |
| |
| @Subscribe |
| public void buildComplete(BuildCompleteEvent event) { |
| stateTracker.buildComplete(event); |
| refresh(); |
| buildComplete = true; |
| } |
| |
| @Subscribe |
| public void actionStarted(ActionStartedEvent event) { |
| stateTracker.actionStarted(event); |
| refresh(); |
| } |
| |
| @Subscribe |
| public void actionCompletion(ActionCompletionEvent event) { |
| stateTracker.actionCompletion(event); |
| refresh(); |
| } |
| |
| private void refresh() { |
| progressBarNeedsRefresh = true; |
| doRefresh(); |
| } |
| |
| private synchronized void doRefresh() { |
| try { |
| if (progressBarNeedsRefresh) { |
| progressBarNeedsRefresh = false; |
| clearProgressBar(); |
| addProgressBar(); |
| terminal.flush(); |
| } |
| } catch (IOException e) { |
| LOG.warning("IO Error writing to output stream: " + e); |
| } |
| } |
| |
| public void resetTerminal() { |
| try { |
| terminal.resetTerminal(); |
| } catch (IOException e) { |
| LOG.warning("IO Error writing to user terminal: " + e); |
| } |
| } |
| |
| private void clearProgressBar() throws IOException { |
| for (int i = 0; i < numLinesProgressBar; i++) { |
| terminal.cr(); |
| terminal.cursorUp(1); |
| terminal.clearLine(); |
| } |
| numLinesProgressBar = 0; |
| } |
| |
| private void crlf() throws IOException { |
| terminal.cr(); |
| terminal.writeString("\n"); |
| } |
| |
| private void addProgressBar() throws IOException { |
| LineCountingAnsiTerminalWriter terminalWriter = new LineCountingAnsiTerminalWriter(terminal); |
| stateTracker.writeProgressBar( |
| new LineWrappingAnsiTerminalWriter(terminalWriter, terminalWidth - 1)); |
| terminalWriter.newline(); |
| numLinesProgressBar = terminalWriter.getWrittenLines(); |
| } |
| } |