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