Add color support for errors/warnings etc.
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ConsoleLogger.cs
blobb2bdfe7aba948a9617fc3698f4650f74b399351a
1 //
2 // ConsoleLogger.cs: Outputs to the console
3 //
4 // Author:
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 //
7 // (C) 2005 Marek Sieradzki
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 #if NET_2_0
30 using System;
31 using System.Runtime.InteropServices;
32 using System.Collections.Generic;
33 using System.IO;
34 using System.Security;
35 using System.Text;
36 using Microsoft.Build.Framework;
38 namespace Microsoft.Build.BuildEngine {
39 public class ConsoleLogger : ILogger {
41 string parameters;
42 int indent;
43 LoggerVerbosity verbosity;
44 WriteHandler writeHandler;
45 int errorCount;
46 int warningCount;
47 DateTime buildStart;
48 bool performanceSummary;
49 bool showSummary;
50 bool skipProjectStartedText;
51 List<string> errors, warnings;
52 bool projectFailed;
53 ConsoleColor errorColor, warningColor, eventColor, messageColor, highMessageColor;
54 ColorSetter colorSet;
55 ColorResetter colorReset;
56 bool no_message_color, no_colors;
58 public ConsoleLogger ()
59 : this (LoggerVerbosity.Normal, null, null, null)
63 public ConsoleLogger (LoggerVerbosity verbosity)
64 : this (LoggerVerbosity.Normal, null, null, null)
68 public ConsoleLogger (LoggerVerbosity verbosity,
69 WriteHandler write,
70 ColorSetter colorSet,
71 ColorResetter colorReset)
73 this.verbosity = verbosity;
74 this.indent = 0;
75 this.errorCount = 0;
76 this.warningCount = 0;
77 if (write == null)
78 this.writeHandler += new WriteHandler (WriteHandlerFunction);
79 else
80 this.writeHandler += write;
81 this.performanceSummary = false;
82 this.showSummary = true;
83 this.skipProjectStartedText = false;
84 errors = new List<string> ();
85 warnings = new List<string> ();
86 this.colorSet = colorSet;
87 this.colorReset = colorReset;
89 //defaults
90 errorColor = ConsoleColor.DarkRed;
91 warningColor = ConsoleColor.DarkYellow;
92 eventColor = ConsoleColor.DarkCyan;
93 messageColor = ConsoleColor.DarkGray;
94 highMessageColor = ConsoleColor.White;
96 // if message color is not set via the env var,
97 // then don't use any color for it.
98 no_message_color = true;
100 no_colors = true;
101 if (colorSet == null || colorReset == null)
102 return;
104 // color support
105 string config = Environment.GetEnvironmentVariable ("XBUILD_COLORS");
106 if (config != null && config != "disable") {
107 no_colors = false;
108 string [] pairs = config.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
109 foreach (string pair in pairs) {
110 string [] parts = pair.Split (new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
111 if (parts.Length != 2)
112 continue;
114 if (parts [0] == "errors")
115 TryParseConsoleColor (parts [1], ref errorColor);
116 else if (parts [0] == "warnings")
117 TryParseConsoleColor (parts [1], ref warningColor);
118 else if (parts [0] == "events")
119 TryParseConsoleColor (parts [1], ref eventColor);
120 else if (parts [0] == "messages") {
121 if (TryParseConsoleColor (parts [1], ref messageColor)) {
122 highMessageColor = GetBrightColorFor (messageColor);
123 no_message_color = false;
130 bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
132 switch (color_str.ToLower ()) {
133 case "black": color = ConsoleColor.Black; break;
135 case "blue": color = ConsoleColor.DarkBlue; break;
136 case "green": color = ConsoleColor.DarkGreen; break;
137 case "cyan": color = ConsoleColor.DarkCyan; break;
138 case "red": color = ConsoleColor.DarkRed; break;
139 case "magenta": color = ConsoleColor.DarkMagenta; break;
140 case "yellow": color = ConsoleColor.DarkYellow; break;
141 case "grey": color = ConsoleColor.DarkGray; break;
143 case "brightgrey": color = ConsoleColor.Gray; break;
144 case "brightblue": color = ConsoleColor.Blue; break;
145 case "brightgreen": color = ConsoleColor.Green; break;
146 case "brightcyan": color = ConsoleColor.Cyan; break;
147 case "brightred": color = ConsoleColor.Red; break;
148 case "brightmagenta": color = ConsoleColor.Magenta; break;
149 case "brightyellow": color = ConsoleColor.Yellow; break;
151 case "white":
152 case "brightwhite": color = ConsoleColor.White; break;
153 default: return false;
156 return true;
159 ConsoleColor GetBrightColorFor (ConsoleColor color)
161 switch (color) {
162 case ConsoleColor.DarkBlue: return ConsoleColor.Blue;
163 case ConsoleColor.DarkGreen: return ConsoleColor.Green;
164 case ConsoleColor.DarkCyan: return ConsoleColor.Cyan;
165 case ConsoleColor.DarkRed: return ConsoleColor.Red;
166 case ConsoleColor.DarkMagenta: return ConsoleColor.Magenta;
167 case ConsoleColor.DarkYellow: return ConsoleColor.Yellow;
168 case ConsoleColor.DarkGray: return ConsoleColor.Gray;
169 case ConsoleColor.Gray: return ConsoleColor.White;
171 default: return color;
175 public void ApplyParameter (string parameterName,
176 string parameterValue)
178 // FIXME: what we should do here? in msbuild it isn't
179 // changing "parameters" property
182 public virtual void Initialize (IEventSource eventSource)
184 eventSource.BuildStarted += new BuildStartedEventHandler (BuildStartedHandler);
185 eventSource.BuildFinished += new BuildFinishedEventHandler (BuildFinishedHandler);
186 eventSource.ProjectStarted += new ProjectStartedEventHandler (ProjectStartedHandler);
187 eventSource.ProjectFinished += new ProjectFinishedEventHandler (ProjectFinishedHandler);
188 eventSource.TargetStarted += new TargetStartedEventHandler (TargetStartedHandler);
189 eventSource.TargetFinished += new TargetFinishedEventHandler (TargetFinishedHandler);
190 eventSource.TaskStarted += new TaskStartedEventHandler (TaskStartedHandler);
191 eventSource.TaskFinished += new TaskFinishedEventHandler (TaskFinishedHandler);
192 eventSource.MessageRaised += new BuildMessageEventHandler (MessageHandler);
193 eventSource.WarningRaised += new BuildWarningEventHandler (WarningHandler);
194 eventSource.ErrorRaised += new BuildErrorEventHandler (ErrorHandler);
197 public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
199 WriteLine (String.Empty);
200 WriteLine (String.Format ("Build started {0}.", args.Timestamp));
201 WriteLine ("__________________________________________________");
202 buildStart = args.Timestamp;
205 public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
207 if (args.Succeeded == true && !projectFailed) {
208 WriteLine ("Build succeeded.");
209 } else {
210 WriteLine ("Build FAILED.");
212 if (performanceSummary == true) {
215 if (warnings.Count > 0) {
216 WriteLine (Environment.NewLine + "Warnings:");
217 SetColor (warningColor);
218 foreach (string warning in warnings)
219 WriteLine (warning);
220 ResetColor ();
221 WriteLine ("");
224 if (errors.Count > 0) {
225 WriteLine ("Errors:");
226 SetColor (errorColor);
227 foreach (string error in errors)
228 WriteLine (error);
229 ResetColor ();
232 if (showSummary == true){
233 TimeSpan timeElapsed = args.Timestamp - buildStart;
234 WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
235 WriteLine (String.Format ("\t {0} Error(s)", errorCount));
236 WriteLine (String.Empty);
237 WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
241 public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
243 SetColor (eventColor);
244 WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile, args.TargetNames));
245 ResetColor ();
246 WriteLine (String.Empty);
249 public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
251 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
252 if (indent == 1)
253 indent --;
254 SetColor (eventColor);
255 WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
256 args.Succeeded ? String.Empty : "-- FAILED"));
257 ResetColor ();
258 WriteLine (String.Empty);
260 if (!projectFailed)
261 // no project has failed yet, so update the flag
262 projectFailed = !args.Succeeded;
265 public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
267 indent++;
268 SetColor (eventColor);
269 WriteLine (String.Format ("Target {0}:",args.TargetName));
270 ResetColor ();
273 public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
275 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) || !args.Succeeded) {
276 SetColor (eventColor);
277 WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
278 args.TargetName, args.ProjectFile,
279 args.Succeeded ? String.Empty : "-- FAILED"));
280 ResetColor ();
282 indent--;
284 WriteLine (String.Empty);
287 public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
289 if (this.verbosity == LoggerVerbosity.Detailed) {
290 SetColor (eventColor);
291 WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
292 ResetColor ();
294 indent++;
297 public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
299 indent--;
300 if (this.verbosity == LoggerVerbosity.Detailed || !args.Succeeded) {
301 SetColor (eventColor);
302 if (args.Succeeded)
303 WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
304 else
305 WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
306 ResetColor ();
310 public void MessageHandler (object sender, BuildMessageEventArgs args)
312 if (IsMessageOk (args)) {
313 if (no_message_color) {
314 WriteLine (args.Message);
315 } else {
316 SetColor (args.Importance == MessageImportance.High ? highMessageColor : messageColor);
317 WriteLine (args.Message);
318 ResetColor ();
323 public void WarningHandler (object sender, BuildWarningEventArgs args)
325 string msg = FormatWarningEvent (args);
326 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
327 SetColor (warningColor);
328 WriteLineWithoutIndent (msg);
329 ResetColor ();
331 warnings.Add (msg);
332 warningCount++;
335 public void ErrorHandler (object sender, BuildErrorEventArgs args)
337 string msg = FormatErrorEvent (args);
338 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
339 SetColor (errorColor);
340 WriteLineWithoutIndent (msg);
341 ResetColor ();
343 errors.Add (msg);
344 errorCount++;
347 [MonoTODO]
348 public void CustomEventHandler (object sender, CustomBuildEventArgs args)
352 private void WriteLine (string message)
354 if (indent > 0) {
355 StringBuilder sb = new StringBuilder ();
356 for (int i = 0; i < indent; i++)
357 sb.Append ('\t');
358 sb.Append (message);
360 writeHandler (sb.ToString ());
361 } else {
362 writeHandler (message);
366 private void WriteLineWithoutIndent (string message)
368 writeHandler (message);
371 private void WriteHandlerFunction (string message)
373 Console.WriteLine (message);
376 void SetColor (ConsoleColor color)
378 if (!no_colors)
379 colorSet (color);
382 void ResetColor ()
384 if (!no_colors)
385 colorReset ();
388 private void ParseParameters ()
390 string[] splittedParameters = parameters.Split (';');
391 foreach (string s in splittedParameters ) {
392 switch (s) {
393 case "PerformanceSummary":
394 this.performanceSummary = true;
395 break;
396 case "NoSummary":
397 this.showSummary = false;
398 break;
399 default:
400 throw new ArgumentException ("Invalid parameter.");
405 public virtual void Shutdown ()
409 static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
411 private string FormatErrorEvent (BuildErrorEventArgs args)
413 // For some reason we get an 1-char empty string as Subcategory somtimes.
414 string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
415 string subcat = subprefix == "" ? "" : args.Subcategory;
417 if (args.LineNumber != 0){
418 if (args.ColumnNumber != 0 && !InEmacs)
419 return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
420 args.File, args.LineNumber, args.ColumnNumber,
421 subprefix, subcat, args.Code, args.Message);
423 return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
424 args.File, args.LineNumber,
425 subprefix, subcat, args.Code, args.Message);
426 } else {
427 return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
428 args.Message);
432 private string FormatWarningEvent (BuildWarningEventArgs args)
434 // For some reason we get an 1-char empty string as Subcategory somtimes.
435 string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
436 string subcat = subprefix == "" ? "" : args.Subcategory;
438 // FIXME: show more complicated args
439 if (args.LineNumber != 0){
441 if (args.ColumnNumber != 0 && !InEmacs) {
442 return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
443 args.File, args.LineNumber, args.ColumnNumber,
444 subprefix, subcat, args.Code, args.Message);
446 return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
447 args.File, args.LineNumber,
448 subprefix, subcat, args.Code, args.Message);
449 } else {
450 return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
451 args.Message);
455 private bool IsMessageOk (BuildMessageEventArgs bsea)
457 if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
458 return true;
459 } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
460 return true;
461 } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
462 return true;
463 } else
464 return false;
467 private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
469 if (v == LoggerVerbosity.Diagnostic) {
470 return LoggerVerbosity.Diagnostic <= verbosity;
471 } else if (v == LoggerVerbosity.Detailed) {
472 return LoggerVerbosity.Detailed <= verbosity;
473 } else if (v == LoggerVerbosity.Normal) {
474 return LoggerVerbosity.Normal <= verbosity;
475 } else if (v == LoggerVerbosity.Minimal) {
476 return LoggerVerbosity.Minimal <= verbosity;
477 } else if (v == LoggerVerbosity.Quiet) {
478 return true;
479 } else
480 return false;
483 public string Parameters {
484 get {
485 return parameters;
487 set {
488 if (value == null)
489 throw new ArgumentNullException ();
490 parameters = value;
491 if (parameters != String.Empty)
492 ParseParameters ();
496 public bool ShowSummary {
497 get { return showSummary; }
498 set { showSummary = value; }
501 public bool SkipProjectStartedText {
502 get { return skipProjectStartedText; }
503 set { skipProjectStartedText = value; }
506 public LoggerVerbosity Verbosity {
507 get { return verbosity; }
508 set { verbosity = value; }
511 protected WriteHandler WriteHandler {
512 get { return writeHandler; }
513 set { writeHandler = value; }
518 #endif