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
.Generic
;
34 using System
.Security
;
36 using Microsoft
.Build
.Framework
;
38 namespace Microsoft
.Build
.BuildEngine
{
39 public class ConsoleLogger
: ILogger
{
43 LoggerVerbosity verbosity
;
44 WriteHandler writeHandler
;
48 bool performanceSummary
;
50 bool skipProjectStartedText
;
51 List
<string> errors
, warnings
;
53 ConsoleColor errorColor
, warningColor
, eventColor
, messageColor
, highMessageColor
;
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
,
71 ColorResetter colorReset
)
73 this.verbosity
= verbosity
;
76 this.warningCount
= 0;
78 this.writeHandler
+= new WriteHandler (WriteHandlerFunction
);
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
;
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;
101 if (colorSet
== null || colorReset
== null)
105 string config
= Environment
.GetEnvironmentVariable ("XBUILD_COLORS");
106 if (config
!= null && config
!= "disable") {
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)
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;
152 case "brightwhite": color
= ConsoleColor
.White
; break;
153 default: return false;
159 ConsoleColor
GetBrightColorFor (ConsoleColor 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.");
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
)
224 if (errors
.Count
> 0) {
225 WriteLine ("Errors:");
226 SetColor (errorColor
);
227 foreach (string error
in errors
)
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
));
246 WriteLine (String
.Empty
);
249 public void ProjectFinishedHandler (object sender
, ProjectFinishedEventArgs args
)
251 if (IsVerbosityGreaterOrEqual (LoggerVerbosity
.Normal
)) {
254 SetColor (eventColor
);
255 WriteLine (String
.Format ("Done building project \"{0}\".{1}", args
.ProjectFile
,
256 args
.Succeeded
? String
.Empty
: "-- FAILED"));
258 WriteLine (String
.Empty
);
261 // no project has failed yet, so update the flag
262 projectFailed
= !args
.Succeeded
;
265 public void TargetStartedHandler (object sender
, TargetStartedEventArgs args
)
268 SetColor (eventColor
);
269 WriteLine (String
.Format ("Target {0}:",args
.TargetName
));
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"));
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
));
297 public void TaskFinishedHandler (object sender
, TaskFinishedEventArgs args
)
300 if (this.verbosity
== LoggerVerbosity
.Detailed
|| !args
.Succeeded
) {
301 SetColor (eventColor
);
303 WriteLine (String
.Format ("Done executing task \"{0}\"", args
.TaskName
));
305 WriteLine (String
.Format ("Task \"{0}\" execution -- FAILED", args
.TaskName
));
310 public void MessageHandler (object sender
, BuildMessageEventArgs args
)
312 if (IsMessageOk (args
)) {
313 if (no_message_color
) {
314 WriteLine (args
.Message
);
316 SetColor (args
.Importance
== MessageImportance
.High
? highMessageColor
: messageColor
);
317 WriteLine (args
.Message
);
323 public void WarningHandler (object sender
, BuildWarningEventArgs args
)
325 string msg
= FormatWarningEvent (args
);
326 if (IsVerbosityGreaterOrEqual (LoggerVerbosity
.Normal
)) {
327 SetColor (warningColor
);
328 WriteLineWithoutIndent (msg
);
335 public void ErrorHandler (object sender
, BuildErrorEventArgs args
)
337 string msg
= FormatErrorEvent (args
);
338 if (IsVerbosityGreaterOrEqual (LoggerVerbosity
.Minimal
)) {
339 SetColor (errorColor
);
340 WriteLineWithoutIndent (msg
);
348 public void CustomEventHandler (object sender
, CustomBuildEventArgs args
)
352 private void WriteLine (string message
)
355 StringBuilder sb
= new StringBuilder ();
356 for (int i
= 0; i
< indent
; i
++)
360 writeHandler (sb
.ToString ());
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
)
388 private void ParseParameters ()
390 string[] splittedParameters
= parameters
.Split (';');
391 foreach (string s
in splittedParameters
) {
393 case "PerformanceSummary":
394 this.performanceSummary
= true;
397 this.showSummary
= false;
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
);
427 return String
.Format ("{0}: {1}{2}error {3}: {4}", args
.File
, subprefix
, subcat
, args
.Code
,
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
);
450 return String
.Format ("{0}: {1} warning {2}: {3}", args
.File
, args
.Subcategory
, args
.Code
,
455 private bool IsMessageOk (BuildMessageEventArgs bsea
)
457 if (bsea
.Importance
== MessageImportance
.High
&& IsVerbosityGreaterOrEqual (LoggerVerbosity
.Minimal
)) {
459 } else if (bsea
.Importance
== MessageImportance
.Normal
&& IsVerbosityGreaterOrEqual (LoggerVerbosity
.Normal
)) {
461 } else if (bsea
.Importance
== MessageImportance
.Low
&& IsVerbosityGreaterOrEqual (LoggerVerbosity
.Detailed
)) {
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
) {
483 public string Parameters
{
489 throw new ArgumentNullException ();
491 if (parameters
!= String
.Empty
)
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; }