[xbuild] ConsoleLogger - parse params only on Initialize.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / ConsoleLogger.cs
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
27
28 #if NET_2_0
29
30 using System;
31 using System.Runtime.InteropServices;
32 using System.Collections;
33 using System.Collections.Generic;
34 using System.IO;
35 using System.Linq;
36 using System.Security;
37 using System.Text;
38 using Microsoft.Build.Framework;
39
40 namespace Microsoft.Build.BuildEngine {
41         public class ConsoleLogger : ILogger {
42         
43                 string          parameters;
44                 int             indent;
45                 LoggerVerbosity verbosity;
46                 WriteHandler    writeHandler;
47                 int             errorCount;
48                 int             warningCount;
49                 DateTime                buildStart;
50                 bool            performanceSummary;
51                 bool            showSummary;
52                 bool            skipProjectStartedText;
53                 List<string> errors, warnings;
54                 bool            projectFailed;
55                 ConsoleColor errorColor, warningColor, eventColor, messageColor, highMessageColor;
56                 ColorSetter colorSet;
57                 ColorResetter colorReset;
58                 IEventSource eventSource;
59                 bool no_message_color, use_colors;
60                 bool noItemAndPropertyList;
61
62                 List<BuildEvent> events;
63                 Dictionary<string, List<string>> errorsTable;
64                 Dictionary<string, List<string>> warningsTable;
65                 SortedDictionary<string, PerfInfo> targetPerfTable, tasksPerfTable;
66                 string current_events_string;
67                 
68                 public ConsoleLogger ()
69                         : this (LoggerVerbosity.Normal, null, null, null)
70                 {
71                 }
72
73                 public ConsoleLogger (LoggerVerbosity verbosity)
74                         : this (LoggerVerbosity.Normal, null, null, null)
75                 {
76                 }
77                 
78                 public ConsoleLogger (LoggerVerbosity verbosity,
79                                       WriteHandler write,
80                                       ColorSetter colorSet,
81                                       ColorResetter colorReset)
82                 {
83                         this.verbosity = verbosity;
84                         this.indent = 0;
85                         this.errorCount = 0;
86                         this.warningCount = 0;
87                         if (write == null)
88                                 this.writeHandler += new WriteHandler (WriteHandlerFunction);
89                         else
90                                 this.writeHandler += write;
91                         this.performanceSummary = false;
92                         this.showSummary = true;
93                         this.skipProjectStartedText = false;
94                         errors = new List<string> ();
95                         warnings = new List<string> ();
96                         this.colorSet = colorSet;
97                         this.colorReset = colorReset;
98
99                         events = new List<BuildEvent> ();
100                         errorsTable = new Dictionary<string, List<string>> ();
101                         warningsTable = new Dictionary<string, List<string>> ();
102                         targetPerfTable = new SortedDictionary<string, PerfInfo> ();
103                         tasksPerfTable = new SortedDictionary<string, PerfInfo> ();
104
105                         //defaults
106                         errorColor = ConsoleColor.DarkRed;
107                         warningColor = ConsoleColor.DarkYellow;
108                         eventColor = ConsoleColor.DarkCyan;
109                         messageColor = ConsoleColor.DarkGray;
110                         highMessageColor = ConsoleColor.White;
111
112                         // if message color is not set via the env var,
113                         // then don't use any color for it.
114                         no_message_color = true;
115
116                         use_colors = false;
117                         if (colorSet == null || colorReset == null)
118                                 return;
119
120                         // color support
121                         string config = Environment.GetEnvironmentVariable ("XBUILD_COLORS");
122                         if (config == null) {
123                                 use_colors = true;
124                                 return;
125                         }
126
127                         if (config == "disable")
128                                 return;
129
130                         use_colors = true;
131                         string [] pairs = config.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
132                         foreach (string pair in pairs) {
133                                 string [] parts = pair.Split (new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
134                                 if (parts.Length != 2)
135                                         continue;
136
137                                 if (parts [0] == "errors")
138                                         TryParseConsoleColor (parts [1], ref errorColor);
139                                 else if (parts [0] == "warnings")
140                                         TryParseConsoleColor (parts [1], ref warningColor);
141                                 else if (parts [0] == "events")
142                                         TryParseConsoleColor (parts [1], ref eventColor);
143                                 else if (parts [0] == "messages") {
144                                         if (TryParseConsoleColor (parts [1], ref messageColor)) {
145                                                 highMessageColor = GetBrightColorFor (messageColor);
146                                                 no_message_color = false;
147                                         }
148                                 }
149                         }
150                 }
151
152                 bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
153                 {
154                         switch (color_str.ToLower ()) {
155                         case "black": color = ConsoleColor.Black; break;
156
157                         case "blue": color = ConsoleColor.DarkBlue; break;
158                         case "green": color = ConsoleColor.DarkGreen; break;
159                         case "cyan": color = ConsoleColor.DarkCyan; break;
160                         case "red": color = ConsoleColor.DarkRed; break;
161                         case "magenta": color = ConsoleColor.DarkMagenta; break;
162                         case "yellow": color = ConsoleColor.DarkYellow; break;
163                         case "grey": color = ConsoleColor.DarkGray; break;
164
165                         case "brightgrey": color = ConsoleColor.Gray; break;
166                         case "brightblue": color = ConsoleColor.Blue; break;
167                         case "brightgreen": color = ConsoleColor.Green; break;
168                         case "brightcyan": color = ConsoleColor.Cyan; break;
169                         case "brightred": color = ConsoleColor.Red; break;
170                         case "brightmagenta": color = ConsoleColor.Magenta; break;
171                         case "brightyellow": color = ConsoleColor.Yellow; break;
172
173                         case "white":
174                         case "brightwhite": color = ConsoleColor.White; break;
175                         default: return false;
176                         }
177
178                         return true;
179                 }
180
181                 ConsoleColor GetBrightColorFor (ConsoleColor color)
182                 {
183                         switch (color) {
184                         case ConsoleColor.DarkBlue: return ConsoleColor.Blue;
185                         case ConsoleColor.DarkGreen: return ConsoleColor.Green;
186                         case ConsoleColor.DarkCyan: return ConsoleColor.Cyan;
187                         case ConsoleColor.DarkRed: return ConsoleColor.Red;
188                         case ConsoleColor.DarkMagenta: return ConsoleColor.Magenta;
189                         case ConsoleColor.DarkYellow: return ConsoleColor.Yellow;
190                         case ConsoleColor.DarkGray: return ConsoleColor.Gray;
191                         case ConsoleColor.Gray: return ConsoleColor.White;
192
193                         default: return color;
194                         }
195                 }
196                 
197                 public void ApplyParameter (string parameterName,
198                                             string parameterValue)
199                 {
200                         switch (parameterName) {
201                                 case "PerformanceSummary":
202                                         this.performanceSummary = true;
203                                         break;
204                                 case "Summary":
205                                         this.showSummary = true;
206                                         break;
207                                 case "NoSummary":
208                                         this.showSummary = false;
209                                         break;
210                                 case "NoItemAndPropertyList":
211                                         this.noItemAndPropertyList = true;
212                                         break;
213                                 default:
214                                         if (parameterName.StartsWith ("Verbosity="))
215                                                 ParseVerbosity (parameterName);
216                                         break;
217                         }
218                 }
219
220                 void ParseVerbosity (string s)
221                 {
222                         string key, value;
223                         if (!TrySplitKeyValuePair (s, out key, out value))
224                                 throw new LoggerException ("Unknown Verbosity, should be set as 'Verbosity=<verbosity>'");
225
226                         switch (value) {
227                         case "q":
228                         case "quiet":
229                                 Verbosity = LoggerVerbosity.Quiet;
230                                 break;
231                         case "m":
232                         case "minimal":
233                                 Verbosity = LoggerVerbosity.Minimal;
234                                 break;
235                         case "n":
236                         case "normal":
237                                 Verbosity = LoggerVerbosity.Normal;
238                                 break;
239                         case "d":
240                         case "detailed":
241                                 Verbosity = LoggerVerbosity.Detailed;
242                                 break;
243                         case "diag":
244                         case "diagnostic":
245                                 Verbosity = LoggerVerbosity.Diagnostic;
246                                 break;
247                         default:
248                                 throw new LoggerException (String.Format ("Unknown verbosity - '{0}'", s));
249                         }
250                 }
251
252                 bool TrySplitKeyValuePair (string pair, out string key, out string value)
253                 {
254                         key = value = null;
255                         string[] parts = pair.Split ('=');
256                         if (parts.Length != 2)
257                                 return false;
258
259                         key = parts [0];
260                         value = parts [1];
261                         return true;
262                 }
263
264                 public virtual void Initialize (IEventSource eventSource)
265                 {
266                         this.eventSource = eventSource;
267
268                         eventSource.BuildStarted += BuildStartedHandler;
269                         eventSource.BuildFinished += BuildFinishedHandler;
270
271                         eventSource.ProjectStarted += PushEvent;
272                         eventSource.ProjectFinished += PopEvent;
273
274                         eventSource.TargetStarted += PushEvent;
275                         eventSource.TargetFinished += PopEvent;
276
277                         eventSource.TaskStarted += PushEvent;
278                         eventSource.TaskFinished += PopEvent;
279
280                         eventSource.MessageRaised += MessageHandler;
281                         eventSource.WarningRaised += WarningHandler;
282                         eventSource.ErrorRaised += ErrorHandler;
283
284                         if (!String.IsNullOrEmpty (parameters))
285                                 ParseParameters ();
286                 }
287
288                 public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
289                 {
290                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
291                                 WriteLine (String.Empty);
292                                 WriteLine (String.Format ("Build started {0}.", args.Timestamp));
293                                 WriteLine ("__________________________________________________");
294                         }
295                         buildStart = args.Timestamp;
296
297                         PushEvent (args);
298                 }
299                 
300                 public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
301                 {
302                         BuildFinishedHandlerActual (args);
303
304                         // Reset
305                         events.Clear ();
306                         errorsTable.Clear ();
307                         warningsTable.Clear ();
308                         targetPerfTable.Clear ();
309                         tasksPerfTable.Clear ();
310                         errors.Clear ();
311                         warnings.Clear ();
312
313                         indent = 0;
314                         errorCount = 0;
315                         warningCount = 0;
316                         projectFailed = false;
317                 }
318
319                 void BuildFinishedHandlerActual (BuildFinishedEventArgs args)
320                 {
321                         if (!IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
322                                 PopEvent (args);
323                                 return;
324                         }
325
326                         TimeSpan timeElapsed = args.Timestamp - buildStart;
327                         if (performanceSummary || verbosity == LoggerVerbosity.Diagnostic)
328                                 DumpPerformanceSummary ();
329
330                         if (args.Succeeded == true && !projectFailed) {
331                                 WriteLine ("Build succeeded.");
332                         } else {
333                                 WriteLine ("Build FAILED.");
334                         }
335                         if (warnings.Count > 0) {
336                                 WriteLine (Environment.NewLine + "Warnings:");
337                                 SetColor (warningColor);
338
339                                 WriteLine (String.Empty);
340                                 foreach (KeyValuePair<string, List<string>> pair in warningsTable) {
341                                         if (!String.IsNullOrEmpty (pair.Key))
342                                                 WriteLine (pair.Key);
343
344                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
345                                         foreach (string msg in pair.Value)
346                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
347
348                                         WriteLine (String.Empty);
349                                 }
350
351                                 ResetColor ();
352                         }
353
354                         if (errors.Count > 0) {
355                                 WriteLine ("Errors:");
356                                 SetColor (errorColor);
357
358                                 WriteLine (String.Empty);
359                                 foreach (KeyValuePair<string, List<string>> pair in errorsTable) {
360                                         if (!String.IsNullOrEmpty (pair.Key))
361                                                 WriteLine (pair.Key);
362
363                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
364                                         foreach (string msg in pair.Value)
365                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
366
367                                         WriteLine (String.Empty);
368                                 }
369                                 ResetColor ();
370                         }
371
372                         if (showSummary == true){
373                                 WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
374                                 WriteLine (String.Format ("\t {0} Error(s)", errorCount));
375                                 WriteLine (String.Empty);
376                                 WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
377                         }
378
379                         PopEvent (args);
380                 }
381
382                 public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
383                 {
384                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
385                                 SetColor (eventColor);
386                                 WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile,
387                                                         String.IsNullOrEmpty (args.TargetNames) ? "default" : args.TargetNames));
388                                 ResetColor ();
389                                 DumpProperties (args.Properties);
390                                 DumpItems (args.Items);
391                         }
392                 }
393                 
394                 public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
395                 {
396                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
397                                 if (indent == 1)
398                                         indent --;
399                                 SetColor (eventColor);
400                                 WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
401                                                         args.Succeeded ? String.Empty : "-- FAILED"));
402                                 ResetColor ();
403                                 WriteLine (String.Empty);
404                         }
405                         if (!projectFailed)
406                                 // no project has failed yet, so update the flag
407                                 projectFailed = !args.Succeeded;
408                 }
409                 
410                 public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
411                 {
412                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
413                                 indent++;
414                                 SetColor (eventColor);
415                                 WriteLine (String.Empty);
416                                 WriteLine (String.Format ("Target {0}:",args.TargetName));
417                                 ResetColor ();
418                         }
419                 }
420                 
421                 public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
422                 {
423                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
424                                         (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
425                                 SetColor (eventColor);
426                                 WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
427                                         args.TargetName, args.ProjectFile,
428                                         args.Succeeded ? String.Empty : "-- FAILED"));
429                                 ResetColor ();
430                                 WriteLine (String.Empty);
431                         }
432                         indent--;
433                 }
434                 
435                 public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
436                 {
437                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
438                                 SetColor (eventColor);
439                                 WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
440                                 ResetColor ();
441                         }
442                         indent++;
443                 }
444                 
445                 public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
446                 {
447                         indent--;
448                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
449                                         (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
450                                 SetColor (eventColor);
451                                 if (args.Succeeded)
452                                         WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
453                                 else
454                                         WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
455                                 ResetColor ();
456                         }
457                 }
458                 
459                 public void MessageHandler (object sender, BuildMessageEventArgs args)
460                 {
461                         if (IsMessageOk (args)) {
462                                 if (no_message_color) {
463                                         ExecutePendingEventHandlers ();
464                                         WriteLine (args.Message);
465                                 } else {
466                                         ExecutePendingEventHandlers ();
467                                         SetColor (args.Importance == MessageImportance.High ? highMessageColor : messageColor);
468                                         WriteLine (args.Message);
469                                         ResetColor ();
470                                 }
471                         }
472                 }
473                 
474                 public void WarningHandler (object sender, BuildWarningEventArgs args)
475                 {
476                         string msg = FormatWarningEvent (args);
477                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
478                                 ExecutePendingEventHandlers ();
479                                 SetColor (warningColor);
480                                 WriteLineWithoutIndent (msg);
481                                 ResetColor ();
482                         }
483                         warnings.Add (msg);
484
485                         List<string> list = null;
486                         if (!warningsTable.TryGetValue (EventsAsString, out list))
487                                 warningsTable [EventsAsString] = list = new List<string> ();
488                         list.Add (msg);
489
490                         warningCount++;
491                 }
492                 
493                 public void ErrorHandler (object sender, BuildErrorEventArgs args)
494                 {
495                         string msg = FormatErrorEvent (args);
496                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
497                                 ExecutePendingEventHandlers ();
498                                 SetColor (errorColor);
499                                 WriteLineWithoutIndent (msg);
500                                 ResetColor ();
501                         }
502                         errors.Add (msg);
503
504                         List<string> list = null;
505                         if (!errorsTable.TryGetValue (EventsAsString, out list))
506                                 errorsTable [EventsAsString] = list = new List<string> ();
507                         list.Add (msg);
508                         errorCount++;
509                 }
510                 
511                 [MonoTODO]
512                 public void CustomEventHandler (object sender, CustomBuildEventArgs args)
513                 {
514                 }
515
516                 private void WriteLine (string message)
517                 {
518                         if (indent > 0) {
519                                 StringBuilder sb = new StringBuilder ();
520                                 for (int i = 0; i < indent; i++)
521                                         sb.Append ('\t');
522
523                                 string indent_str = sb.ToString ();
524
525                                 foreach (string line in message.Split (new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries))
526                                         writeHandler (indent_str + line);
527                         } else {
528                                 writeHandler (message);
529                         }
530                 }
531
532                 void PushEvent<T> (object sender, T args) where T: BuildStatusEventArgs
533                 {
534                         PushEvent (args);
535                 }
536
537                 void PushEvent<T> (T args) where T: BuildStatusEventArgs
538                 {
539                         BuildEvent be = new BuildEvent {
540                                 EventArgs = args,
541                                 StartHandlerHasExecuted = false,
542                                 ConsoleLogger = this
543                         };
544
545                         events.Add (be);
546                         current_events_string = null;
547                 }
548
549                 void PopEvent<T> (object sender, T finished_args) where T: BuildStatusEventArgs
550                 {
551                         PopEvent (finished_args);
552                 }
553
554                 void PopEvent<T> (T finished_args) where T: BuildStatusEventArgs
555                 {
556                         if (events.Count == 0)
557                                 throw new InvalidOperationException ("INTERNAL ERROR: Trying to pop from an empty events stack");
558
559                         BuildEvent be = events [events.Count - 1];
560                         if (performanceSummary || verbosity == LoggerVerbosity.Diagnostic) {
561                                 var args = be.EventArgs;
562                                 TargetStartedEventArgs tgt_args = args as TargetStartedEventArgs;
563                                 if (tgt_args != null) {
564                                         AddPerfInfo (tgt_args.TargetName, args.Timestamp, targetPerfTable);
565                                 } else {
566                                         TaskStartedEventArgs tsk_args = args as TaskStartedEventArgs;
567                                         if (tsk_args != null)
568                                                 AddPerfInfo (tsk_args.TaskName, args.Timestamp, tasksPerfTable);
569                                 }
570                         }
571
572                         be.ExecuteFinishedHandler (finished_args);
573                         events.RemoveAt (events.Count - 1);
574                         current_events_string = null;
575                 }
576
577                 void ExecutePendingEventHandlers ()
578                 {
579                         foreach (var be in events)
580                                 be.ExecuteStartedHandler ();
581                 }
582
583                 string EventsToString ()
584                 {
585                         StringBuilder sb = new StringBuilder ();
586
587                         string last_imported_target_file = String.Empty;
588                         for (int i = 0; i < events.Count; i ++) {
589                                 var args = events [i].EventArgs;
590                                 ProjectStartedEventArgs pargs = args as ProjectStartedEventArgs;
591                                 if (pargs != null) {
592                                         sb.AppendFormat ("{0} ({1}) ->\n", pargs.ProjectFile,
593                                                         String.IsNullOrEmpty (pargs.TargetNames) ?
594                                                                 "default targets" :
595                                                                 pargs.TargetNames);
596                                         last_imported_target_file = String.Empty;
597                                         continue;
598                                 }
599
600                                 TargetStartedEventArgs targs = args as TargetStartedEventArgs;
601                                 if (targs != null) {
602                                         if (targs.TargetFile != targs.ProjectFile && targs.TargetFile != last_imported_target_file)
603                                                 // target from an imported file,
604                                                 // and it hasn't been mentioned as yet
605                                                 sb.AppendFormat ("{0} ", targs.TargetFile);
606
607                                         last_imported_target_file = targs.TargetFile;
608                                         sb.AppendFormat ("({0} target) ->\n", targs.TargetName);
609                                 }
610                         }
611
612                         return sb.ToString ();
613                 }
614
615                 void AddPerfInfo (string name, DateTime start, IDictionary<string, PerfInfo> perf_table)
616                 {
617                         PerfInfo pi;
618                         if (!perf_table.TryGetValue (name, out pi)) {
619                                 pi = new PerfInfo ();
620                                 perf_table [name] = pi;
621                         }
622
623                         pi.Time += DateTime.Now - start;
624                         pi.NumberOfCalls ++;
625                 }
626
627                 void DumpPerformanceSummary ()
628                 {
629                         SetColor (eventColor);
630                         WriteLine ("Target perfomance summary:");
631                         ResetColor ();
632
633                         foreach (var pi in targetPerfTable.OrderBy (pair => pair.Value.Time))
634                                 WriteLine (String.Format ("{0,10:0.000} ms  {1,-50}  {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
635
636                         WriteLine (String.Empty);
637
638                         SetColor (eventColor);
639                         WriteLine ("Tasks perfomance summary:");
640                         ResetColor ();
641
642                         foreach (var pi in tasksPerfTable.OrderBy (pair => pair.Value.Time))
643                                 WriteLine (String.Format ("{0,10:0.000} ms  {1,-50}  {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
644
645                         WriteLine (String.Empty);
646                 }
647                 
648                 private void WriteLineWithoutIndent (string message)
649                 {
650                         writeHandler (message);
651                 }
652                 
653                 private void WriteHandlerFunction (string message)
654                 {
655                         Console.WriteLine (message);
656                 }
657
658                 void SetColor (ConsoleColor color)
659                 {
660                         if (use_colors)
661                                 colorSet (color);
662                 }
663
664                 void ResetColor ()
665                 {
666                         if (use_colors)
667                                 colorReset ();
668                 }
669                 
670                 private void ParseParameters ()
671                 {
672                         string[] splittedParameters = parameters.Split (';');
673                         foreach (string s in splittedParameters )
674                                 ApplyParameter (s, null);
675                 }
676                 
677                 public virtual void Shutdown ()
678                 {
679                         if (eventSource == null)
680                                 return;
681
682                         eventSource.BuildStarted -= BuildStartedHandler;
683                         eventSource.BuildFinished -= BuildFinishedHandler;
684
685                         eventSource.ProjectStarted -= PushEvent;
686                         eventSource.ProjectFinished -= PopEvent;
687
688                         eventSource.TargetStarted -= PushEvent;
689                         eventSource.TargetFinished -= PopEvent;
690
691                         eventSource.TaskStarted -= PushEvent;
692                         eventSource.TaskFinished -= PopEvent;
693
694                         eventSource.MessageRaised -= MessageHandler;
695                         eventSource.WarningRaised -= WarningHandler;
696                         eventSource.ErrorRaised -= ErrorHandler;
697                 }
698
699                 static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
700                 
701                 private string FormatErrorEvent (BuildErrorEventArgs args)
702                 {
703                         // For some reason we get an 1-char empty string as Subcategory somtimes.
704                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
705                         string subcat = subprefix == "" ? "" : args.Subcategory;
706                                 
707                         if (args.LineNumber != 0){
708                                 if (args.ColumnNumber != 0 && !InEmacs) 
709                                         return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
710                                                               args.File, args.LineNumber, args.ColumnNumber,
711                                                               subprefix, subcat, args.Code, args.Message);
712
713                                 return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
714                                                       args.File, args.LineNumber,
715                                                       subprefix, subcat, args.Code, args.Message);
716                         } else {
717                                 return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
718                                         args.Message);
719                         }
720                 }
721
722                 private string FormatWarningEvent (BuildWarningEventArgs args)
723                 {
724                         // For some reason we get an 1-char empty string as Subcategory somtimes.
725                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
726                         string subcat = subprefix == "" ? "" : args.Subcategory;
727
728                         // FIXME: show more complicated args
729                         if (args.LineNumber != 0){
730
731                                 if (args.ColumnNumber != 0 && !InEmacs) {
732                                         return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
733                                                               args.File, args.LineNumber, args.ColumnNumber,
734                                                               subprefix, subcat, args.Code, args.Message);
735                                 }
736                                 return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
737                                                       args.File, args.LineNumber,
738                                                       subprefix, subcat, args.Code, args.Message);
739                         } else {
740                                 return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
741                                         args.Message);
742                         }
743                 }
744                 
745                 private bool IsMessageOk (BuildMessageEventArgs bsea)
746                 {
747                         if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
748                                 return true;
749                         } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
750                                 return true;
751                         } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
752                                 return true;
753                         } else
754                                 return false;
755                 }
756                 
757                 private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
758                 {
759                                 if (v == LoggerVerbosity.Diagnostic) {
760                                         return LoggerVerbosity.Diagnostic <= verbosity;
761                                 } else if (v == LoggerVerbosity.Detailed) {
762                                         return LoggerVerbosity.Detailed <= verbosity;
763                                 } else if (v == LoggerVerbosity.Normal) {
764                                         return LoggerVerbosity.Normal <= verbosity;
765                                 } else if (v == LoggerVerbosity.Minimal) {
766                                         return LoggerVerbosity.Minimal <= verbosity;
767                                 } else if (v == LoggerVerbosity.Quiet) {
768                                         return true;
769                                 } else
770                                         return false;
771                 }
772
773                 void DumpProperties (IEnumerable properties)
774                 {
775                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
776                                 return;
777
778                         SetColor (eventColor);
779                         WriteLine (String.Empty);
780                         WriteLine ("Initial Properties:");
781                         ResetColor ();
782
783                         if (properties == null)
784                                 return;
785
786                         var dict = new SortedDictionary<string, string> ();
787                         foreach (DictionaryEntry de in properties)
788                                 dict [(string)de.Key] = (string)de.Value;
789
790                         foreach (KeyValuePair<string, string> pair in dict)
791                                 WriteLine (String.Format ("{0} = {1}", pair.Key, pair.Value));
792                 }
793
794                 void DumpItems (IEnumerable items)
795                 {
796                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic) || items == null)
797                                 return;
798
799                         SetColor (eventColor);
800                         WriteLine (String.Empty);
801                         WriteLine ("Initial Items:");
802                         ResetColor ();
803                         if (items == null)
804                                 return;
805
806                         var items_table = new SortedDictionary<string, List<ITaskItem>> ();
807                         foreach (DictionaryEntry de in items) {
808                                 string key = (string)de.Key;
809                                 if (!items_table.ContainsKey (key))
810                                         items_table [key] = new List<ITaskItem> ();
811
812                                 items_table [key].Add ((ITaskItem) de.Value);
813                         }
814
815                         foreach (string name in items_table.Keys) {
816                                 WriteLine (name);
817                                 indent ++;
818                                 foreach (ITaskItem item in items_table [name])
819                                         WriteLine (item.ItemSpec);
820                                 indent--;
821                         }
822                 }
823
824                 public string Parameters {
825                         get {
826                                 return parameters;
827                         }
828                         set {
829                                 if (value == null)
830                                         throw new ArgumentNullException ();
831                                 parameters = value;
832                         }
833                 }
834
835                 string EventsAsString {
836                         get {
837                                 if (current_events_string == null)
838                                         current_events_string = EventsToString ();
839                                 return current_events_string;
840                         }
841                 }
842                 
843                 public bool ShowSummary {
844                         get { return showSummary; }
845                         set { showSummary = value; }
846                 }
847                 
848                 public bool SkipProjectStartedText {
849                         get { return skipProjectStartedText; }
850                         set { skipProjectStartedText = value; }
851                 }
852
853                 public LoggerVerbosity Verbosity {
854                         get { return verbosity; }
855                         set { verbosity = value; }
856                 }
857
858                 protected WriteHandler WriteHandler {
859                         get { return writeHandler; }
860                         set { writeHandler = value; }
861                 }
862         }
863
864         class BuildEvent {
865                 public BuildStatusEventArgs EventArgs;
866                 public bool StartHandlerHasExecuted;
867                 public ConsoleLogger ConsoleLogger;
868
869                 public void ExecuteStartedHandler ()
870                 {
871                         if (StartHandlerHasExecuted)
872                                 return;
873
874                         if (EventArgs is ProjectStartedEventArgs)
875                                 ConsoleLogger.ProjectStartedHandler (null, (ProjectStartedEventArgs)EventArgs);
876                         else if (EventArgs is TargetStartedEventArgs)
877                                 ConsoleLogger.TargetStartedHandler (null, (TargetStartedEventArgs)EventArgs);
878                         else if (EventArgs is TaskStartedEventArgs)
879                                 ConsoleLogger.TaskStartedHandler (null, (TaskStartedEventArgs)EventArgs);
880                         else if (!(EventArgs is BuildStartedEventArgs))
881                                 throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
882
883                         StartHandlerHasExecuted = true;
884                 }
885
886                 public void ExecuteFinishedHandler (BuildStatusEventArgs finished_args)
887                 {
888                         if (!StartHandlerHasExecuted)
889                                 return;
890
891                         if (EventArgs is ProjectStartedEventArgs)
892                                 ConsoleLogger.ProjectFinishedHandler (null, finished_args as ProjectFinishedEventArgs);
893                         else if (EventArgs is TargetStartedEventArgs)
894                                 ConsoleLogger.TargetFinishedHandler (null, finished_args as TargetFinishedEventArgs);
895                         else if (EventArgs is TaskStartedEventArgs)
896                                 ConsoleLogger.TaskFinishedHandler (null, finished_args as TaskFinishedEventArgs);
897                         else if (!(EventArgs is BuildStartedEventArgs))
898                                 throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
899                 }
900         }
901
902         class PerfInfo {
903                 public TimeSpan Time;
904                 public int NumberOfCalls;
905         }
906 }
907
908 #endif