759c88b09cdafa168e7389ae40f3b0588e7ddbf9
[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                         // FIXME: what we should do here? in msbuild it isn't
201                         // changing "parameters" property
202                 }
203
204                 public virtual void Initialize (IEventSource eventSource)
205                 {
206                         this.eventSource = eventSource;
207
208                         eventSource.BuildStarted += BuildStartedHandler;
209                         eventSource.BuildFinished += BuildFinishedHandler;
210
211                         eventSource.ProjectStarted += PushEvent;
212                         eventSource.ProjectFinished += PopEvent;
213
214                         eventSource.TargetStarted += PushEvent;
215                         eventSource.TargetFinished += PopEvent;
216
217                         eventSource.TaskStarted += PushEvent;
218                         eventSource.TaskFinished += PopEvent;
219
220                         eventSource.MessageRaised += MessageHandler;
221                         eventSource.WarningRaised += WarningHandler;
222                         eventSource.ErrorRaised += ErrorHandler;
223                 }
224
225                 public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
226                 {
227                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
228                                 WriteLine (String.Empty);
229                                 WriteLine (String.Format ("Build started {0}.", args.Timestamp));
230                                 WriteLine ("__________________________________________________");
231                         }
232                         buildStart = args.Timestamp;
233
234                         PushEvent (args);
235                 }
236                 
237                 public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
238                 {
239                         BuildFinishedHandlerActual (args);
240
241                         // Reset
242                         events.Clear ();
243                         errorsTable.Clear ();
244                         warningsTable.Clear ();
245                         targetPerfTable.Clear ();
246                         tasksPerfTable.Clear ();
247                         errors.Clear ();
248                         warnings.Clear ();
249
250                         indent = 0;
251                         errorCount = 0;
252                         warningCount = 0;
253                         projectFailed = false;
254                 }
255
256                 void BuildFinishedHandlerActual (BuildFinishedEventArgs args)
257                 {
258                         if (!IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
259                                 PopEvent (args);
260                                 return;
261                         }
262
263                         TimeSpan timeElapsed = args.Timestamp - buildStart;
264                         if (performanceSummary || verbosity == LoggerVerbosity.Diagnostic)
265                                 DumpPerformanceSummary ();
266
267                         if (args.Succeeded == true && !projectFailed) {
268                                 WriteLine ("Build succeeded.");
269                         } else {
270                                 WriteLine ("Build FAILED.");
271                         }
272                         if (warnings.Count > 0) {
273                                 WriteLine (Environment.NewLine + "Warnings:");
274                                 SetColor (warningColor);
275
276                                 WriteLine (String.Empty);
277                                 foreach (KeyValuePair<string, List<string>> pair in warningsTable) {
278                                         if (!String.IsNullOrEmpty (pair.Key))
279                                                 WriteLine (pair.Key);
280
281                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
282                                         foreach (string msg in pair.Value)
283                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
284
285                                         WriteLine (String.Empty);
286                                 }
287
288                                 ResetColor ();
289                         }
290
291                         if (errors.Count > 0) {
292                                 WriteLine ("Errors:");
293                                 SetColor (errorColor);
294
295                                 WriteLine (String.Empty);
296                                 foreach (KeyValuePair<string, List<string>> pair in errorsTable) {
297                                         if (!String.IsNullOrEmpty (pair.Key))
298                                                 WriteLine (pair.Key);
299
300                                         string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
301                                         foreach (string msg in pair.Value)
302                                                 WriteLine (String.Format ("{0}{1}", indent_str, msg));
303
304                                         WriteLine (String.Empty);
305                                 }
306                                 ResetColor ();
307                         }
308
309                         if (showSummary == true){
310                                 WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
311                                 WriteLine (String.Format ("\t {0} Error(s)", errorCount));
312                                 WriteLine (String.Empty);
313                                 WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
314                         }
315
316                         PopEvent (args);
317                 }
318
319                 public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
320                 {
321                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
322                                 SetColor (eventColor);
323                                 WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile,
324                                                         String.IsNullOrEmpty (args.TargetNames) ? "default" : args.TargetNames));
325                                 ResetColor ();
326                                 DumpProperties (args.Properties);
327                                 DumpItems (args.Items);
328                         }
329                 }
330                 
331                 public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
332                 {
333                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
334                                 if (indent == 1)
335                                         indent --;
336                                 SetColor (eventColor);
337                                 WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
338                                                         args.Succeeded ? String.Empty : "-- FAILED"));
339                                 ResetColor ();
340                                 WriteLine (String.Empty);
341                         }
342                         if (!projectFailed)
343                                 // no project has failed yet, so update the flag
344                                 projectFailed = !args.Succeeded;
345                 }
346                 
347                 public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
348                 {
349                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
350                                 indent++;
351                                 SetColor (eventColor);
352                                 WriteLine (String.Empty);
353                                 WriteLine (String.Format ("Target {0}:",args.TargetName));
354                                 ResetColor ();
355                         }
356                 }
357                 
358                 public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
359                 {
360                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
361                                         (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
362                                 SetColor (eventColor);
363                                 WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
364                                         args.TargetName, args.ProjectFile,
365                                         args.Succeeded ? String.Empty : "-- FAILED"));
366                                 ResetColor ();
367                                 WriteLine (String.Empty);
368                         }
369                         indent--;
370                 }
371                 
372                 public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
373                 {
374                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
375                                 SetColor (eventColor);
376                                 WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
377                                 ResetColor ();
378                         }
379                         indent++;
380                 }
381                 
382                 public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
383                 {
384                         indent--;
385                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
386                                         (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
387                                 SetColor (eventColor);
388                                 if (args.Succeeded)
389                                         WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
390                                 else
391                                         WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
392                                 ResetColor ();
393                         }
394                 }
395                 
396                 public void MessageHandler (object sender, BuildMessageEventArgs args)
397                 {
398                         if (IsMessageOk (args)) {
399                                 if (no_message_color) {
400                                         ExecutePendingEventHandlers ();
401                                         WriteLine (args.Message);
402                                 } else {
403                                         ExecutePendingEventHandlers ();
404                                         SetColor (args.Importance == MessageImportance.High ? highMessageColor : messageColor);
405                                         WriteLine (args.Message);
406                                         ResetColor ();
407                                 }
408                         }
409                 }
410                 
411                 public void WarningHandler (object sender, BuildWarningEventArgs args)
412                 {
413                         string msg = FormatWarningEvent (args);
414                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
415                                 ExecutePendingEventHandlers ();
416                                 SetColor (warningColor);
417                                 WriteLineWithoutIndent (msg);
418                                 ResetColor ();
419                         }
420                         warnings.Add (msg);
421
422                         List<string> list = null;
423                         if (!warningsTable.TryGetValue (EventsAsString, out list))
424                                 warningsTable [EventsAsString] = list = new List<string> ();
425                         list.Add (msg);
426
427                         warningCount++;
428                 }
429                 
430                 public void ErrorHandler (object sender, BuildErrorEventArgs args)
431                 {
432                         string msg = FormatErrorEvent (args);
433                         if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
434                                 ExecutePendingEventHandlers ();
435                                 SetColor (errorColor);
436                                 WriteLineWithoutIndent (msg);
437                                 ResetColor ();
438                         }
439                         errors.Add (msg);
440
441                         List<string> list = null;
442                         if (!errorsTable.TryGetValue (EventsAsString, out list))
443                                 errorsTable [EventsAsString] = list = new List<string> ();
444                         list.Add (msg);
445                         errorCount++;
446                 }
447                 
448                 [MonoTODO]
449                 public void CustomEventHandler (object sender, CustomBuildEventArgs args)
450                 {
451                 }
452
453                 private void WriteLine (string message)
454                 {
455                         if (indent > 0) {
456                                 StringBuilder sb = new StringBuilder ();
457                                 for (int i = 0; i < indent; i++)
458                                         sb.Append ('\t');
459
460                                 string indent_str = sb.ToString ();
461
462                                 foreach (string line in message.Split (new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries))
463                                         writeHandler (indent_str + line);
464                         } else {
465                                 writeHandler (message);
466                         }
467                 }
468
469                 void PushEvent<T> (object sender, T args) where T: BuildStatusEventArgs
470                 {
471                         PushEvent (args);
472                 }
473
474                 void PushEvent<T> (T args) where T: BuildStatusEventArgs
475                 {
476                         BuildEvent be = new BuildEvent {
477                                 EventArgs = args,
478                                 StartHandlerHasExecuted = false,
479                                 ConsoleLogger = this
480                         };
481
482                         events.Add (be);
483                         current_events_string = null;
484                 }
485
486                 void PopEvent<T> (object sender, T finished_args) where T: BuildStatusEventArgs
487                 {
488                         PopEvent (finished_args);
489                 }
490
491                 void PopEvent<T> (T finished_args) where T: BuildStatusEventArgs
492                 {
493                         if (events.Count == 0)
494                                 throw new InvalidOperationException ("INTERNAL ERROR: Trying to pop from an empty events stack");
495
496                         BuildEvent be = events [events.Count - 1];
497                         if (performanceSummary || verbosity == LoggerVerbosity.Diagnostic) {
498                                 var args = be.EventArgs;
499                                 TargetStartedEventArgs tgt_args = args as TargetStartedEventArgs;
500                                 if (tgt_args != null) {
501                                         AddPerfInfo (tgt_args.TargetName, args.Timestamp, targetPerfTable);
502                                 } else {
503                                         TaskStartedEventArgs tsk_args = args as TaskStartedEventArgs;
504                                         if (tsk_args != null)
505                                                 AddPerfInfo (tsk_args.TaskName, args.Timestamp, tasksPerfTable);
506                                 }
507                         }
508
509                         be.ExecuteFinishedHandler (finished_args);
510                         events.RemoveAt (events.Count - 1);
511                         current_events_string = null;
512                 }
513
514                 void ExecutePendingEventHandlers ()
515                 {
516                         foreach (var be in events)
517                                 be.ExecuteStartedHandler ();
518                 }
519
520                 string EventsToString ()
521                 {
522                         StringBuilder sb = new StringBuilder ();
523
524                         string last_imported_target_file = String.Empty;
525                         for (int i = 0; i < events.Count; i ++) {
526                                 var args = events [i].EventArgs;
527                                 ProjectStartedEventArgs pargs = args as ProjectStartedEventArgs;
528                                 if (pargs != null) {
529                                         sb.AppendFormat ("{0} ({1}) ->\n", pargs.ProjectFile,
530                                                         String.IsNullOrEmpty (pargs.TargetNames) ?
531                                                                 "default targets" :
532                                                                 pargs.TargetNames);
533                                         last_imported_target_file = String.Empty;
534                                         continue;
535                                 }
536
537                                 TargetStartedEventArgs targs = args as TargetStartedEventArgs;
538                                 if (targs != null) {
539                                         if (targs.TargetFile != targs.ProjectFile && targs.TargetFile != last_imported_target_file)
540                                                 // target from an imported file,
541                                                 // and it hasn't been mentioned as yet
542                                                 sb.AppendFormat ("{0} ", targs.TargetFile);
543
544                                         last_imported_target_file = targs.TargetFile;
545                                         sb.AppendFormat ("({0} target) ->\n", targs.TargetName);
546                                 }
547                         }
548
549                         return sb.ToString ();
550                 }
551
552                 void AddPerfInfo (string name, DateTime start, IDictionary<string, PerfInfo> perf_table)
553                 {
554                         PerfInfo pi;
555                         if (!perf_table.TryGetValue (name, out pi)) {
556                                 pi = new PerfInfo ();
557                                 perf_table [name] = pi;
558                         }
559
560                         pi.Time += DateTime.Now - start;
561                         pi.NumberOfCalls ++;
562                 }
563
564                 void DumpPerformanceSummary ()
565                 {
566                         SetColor (eventColor);
567                         WriteLine ("Target perfomance summary:");
568                         ResetColor ();
569
570                         foreach (var pi in targetPerfTable.OrderBy (pair => pair.Value.Time))
571                                 WriteLine (String.Format ("{0,10:0.000} ms  {1,-50}  {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
572
573                         WriteLine (String.Empty);
574
575                         SetColor (eventColor);
576                         WriteLine ("Tasks perfomance summary:");
577                         ResetColor ();
578
579                         foreach (var pi in tasksPerfTable.OrderBy (pair => pair.Value.Time))
580                                 WriteLine (String.Format ("{0,10:0.000} ms  {1,-50}  {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
581
582                         WriteLine (String.Empty);
583                 }
584                 
585                 private void WriteLineWithoutIndent (string message)
586                 {
587                         writeHandler (message);
588                 }
589                 
590                 private void WriteHandlerFunction (string message)
591                 {
592                         Console.WriteLine (message);
593                 }
594
595                 void SetColor (ConsoleColor color)
596                 {
597                         if (use_colors)
598                                 colorSet (color);
599                 }
600
601                 void ResetColor ()
602                 {
603                         if (use_colors)
604                                 colorReset ();
605                 }
606                 
607                 private void ParseParameters ()
608                 {
609                         string[] splittedParameters = parameters.Split (';');
610                         foreach (string s in splittedParameters ) {
611                                 switch (s) {
612                                 case "PerformanceSummary":
613                                         this.performanceSummary = true;
614                                         break;
615                                 case "NoSummary":
616                                         this.showSummary = false;
617                                         break;
618                                 case "NoItemAndPropertyList":
619                                         this.noItemAndPropertyList = true;
620                                         break;
621                                 default:
622                                         throw new ArgumentException ("Invalid parameter : " + s);
623                                 }
624                         }
625                 }
626                 
627                 public virtual void Shutdown ()
628                 {
629                         if (eventSource == null)
630                                 return;
631
632                         eventSource.BuildStarted -= BuildStartedHandler;
633                         eventSource.BuildFinished -= BuildFinishedHandler;
634
635                         eventSource.ProjectStarted -= PushEvent;
636                         eventSource.ProjectFinished -= PopEvent;
637
638                         eventSource.TargetStarted -= PushEvent;
639                         eventSource.TargetFinished -= PopEvent;
640
641                         eventSource.TaskStarted -= PushEvent;
642                         eventSource.TaskFinished -= PopEvent;
643
644                         eventSource.MessageRaised -= MessageHandler;
645                         eventSource.WarningRaised -= WarningHandler;
646                         eventSource.ErrorRaised -= ErrorHandler;
647                 }
648
649                 static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
650                 
651                 private string FormatErrorEvent (BuildErrorEventArgs args)
652                 {
653                         // For some reason we get an 1-char empty string as Subcategory somtimes.
654                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
655                         string subcat = subprefix == "" ? "" : args.Subcategory;
656                                 
657                         if (args.LineNumber != 0){
658                                 if (args.ColumnNumber != 0 && !InEmacs) 
659                                         return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
660                                                               args.File, args.LineNumber, args.ColumnNumber,
661                                                               subprefix, subcat, args.Code, args.Message);
662
663                                 return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
664                                                       args.File, args.LineNumber,
665                                                       subprefix, subcat, args.Code, args.Message);
666                         } else {
667                                 return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
668                                         args.Message);
669                         }
670                 }
671
672                 private string FormatWarningEvent (BuildWarningEventArgs args)
673                 {
674                         // For some reason we get an 1-char empty string as Subcategory somtimes.
675                         string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
676                         string subcat = subprefix == "" ? "" : args.Subcategory;
677
678                         // FIXME: show more complicated args
679                         if (args.LineNumber != 0){
680
681                                 if (args.ColumnNumber != 0 && !InEmacs) {
682                                         return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
683                                                               args.File, args.LineNumber, args.ColumnNumber,
684                                                               subprefix, subcat, args.Code, args.Message);
685                                 }
686                                 return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
687                                                       args.File, args.LineNumber,
688                                                       subprefix, subcat, args.Code, args.Message);
689                         } else {
690                                 return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
691                                         args.Message);
692                         }
693                 }
694                 
695                 private bool IsMessageOk (BuildMessageEventArgs bsea)
696                 {
697                         if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
698                                 return true;
699                         } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
700                                 return true;
701                         } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
702                                 return true;
703                         } else
704                                 return false;
705                 }
706                 
707                 private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
708                 {
709                                 if (v == LoggerVerbosity.Diagnostic) {
710                                         return LoggerVerbosity.Diagnostic <= verbosity;
711                                 } else if (v == LoggerVerbosity.Detailed) {
712                                         return LoggerVerbosity.Detailed <= verbosity;
713                                 } else if (v == LoggerVerbosity.Normal) {
714                                         return LoggerVerbosity.Normal <= verbosity;
715                                 } else if (v == LoggerVerbosity.Minimal) {
716                                         return LoggerVerbosity.Minimal <= verbosity;
717                                 } else if (v == LoggerVerbosity.Quiet) {
718                                         return true;
719                                 } else
720                                         return false;
721                 }
722
723                 void DumpProperties (IEnumerable properties)
724                 {
725                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
726                                 return;
727
728                         SetColor (eventColor);
729                         WriteLine (String.Empty);
730                         WriteLine ("Initial Properties:");
731                         ResetColor ();
732
733                         if (properties == null)
734                                 return;
735
736                         var dict = new SortedDictionary<string, string> ();
737                         foreach (DictionaryEntry de in properties)
738                                 dict [(string)de.Key] = (string)de.Value;
739
740                         foreach (KeyValuePair<string, string> pair in dict)
741                                 WriteLine (String.Format ("{0} = {1}", pair.Key, pair.Value));
742                 }
743
744                 void DumpItems (IEnumerable items)
745                 {
746                         if (noItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic) || items == null)
747                                 return;
748
749                         SetColor (eventColor);
750                         WriteLine (String.Empty);
751                         WriteLine ("Initial Items:");
752                         ResetColor ();
753                         if (items == null)
754                                 return;
755
756                         var items_table = new SortedDictionary<string, List<ITaskItem>> ();
757                         foreach (DictionaryEntry de in items) {
758                                 string key = (string)de.Key;
759                                 if (!items_table.ContainsKey (key))
760                                         items_table [key] = new List<ITaskItem> ();
761
762                                 items_table [key].Add ((ITaskItem) de.Value);
763                         }
764
765                         foreach (string name in items_table.Keys) {
766                                 WriteLine (name);
767                                 indent ++;
768                                 foreach (ITaskItem item in items_table [name])
769                                         WriteLine (item.ItemSpec);
770                                 indent--;
771                         }
772                 }
773
774                 public string Parameters {
775                         get {
776                                 return parameters;
777                         }
778                         set {
779                                 if (value == null)
780                                         throw new ArgumentNullException ();
781                                 parameters = value;
782                                 if (parameters != String.Empty)
783                                         ParseParameters ();
784                         }
785                 }
786
787                 string EventsAsString {
788                         get {
789                                 if (current_events_string == null)
790                                         current_events_string = EventsToString ();
791                                 return current_events_string;
792                         }
793                 }
794                 
795                 public bool ShowSummary {
796                         get { return showSummary; }
797                         set { showSummary = value; }
798                 }
799                 
800                 public bool SkipProjectStartedText {
801                         get { return skipProjectStartedText; }
802                         set { skipProjectStartedText = value; }
803                 }
804
805                 public LoggerVerbosity Verbosity {
806                         get { return verbosity; }
807                         set { verbosity = value; }
808                 }
809
810                 protected WriteHandler WriteHandler {
811                         get { return writeHandler; }
812                         set { writeHandler = value; }
813                 }
814         }
815
816         class BuildEvent {
817                 public BuildStatusEventArgs EventArgs;
818                 public bool StartHandlerHasExecuted;
819                 public ConsoleLogger ConsoleLogger;
820
821                 public void ExecuteStartedHandler ()
822                 {
823                         if (StartHandlerHasExecuted)
824                                 return;
825
826                         if (EventArgs is ProjectStartedEventArgs)
827                                 ConsoleLogger.ProjectStartedHandler (null, (ProjectStartedEventArgs)EventArgs);
828                         else if (EventArgs is TargetStartedEventArgs)
829                                 ConsoleLogger.TargetStartedHandler (null, (TargetStartedEventArgs)EventArgs);
830                         else if (EventArgs is TaskStartedEventArgs)
831                                 ConsoleLogger.TaskStartedHandler (null, (TaskStartedEventArgs)EventArgs);
832                         else if (!(EventArgs is BuildStartedEventArgs))
833                                 throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
834
835                         StartHandlerHasExecuted = true;
836                 }
837
838                 public void ExecuteFinishedHandler (BuildStatusEventArgs finished_args)
839                 {
840                         if (!StartHandlerHasExecuted)
841                                 return;
842
843                         if (EventArgs is ProjectStartedEventArgs)
844                                 ConsoleLogger.ProjectFinishedHandler (null, finished_args as ProjectFinishedEventArgs);
845                         else if (EventArgs is TargetStartedEventArgs)
846                                 ConsoleLogger.TargetFinishedHandler (null, finished_args as TargetFinishedEventArgs);
847                         else if (EventArgs is TaskStartedEventArgs)
848                                 ConsoleLogger.TaskFinishedHandler (null, finished_args as TaskFinishedEventArgs);
849                         else if (!(EventArgs is BuildStartedEventArgs))
850                                 throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
851                 }
852         }
853
854         class PerfInfo {
855                 public TimeSpan Time;
856                 public int NumberOfCalls;
857         }
858 }
859
860 #endif