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