blob: f1906fa00345bb16f532acbfb7f386ec35f69dc2 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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.
14package com.google.devtools.build.lib.runtime;
15
16import com.google.devtools.build.lib.events.Event;
17import com.google.devtools.build.lib.events.EventHandler;
18import com.google.devtools.build.lib.events.EventKind;
19import com.google.devtools.build.lib.events.Location;
20import com.google.devtools.build.lib.util.io.OutErr;
21import com.google.devtools.common.options.EnumConverter;
22import com.google.devtools.common.options.Option;
23import com.google.devtools.common.options.OptionsBase;
ccalvarin2eaa02e2017-04-17 23:37:46 +020024import com.google.devtools.common.options.OptionsParser.OptionUsageRestrictions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import java.io.IOException;
26import java.io.OutputStream;
27import java.io.PrintStream;
28import java.util.EnumSet;
29import java.util.Set;
Michajlo Matijkiw440fbc72015-08-04 21:03:25 +000030import java.util.logging.Level;
31import java.util.logging.Logger;
Klaus Aehlig0436ce02016-10-07 11:31:52 +000032import org.joda.time.format.DateTimeFormat;
33import org.joda.time.format.DateTimeFormatter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034
35/**
36 * BlazeCommandEventHandler: an event handler established for the duration of a
37 * single Blaze command.
38 */
39public class BlazeCommandEventHandler implements EventHandler {
40
Michajlo Matijkiw440fbc72015-08-04 21:03:25 +000041 private static final Logger LOG = Logger.getLogger(BlazeCommandEventHandler.class.getName());
42
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043 public enum UseColor { YES, NO, AUTO }
44 public enum UseCurses { YES, NO, AUTO }
45
46 public static class UseColorConverter extends EnumConverter<UseColor> {
47 public UseColorConverter() {
48 super(UseColor.class, "--color setting");
49 }
50 }
51
52 public static class UseCursesConverter extends EnumConverter<UseCurses> {
53 public UseCursesConverter() {
54 super(UseCurses.class, "--curses setting");
55 }
56 }
57
58 public static class Options extends OptionsBase {
59
ccalvarin2eaa02e2017-04-17 23:37:46 +020060 @Option(
61 name = "show_progress",
62 defaultValue = "true",
63 category = "verbosity",
64 help = "Display progress messages during a build."
65 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010066 public boolean showProgress;
67
ccalvarin2eaa02e2017-04-17 23:37:46 +020068 @Option(
69 name = "show_task_finish",
70 defaultValue = "false",
71 category = "verbosity",
72 help = "Display progress messages when tasks complete, not just when they start."
73 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074 public boolean showTaskFinish;
75
ccalvarin2eaa02e2017-04-17 23:37:46 +020076 @Option(
77 name = "show_progress_rate_limit",
78 defaultValue = "0.03", // A nice middle ground; snappy but not too spammy in logs.
79 category = "verbosity",
80 help = "Minimum number of seconds between progress messages in the output."
81 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010082 public double showProgressRateLimit;
83
ccalvarin2eaa02e2017-04-17 23:37:46 +020084 @Option(
85 name = "color",
86 defaultValue = "auto",
87 converter = UseColorConverter.class,
88 category = "verbosity",
89 help = "Use terminal controls to colorize output."
90 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091 public UseColor useColorEnum;
92
ccalvarin2eaa02e2017-04-17 23:37:46 +020093 @Option(
94 name = "curses",
95 defaultValue = "auto",
96 converter = UseCursesConverter.class,
97 category = "verbosity",
98 help = "Use terminal cursor controls to minimize scrolling output"
99 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 public UseCurses useCursesEnum;
101
ccalvarin2eaa02e2017-04-17 23:37:46 +0200102 @Option(
103 name = "terminal_columns",
104 defaultValue = "80",
105 optionUsageRestrictions = OptionUsageRestrictions.HIDDEN,
106 help = "A system-generated parameter which specifies the terminal width in columns."
107 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 public int terminalColumns;
109
ccalvarin2eaa02e2017-04-17 23:37:46 +0200110 @Option(
111 name = "isatty",
112 defaultValue = "false",
113 optionUsageRestrictions = OptionUsageRestrictions.HIDDEN,
114 help =
115 "A system-generated parameter which is used to notify the "
116 + "server whether this client is running in a terminal. "
117 + "If this is set to false, then '--color=auto' will be treated as '--color=no'. "
118 + "If this is set to true, then '--color=auto' will be treated as '--color=yes'."
119 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 public boolean isATty;
121
122 // This lives here (as opposed to the more logical BuildRequest.Options)
123 // because the client passes it to the server *always*. We don't want the
124 // client to have to figure out when it should or shouldn't to send it.
ccalvarin2eaa02e2017-04-17 23:37:46 +0200125 @Option(
126 name = "emacs",
127 defaultValue = "false",
128 optionUsageRestrictions = OptionUsageRestrictions.UNDOCUMENTED,
129 help =
130 "A system-generated parameter which is true iff EMACS=t or INSIDE_EMACS is set "
131 + "in the environment of the client. This option controls certain display "
132 + "features."
133 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100134 public boolean runningInEmacs;
135
ccalvarin2eaa02e2017-04-17 23:37:46 +0200136 @Option(
137 name = "show_timestamps",
138 defaultValue = "false",
139 category = "verbosity",
140 help = "Include timestamps in messages"
141 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100142 public boolean showTimestamp;
143
ccalvarin2eaa02e2017-04-17 23:37:46 +0200144 @Option(
145 name = "progress_in_terminal_title",
146 defaultValue = "false",
147 category = "verbosity",
148 help =
149 "Show the command progress in the terminal title. "
150 + "Useful to see what blaze is doing when having multiple terminal tabs."
151 )
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100152 public boolean progressInTermTitle;
153
ccalvarin2eaa02e2017-04-17 23:37:46 +0200154 @Option(
155 name = "experimental_external_repositories",
156 defaultValue = "false",
157 category = "verbosity",
158 help = "Use external repositories for improved stability and speed when available."
159 )
Googler2f95be32015-03-20 17:26:11 +0000160 public boolean externalRepositories;
Michajlo Matijkiwc0770d92015-07-31 17:58:35 +0000161
ccalvarin2eaa02e2017-04-17 23:37:46 +0200162 @Option(
163 name = "force_experimental_external_repositories",
164 defaultValue = "false",
165 category = "verbosity",
166 help = "Forces --experimental_external_repositories."
167 )
Googler2f95be32015-03-20 17:26:11 +0000168 public boolean forceExternalRepositories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169
Ulf Adamsd6347a92016-02-24 13:23:54 +0000170 @Option(
171 name = "experimental_ui",
172 defaultValue = "false",
Ulf Adams9934f152016-06-22 12:04:40 +0000173 category = "verbosity",
ccalvarin2eaa02e2017-04-17 23:37:46 +0200174 help =
175 "Switches to an alternative progress bar that more explicitly shows progress, such "
176 + "as loaded packages and executed actions."
177 )
Ulf Adamsd6347a92016-02-24 13:23:54 +0000178 public boolean experimentalUi;
179
Klaus Aehlig9d068c02016-02-24 16:09:07 +0000180 @Option(
181 name = "experimental_ui_debug_all_events",
182 defaultValue = "false",
ccalvarin2eaa02e2017-04-17 23:37:46 +0200183 optionUsageRestrictions = OptionUsageRestrictions.HIDDEN,
Klaus Aehlig9d068c02016-02-24 16:09:07 +0000184 help = "Report all events known to the experimental new Bazel UI."
185 )
186 public boolean experimentalUiDebugAllEvents;
187
Klaus Aehligcd211ee2016-06-21 08:44:15 +0000188 @Option(
189 name = "experimental_ui_actions_shown",
190 defaultValue = "3",
Klaus Aehlig6c07f622016-06-27 15:15:52 +0000191 category = "verbosity",
192 help =
193 "Number of concurrent actions shown in the alternative progress bar; each "
194 + "action is shown on a separate line. The alternative progress bar always shows "
195 + "at least one one, all numbers less than 1 are mapped to 1. "
196 + "This option has no effect unless --experimental_ui is set."
Klaus Aehligcd211ee2016-06-21 08:44:15 +0000197 )
198 public int experimentalUiActionsShown;
Klaus Aehlig9d068c02016-02-24 16:09:07 +0000199
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 public boolean useColor() {
201 return useColorEnum == UseColor.YES || (useColorEnum == UseColor.AUTO && isATty);
202 }
203
204 public boolean useCursorControl() {
205 return useCursesEnum == UseCurses.YES || (useCursesEnum == UseCurses.AUTO && isATty);
206 }
207 }
208
209 private static final DateTimeFormatter TIMESTAMP_FORMAT =
210 DateTimeFormat.forPattern("(MM-dd HH:mm:ss.SSS) ");
211
212 protected final OutErr outErr;
213
214 private final PrintStream errPrintStream;
215
216 protected final Set<EventKind> eventMask =
217 EnumSet.copyOf(EventKind.ERRORS_WARNINGS_AND_INFO_AND_OUTPUT);
218
219 protected final boolean showTimestamp;
220
221 public BlazeCommandEventHandler(OutErr outErr, Options eventOptions) {
222 this.outErr = outErr;
223 this.errPrintStream = new PrintStream(outErr.getErrorStream(), true);
224 if (eventOptions.showProgress) {
225 eventMask.add(EventKind.PROGRESS);
226 eventMask.add(EventKind.START);
227 } else {
228 // Skip PASS events if --noshow_progress is requested.
229 eventMask.remove(EventKind.PASS);
230 }
231 if (eventOptions.showTaskFinish) {
232 eventMask.add(EventKind.FINISH);
233 }
234 eventMask.add(EventKind.SUBCOMMAND);
235 this.showTimestamp = eventOptions.showTimestamp;
236 }
237
238 /** See EventHandler.handle. */
239 @Override
240 public void handle(Event event) {
241 if (!eventMask.contains(event.getKind())) {
242 return;
243 }
244 String prefix;
245 switch (event.getKind()) {
246 case STDOUT:
247 putOutput(outErr.getOutputStream(), event);
248 return;
249 case STDERR:
250 putOutput(outErr.getErrorStream(), event);
251 return;
252 case PASS:
253 case FAIL:
254 case TIMEOUT:
255 case ERROR:
256 case WARNING:
257 case DEPCHECKER:
258 prefix = event.getKind() + ": ";
259 break;
260 case SUBCOMMAND:
261 prefix = ">>>>>>>>> ";
262 break;
263 case INFO:
264 case PROGRESS:
265 case START:
266 case FINISH:
267 prefix = "____";
268 break;
269 default:
270 throw new IllegalStateException("" + event.getKind());
271 }
272 StringBuilder buf = new StringBuilder();
273 buf.append(prefix);
274
275 if (showTimestamp) {
276 buf.append(timestamp());
277 }
278
279 Location location = event.getLocation();
280 if (location != null) {
281 buf.append(location.print()).append(": ");
282 }
283
284 buf.append(event.getMessage());
285 if (event.getKind() == EventKind.FINISH) {
286 buf.append(" DONE");
287 }
288
289 // Add a trailing period for ERROR and WARNING messages, which are
290 // typically English sentences composed from exception messages.
291 if (event.getKind() == EventKind.WARNING ||
292 event.getKind() == EventKind.ERROR) {
293 buf.append('.');
294 }
295
296 // Event messages go to stderr; results (e.g. 'blaze query') go to stdout.
297 errPrintStream.println(buf);
298 }
299
300 private void putOutput(OutputStream out, Event event) {
301 try {
302 out.write(event.getMessageBytes());
303 out.flush();
304 } catch (IOException e) {
Michajlo Matijkiwdda04e22015-08-12 18:16:58 +0000305 // This can happen in server mode if the blaze client has exited, or if output is redirected
306 // to a file and the disk is full, etc. May be moot in the case of full disk, or useful in
307 // the case of real bug in our handling of streams.
308 LOG.log(Level.WARNING, "Failed to write event", e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100309 }
310 }
311
312 /**
313 * @return a string representing the current time, eg "04-26 13:47:32.124".
314 */
315 protected String timestamp() {
316 return TIMESTAMP_FORMAT.print(System.currentTimeMillis());
317 }
318}