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