2 // ConsoleLogger.cs: Outputs to the console
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
7 // (C) 2005 Marek Sieradzki
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.
31 using System.Runtime.InteropServices;
32 using System.Collections;
33 using System.Collections.Generic;
35 using System.Security;
37 using Microsoft.Build.Framework;
39 namespace Microsoft.Build.BuildEngine {
40 public class ConsoleLogger : ILogger {
44 LoggerVerbosity verbosity;
45 WriteHandler writeHandler;
49 bool performanceSummary;
51 bool skipProjectStartedText;
52 List<string> errors, warnings;
54 ConsoleColor errorColor, warningColor, eventColor, messageColor, highMessageColor;
56 ColorResetter colorReset;
57 bool no_message_color, use_colors;
59 List<BuildStatusEventArgs> events;
60 Dictionary<string, List<string>> errorsTable;
61 Dictionary<string, List<string>> warningsTable;
62 string current_events_string;
64 public ConsoleLogger ()
65 : this (LoggerVerbosity.Normal, null, null, null)
69 public ConsoleLogger (LoggerVerbosity verbosity)
70 : this (LoggerVerbosity.Normal, null, null, null)
74 public ConsoleLogger (LoggerVerbosity verbosity,
77 ColorResetter colorReset)
79 this.verbosity = verbosity;
82 this.warningCount = 0;
84 this.writeHandler += new WriteHandler (WriteHandlerFunction);
86 this.writeHandler += write;
87 this.performanceSummary = false;
88 this.showSummary = true;
89 this.skipProjectStartedText = false;
90 errors = new List<string> ();
91 warnings = new List<string> ();
92 this.colorSet = colorSet;
93 this.colorReset = colorReset;
95 events = new List<BuildStatusEventArgs> ();
96 errorsTable = new Dictionary<string, List<string>> ();
97 warningsTable = new Dictionary<string, List<string>> ();
100 errorColor = ConsoleColor.DarkRed;
101 warningColor = ConsoleColor.DarkYellow;
102 eventColor = ConsoleColor.DarkCyan;
103 messageColor = ConsoleColor.DarkGray;
104 highMessageColor = ConsoleColor.White;
106 // if message color is not set via the env var,
107 // then don't use any color for it.
108 no_message_color = true;
111 if (colorSet == null || colorReset == null)
115 string config = Environment.GetEnvironmentVariable ("XBUILD_COLORS");
116 if (config == null) {
121 if (config == "disable")
125 string [] pairs = config.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
126 foreach (string pair in pairs) {
127 string [] parts = pair.Split (new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
128 if (parts.Length != 2)
131 if (parts [0] == "errors")
132 TryParseConsoleColor (parts [1], ref errorColor);
133 else if (parts [0] == "warnings")
134 TryParseConsoleColor (parts [1], ref warningColor);
135 else if (parts [0] == "events")
136 TryParseConsoleColor (parts [1], ref eventColor);
137 else if (parts [0] == "messages") {
138 if (TryParseConsoleColor (parts [1], ref messageColor)) {
139 highMessageColor = GetBrightColorFor (messageColor);
140 no_message_color = false;
146 bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
148 switch (color_str.ToLower ()) {
149 case "black": color = ConsoleColor.Black; break;
151 case "blue": color = ConsoleColor.DarkBlue; break;
152 case "green": color = ConsoleColor.DarkGreen; break;
153 case "cyan": color = ConsoleColor.DarkCyan; break;
154 case "red": color = ConsoleColor.DarkRed; break;
155 case "magenta": color = ConsoleColor.DarkMagenta; break;
156 case "yellow": color = ConsoleColor.DarkYellow; break;
157 case "grey": color = ConsoleColor.DarkGray; break;
159 case "brightgrey": color = ConsoleColor.Gray; break;
160 case "brightblue": color = ConsoleColor.Blue; break;
161 case "brightgreen": color = ConsoleColor.Green; break;
162 case "brightcyan": color = ConsoleColor.Cyan; break;
163 case "brightred": color = ConsoleColor.Red; break;
164 case "brightmagenta": color = ConsoleColor.Magenta; break;
165 case "brightyellow": color = ConsoleColor.Yellow; break;
168 case "brightwhite": color = ConsoleColor.White; break;
169 default: return false;
175 ConsoleColor GetBrightColorFor (ConsoleColor color)
178 case ConsoleColor.DarkBlue: return ConsoleColor.Blue;
179 case ConsoleColor.DarkGreen: return ConsoleColor.Green;
180 case ConsoleColor.DarkCyan: return ConsoleColor.Cyan;
181 case ConsoleColor.DarkRed: return ConsoleColor.Red;
182 case ConsoleColor.DarkMagenta: return ConsoleColor.Magenta;
183 case ConsoleColor.DarkYellow: return ConsoleColor.Yellow;
184 case ConsoleColor.DarkGray: return ConsoleColor.Gray;
185 case ConsoleColor.Gray: return ConsoleColor.White;
187 default: return color;
191 public void ApplyParameter (string parameterName,
192 string parameterValue)
194 // FIXME: what we should do here? in msbuild it isn't
195 // changing "parameters" property
198 public virtual void Initialize (IEventSource eventSource)
200 eventSource.BuildStarted += new BuildStartedEventHandler (BuildStartedHandler);
201 eventSource.BuildFinished += new BuildFinishedEventHandler (BuildFinishedHandler);
202 eventSource.ProjectStarted += new ProjectStartedEventHandler (ProjectStartedHandler);
203 eventSource.ProjectFinished += new ProjectFinishedEventHandler (ProjectFinishedHandler);
204 eventSource.TargetStarted += new TargetStartedEventHandler (TargetStartedHandler);
205 eventSource.TargetFinished += new TargetFinishedEventHandler (TargetFinishedHandler);
206 eventSource.TaskStarted += new TaskStartedEventHandler (TaskStartedHandler);
207 eventSource.TaskFinished += new TaskFinishedEventHandler (TaskFinishedHandler);
208 eventSource.MessageRaised += new BuildMessageEventHandler (MessageHandler);
209 eventSource.WarningRaised += new BuildWarningEventHandler (WarningHandler);
210 eventSource.ErrorRaised += new BuildErrorEventHandler (ErrorHandler);
213 public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
215 WriteLine (String.Empty);
216 WriteLine (String.Format ("Build started {0}.", args.Timestamp));
217 WriteLine ("__________________________________________________");
218 buildStart = args.Timestamp;
223 public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
225 if (args.Succeeded == true && !projectFailed) {
226 WriteLine ("Build succeeded.");
228 WriteLine ("Build FAILED.");
230 if (performanceSummary == true) {
233 if (warnings.Count > 0) {
234 WriteLine (Environment.NewLine + "Warnings:");
235 SetColor (warningColor);
237 WriteLine (String.Empty);
238 foreach (KeyValuePair<string, List<string>> pair in warningsTable) {
239 if (!String.IsNullOrEmpty (pair.Key))
240 WriteLine (pair.Key);
242 string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
243 foreach (string msg in pair.Value)
244 WriteLine (String.Format ("{0}{1}", indent_str, msg));
246 WriteLine (String.Empty);
252 if (errors.Count > 0) {
253 WriteLine ("Errors:");
254 SetColor (errorColor);
256 WriteLine (String.Empty);
257 foreach (KeyValuePair<string, List<string>> pair in errorsTable) {
258 if (!String.IsNullOrEmpty (pair.Key))
259 WriteLine (pair.Key);
261 string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
262 foreach (string msg in pair.Value)
263 WriteLine (String.Format ("{0}{1}", indent_str, msg));
265 WriteLine (String.Empty);
270 if (showSummary == true){
271 TimeSpan timeElapsed = args.Timestamp - buildStart;
272 WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
273 WriteLine (String.Format ("\t {0} Error(s)", errorCount));
274 WriteLine (String.Empty);
275 WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
280 public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
282 SetColor (eventColor);
283 WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile,
284 String.IsNullOrEmpty (args.TargetNames) ? "default" : args.TargetNames));
286 WriteLine (String.Empty);
287 DumpProperties (args.Properties);
288 DumpItems (args.Items);
292 public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
294 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
297 SetColor (eventColor);
298 WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
299 args.Succeeded ? String.Empty : "-- FAILED"));
301 WriteLine (String.Empty);
304 // no project has failed yet, so update the flag
305 projectFailed = !args.Succeeded;
310 public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
313 SetColor (eventColor);
314 WriteLine (String.Format ("Target {0}:",args.TargetName));
319 public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
321 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) || !args.Succeeded) {
322 SetColor (eventColor);
323 WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
324 args.TargetName, args.ProjectFile,
325 args.Succeeded ? String.Empty : "-- FAILED"));
330 WriteLine (String.Empty);
334 public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
336 if (this.verbosity == LoggerVerbosity.Detailed) {
337 SetColor (eventColor);
338 WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
345 public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
348 if (this.verbosity == LoggerVerbosity.Detailed || !args.Succeeded) {
349 SetColor (eventColor);
351 WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
353 WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
359 public void MessageHandler (object sender, BuildMessageEventArgs args)
361 if (IsMessageOk (args)) {
362 if (no_message_color) {
363 WriteLine (args.Message);
365 SetColor (args.Importance == MessageImportance.High ? highMessageColor : messageColor);
366 WriteLine (args.Message);
372 public void WarningHandler (object sender, BuildWarningEventArgs args)
374 string msg = FormatWarningEvent (args);
375 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
376 SetColor (warningColor);
377 WriteLineWithoutIndent (msg);
382 List<string> list = null;
383 if (!warningsTable.TryGetValue (EventsAsString, out list))
384 warningsTable [EventsAsString] = list = new List<string> ();
390 public void ErrorHandler (object sender, BuildErrorEventArgs args)
392 string msg = FormatErrorEvent (args);
393 if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
394 SetColor (errorColor);
395 WriteLineWithoutIndent (msg);
400 List<string> list = null;
401 if (!errorsTable.TryGetValue (EventsAsString, out list))
402 errorsTable [EventsAsString] = list = new List<string> ();
408 public void CustomEventHandler (object sender, CustomBuildEventArgs args)
412 private void WriteLine (string message)
415 StringBuilder sb = new StringBuilder ();
416 for (int i = 0; i < indent; i++)
420 writeHandler (sb.ToString ());
422 writeHandler (message);
426 void PushEvent (BuildStatusEventArgs args)
429 current_events_string = null;
434 events.RemoveAt (events.Count - 1);
435 current_events_string = null;
438 string EventsToString ()
440 StringBuilder sb = new StringBuilder ();
442 for (int i = 0; i < events.Count; i ++) {
443 BuildStatusEventArgs args = events [i];
444 ProjectStartedEventArgs pargs = args as ProjectStartedEventArgs;
446 sb.AppendFormat ("{0} ({1}) ->\n", pargs.ProjectFile,
447 String.IsNullOrEmpty (pargs.TargetNames) ?
453 TargetStartedEventArgs targs = args as TargetStartedEventArgs;
455 sb.AppendFormat ("({0} target) ->\n", targs.TargetName);
458 return sb.ToString ();
461 private void WriteLineWithoutIndent (string message)
463 writeHandler (message);
466 private void WriteHandlerFunction (string message)
468 Console.WriteLine (message);
471 void SetColor (ConsoleColor color)
483 private void ParseParameters ()
485 string[] splittedParameters = parameters.Split (';');
486 foreach (string s in splittedParameters ) {
488 case "PerformanceSummary":
489 this.performanceSummary = true;
492 this.showSummary = false;
495 throw new ArgumentException ("Invalid parameter.");
500 public virtual void Shutdown ()
504 static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
506 private string FormatErrorEvent (BuildErrorEventArgs args)
508 // For some reason we get an 1-char empty string as Subcategory somtimes.
509 string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
510 string subcat = subprefix == "" ? "" : args.Subcategory;
512 if (args.LineNumber != 0){
513 if (args.ColumnNumber != 0 && !InEmacs)
514 return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
515 args.File, args.LineNumber, args.ColumnNumber,
516 subprefix, subcat, args.Code, args.Message);
518 return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
519 args.File, args.LineNumber,
520 subprefix, subcat, args.Code, args.Message);
522 return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
527 private string FormatWarningEvent (BuildWarningEventArgs args)
529 // For some reason we get an 1-char empty string as Subcategory somtimes.
530 string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
531 string subcat = subprefix == "" ? "" : args.Subcategory;
533 // FIXME: show more complicated args
534 if (args.LineNumber != 0){
536 if (args.ColumnNumber != 0 && !InEmacs) {
537 return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
538 args.File, args.LineNumber, args.ColumnNumber,
539 subprefix, subcat, args.Code, args.Message);
541 return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
542 args.File, args.LineNumber,
543 subprefix, subcat, args.Code, args.Message);
545 return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
550 private bool IsMessageOk (BuildMessageEventArgs bsea)
552 if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
554 } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
556 } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
562 private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
564 if (v == LoggerVerbosity.Diagnostic) {
565 return LoggerVerbosity.Diagnostic <= verbosity;
566 } else if (v == LoggerVerbosity.Detailed) {
567 return LoggerVerbosity.Detailed <= verbosity;
568 } else if (v == LoggerVerbosity.Normal) {
569 return LoggerVerbosity.Normal <= verbosity;
570 } else if (v == LoggerVerbosity.Minimal) {
571 return LoggerVerbosity.Minimal <= verbosity;
572 } else if (v == LoggerVerbosity.Quiet) {
578 void DumpProperties (IEnumerable properties)
580 if (!IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
583 SetColor (eventColor);
585 WriteLine ("Initial Properties:");
588 if (properties == null)
591 var dict = new SortedDictionary<string, string> ();
592 foreach (DictionaryEntry de in properties)
593 dict [(string)de.Key] = (string)de.Value;
595 foreach (KeyValuePair<string, string> pair in dict)
596 WriteLine (String.Format ("{0} = {1}", pair.Key, pair.Value));
600 void DumpItems (IEnumerable items)
602 if (!IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic) || items == null)
605 SetColor (eventColor);
607 WriteLine ("Initial Items:");
612 var items_table = new SortedDictionary<string, List<ITaskItem>> ();
613 foreach (DictionaryEntry de in items) {
614 string key = (string)de.Key;
615 if (!items_table.ContainsKey (key))
616 items_table [key] = new List<ITaskItem> ();
618 items_table [key].Add ((ITaskItem) de.Value);
621 foreach (string name in items_table.Keys) {
624 foreach (ITaskItem item in items_table [name])
625 WriteLine (item.ItemSpec);
631 public string Parameters {
637 throw new ArgumentNullException ();
639 if (parameters != String.Empty)
644 string EventsAsString {
646 if (current_events_string == null)
647 current_events_string = EventsToString ();
648 return current_events_string;
652 public bool ShowSummary {
653 get { return showSummary; }
654 set { showSummary = value; }
657 public bool SkipProjectStartedText {
658 get { return skipProjectStartedText; }
659 set { skipProjectStartedText = value; }
662 public LoggerVerbosity Verbosity {
663 get { return verbosity; }
664 set { verbosity = value; }
667 protected WriteHandler WriteHandler {
668 get { return writeHandler; }
669 set { writeHandler = value; }