// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#if NET_2_0
-
using System;
using System.Runtime.InteropServices;
+using System.Collections;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Security;
+using System.Text;
using Microsoft.Build.Framework;
-namespace Microsoft.Build.BuildEngine {
+#if MICROSOFT_BUILD_DLL
+namespace Microsoft.Build.Logging
+#else
+namespace Microsoft.Build.BuildEngine
+#endif
+{
public class ConsoleLogger : ILogger {
string parameters;
- int indent;
LoggerVerbosity verbosity;
WriteHandler writeHandler;
- int errorCount;
- int warningCount;
- DateTime buildStart;
- bool performanceSummary;
- bool showSummary;
bool skipProjectStartedText;
+ ConsoleColor errorColor, warningColor, eventColor, messageColor, highMessageColor;
+ ColorSetter colorSet;
+ ColorResetter colorReset;
+ IEventSource eventSource;
+ bool no_message_color, use_colors;
+ ConsoleLoggerParameter config = new ConsoleLoggerParameter ();
public ConsoleLogger ()
: this (LoggerVerbosity.Normal, null, null, null)
}
public ConsoleLogger (LoggerVerbosity verbosity)
- : this (LoggerVerbosity.Normal, null, null, null)
+ : this (verbosity, null, null, null)
{
}
- // FIXME: what about colorSet and colorReset?
public ConsoleLogger (LoggerVerbosity verbosity,
WriteHandler write,
ColorSetter colorSet,
ColorResetter colorReset)
{
this.verbosity = verbosity;
- this.indent = 0;
- this.errorCount = 0;
- this.warningCount = 0;
if (write == null)
this.writeHandler += new WriteHandler (WriteHandlerFunction);
else
this.writeHandler += write;
- this.performanceSummary = false;
- this.showSummary = true;
this.skipProjectStartedText = false;
+ this.colorSet = colorSet;
+ this.colorReset = colorReset;
+
+ //defaults
+ errorColor = ConsoleColor.DarkRed;
+ warningColor = ConsoleColor.DarkYellow;
+ eventColor = ConsoleColor.DarkCyan;
+ messageColor = ConsoleColor.DarkGray;
+ highMessageColor = ConsoleColor.White;
+
+ // if message color is not set via the env var,
+ // then don't use any color for it.
+ no_message_color = true;
+
+ use_colors = false;
+ if (colorSet == null || colorReset == null)
+ return;
+
+ // color support
+ string config = Environment.GetEnvironmentVariable ("XBUILD_COLORS");
+ if (config == null) {
+ use_colors = true;
+ return;
+ }
+
+ if (config == "disable")
+ return;
+
+ use_colors = true;
+ string [] pairs = config.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
+ foreach (string pair in pairs) {
+ string [] parts = pair.Split (new char[] {'='}, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length != 2)
+ continue;
+
+ if (parts [0] == "errors")
+ TryParseConsoleColor (parts [1], ref errorColor);
+ else if (parts [0] == "warnings")
+ TryParseConsoleColor (parts [1], ref warningColor);
+ else if (parts [0] == "events")
+ TryParseConsoleColor (parts [1], ref eventColor);
+ else if (parts [0] == "messages") {
+ if (TryParseConsoleColor (parts [1], ref messageColor)) {
+ highMessageColor = GetBrightColorFor (messageColor);
+ no_message_color = false;
+ }
+ }
+ }
}
- public void ApplyParameter (string parameterName,
- string parameterValue)
+ private void WriteHandlerFunction (string message)
+ {
+ Console.WriteLine (message);
+ }
+
+ bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
{
- // FIXME: what we should do here? in msbuild it isn't
- // changing "parameters" property
+ switch (color_str.ToLowerInvariant ()) {
+ case "black": color = ConsoleColor.Black; break;
+
+ case "blue": color = ConsoleColor.DarkBlue; break;
+ case "green": color = ConsoleColor.DarkGreen; break;
+ case "cyan": color = ConsoleColor.DarkCyan; break;
+ case "red": color = ConsoleColor.DarkRed; break;
+ case "magenta": color = ConsoleColor.DarkMagenta; break;
+ case "yellow": color = ConsoleColor.DarkYellow; break;
+ case "grey": color = ConsoleColor.DarkGray; break;
+
+ case "brightgrey": color = ConsoleColor.Gray; break;
+ case "brightblue": color = ConsoleColor.Blue; break;
+ case "brightgreen": color = ConsoleColor.Green; break;
+ case "brightcyan": color = ConsoleColor.Cyan; break;
+ case "brightred": color = ConsoleColor.Red; break;
+ case "brightmagenta": color = ConsoleColor.Magenta; break;
+ case "brightyellow": color = ConsoleColor.Yellow; break;
+
+ case "white":
+ case "brightwhite": color = ConsoleColor.White; break;
+ default: return false;
+ }
+
+ return true;
}
- public virtual void Initialize (IEventSource eventSource)
+ ConsoleColor GetBrightColorFor (ConsoleColor color)
{
- eventSource.BuildStarted += new BuildStartedEventHandler (BuildStartedHandler);
- eventSource.BuildFinished += new BuildFinishedEventHandler (BuildFinishedHandler);
- eventSource.ProjectStarted += new ProjectStartedEventHandler (ProjectStartedHandler);
- eventSource.ProjectFinished += new ProjectFinishedEventHandler (ProjectFinishedHandler);
- eventSource.TargetStarted += new TargetStartedEventHandler (TargetStartedHandler);
- eventSource.TargetFinished += new TargetFinishedEventHandler (TargetFinishedHandler);
- eventSource.TaskStarted += new TaskStartedEventHandler (TaskStartedHandler);
- eventSource.TaskFinished += new TaskFinishedEventHandler (TaskFinishedHandler);
- eventSource.MessageRaised += new BuildMessageEventHandler (MessageHandler);
- eventSource.WarningRaised += new BuildWarningEventHandler (WarningHandler);
- eventSource.ErrorRaised += new BuildErrorEventHandler (ErrorHandler);
+ switch (color) {
+ case ConsoleColor.DarkBlue: return ConsoleColor.Blue;
+ case ConsoleColor.DarkGreen: return ConsoleColor.Green;
+ case ConsoleColor.DarkCyan: return ConsoleColor.Cyan;
+ case ConsoleColor.DarkRed: return ConsoleColor.Red;
+ case ConsoleColor.DarkMagenta: return ConsoleColor.Magenta;
+ case ConsoleColor.DarkYellow: return ConsoleColor.Yellow;
+ case ConsoleColor.DarkGray: return ConsoleColor.Gray;
+ case ConsoleColor.Gray: return ConsoleColor.White;
+
+ default: return color;
+ }
}
- public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
+ class ConsoleLoggerParameter
{
- WriteLine (String.Empty);
- WriteLine (String.Format ("Build started {0}.", args.Timestamp));
- WriteLine ("__________________________________________________");
- buildStart = args.Timestamp;
+ public ConsoleLoggerParameter ()
+ {
+ ShowSummary = true;
+ }
+ public bool PerformanceSummary { get; set; }
+ public bool ShowSummary { get; set; }
+ public bool NoItemAndPropertyList { get; set; }
}
- public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
+ public void ApplyParameter (string parameterName,
+ string parameterValue)
{
- if (args.Succeeded == true) {
- WriteLine ("Build succeeded.");
- } else {
- WriteLine ("Build failed.");
+ switch (parameterName) {
+ case "PerformanceSummary":
+ config.PerformanceSummary = true;
+ break;
+ case "Summary":
+ config.ShowSummary = true;
+ break;
+ case "NoSummary":
+ config.ShowSummary = false;
+ break;
+ case "NoItemAndPropertyList":
+ config.NoItemAndPropertyList = true;
+ break;
+ default:
+ if (parameterName.StartsWith ("Verbosity="))
+ ParseVerbosity (parameterName);
+ break;
}
- if (performanceSummary == true) {
+ }
+
+ void ParseVerbosity (string s)
+ {
+ string key, value;
+ if (!TrySplitKeyValuePair (s, out key, out value))
+ throw new LoggerException ("Unknown Verbosity, should be set as 'Verbosity=<verbosity>'");
+
+ switch (value) {
+ case "q":
+ case "quiet":
+ Verbosity = LoggerVerbosity.Quiet;
+ break;
+ case "m":
+ case "minimal":
+ Verbosity = LoggerVerbosity.Minimal;
+ break;
+ case "n":
+ case "normal":
+ Verbosity = LoggerVerbosity.Normal;
+ break;
+ case "d":
+ case "detailed":
+ Verbosity = LoggerVerbosity.Detailed;
+ break;
+ case "diag":
+ case "diagnostic":
+ Verbosity = LoggerVerbosity.Diagnostic;
+ break;
+ default:
+ throw new LoggerException (String.Format ("Unknown verbosity - '{0}'", s));
}
- if (showSummary == true){
- TimeSpan timeElapsed = args.Timestamp - buildStart;
- WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
- WriteLine (String.Format ("\t {0} Error(s)", errorCount));
- WriteLine (String.Empty);
- WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
- }
}
- public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
+ bool TrySplitKeyValuePair (string pair, out string key, out string value)
{
- WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile, args.TargetNames));
- WriteLine (String.Empty);
+ key = value = null;
+ string[] parts = pair.Split ('=');
+ if (parts.Length != 2)
+ return false;
+
+ key = parts [0];
+ value = parts [1];
+ return true;
+ }
+
+ public virtual void Initialize (IEventSource eventSource)
+ {
+ this.eventSource = eventSource;
+
+ eventSource.BuildStarted += BuildStartedHandler;
+ eventSource.BuildFinished += BuildFinishedHandler;
+
+ eventSource.ProjectStarted += PushEvent;
+ eventSource.ProjectFinished += PopEvent;
+
+ eventSource.TargetStarted += PushEvent;
+ eventSource.TargetFinished += PopEvent;
+
+ eventSource.TaskStarted += PushEvent;
+ eventSource.TaskFinished += PopEvent;
+
+ eventSource.MessageRaised += MessageHandler;
+ eventSource.WarningRaised += WarningHandler;
+ eventSource.ErrorRaised += ErrorHandler;
+
+ if (!String.IsNullOrEmpty (parameters))
+ ParseParameters ();
}
- public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
+ Dictionary<object,BuildRecord> build_records = new Dictionary<object, BuildRecord> ();
+
+ object dummy_key = new object ();
+
+ BuildRecord GetBuildRecord (object sender)
{
- if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic)) {
- WriteLine (String.Format ("Done building project \"{0}\".", args.ProjectFile));
- WriteLine (String.Empty);
+ BuildRecord r;
+ // FIXME: our Microsoft.Build.Engine shouldn't give different "sender" object for each event
+ // during the same build run. But it actually does.
+ // It is problematic for parallel build because it is impossible to determine right "ongoing build"
+ // record for the event without correct sender object.
+ // Hence we expect sender as a valid object only if it is IBuildEngine4 -
+ // only Microsoft.Build.Internal.BuildEngine4 implements it so far.
+ // (Used IBuildEngine3 because it needs to build for NET_4_0).
+#if NET_4_0
+ var key = sender as IBuildEngine3 ?? dummy_key;
+#else
+ var key = dummy_key;
+#endif
+ if (!build_records.TryGetValue (key, out r)) {
+ r = new BuildRecord (this);
+ build_records.Add (key, r);
}
+ return r;
+ }
+
+ public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
+ {
+ GetBuildRecord (sender).BuildStartedHandler (sender, args);
}
- public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
+ public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
{
- WriteLine (String.Format ("Target {0}:",args.TargetName));
- indent++;
+ GetBuildRecord (sender).BuildFinishedHandler (args);
+ build_records.Remove (sender);
}
+ void PushEvent<T> (object sender, T args) where T: BuildStatusEventArgs
+ {
+ GetBuildRecord (sender).PushEvent (sender, args);
+ }
+ void PopEvent<T> (object sender, T args) where T: BuildStatusEventArgs
+ {
+ GetBuildRecord (sender).PopEvent (args);
+ }
+ public void ProjectStartedHandler (object sender, ProjectStartedEventArgs args)
+ {
+ GetBuildRecord (sender).ProjectStartedHandler (args);
+ }
+ public void ProjectFinishedHandler (object sender, ProjectFinishedEventArgs args)
+ {
+ GetBuildRecord (sender).ProjectFinishedHandler (args);
+ }
+ public void TargetStartedHandler (object sender, TargetStartedEventArgs args)
+ {
+ GetBuildRecord (sender).TargetStartedHandler (args);
+ }
public void TargetFinishedHandler (object sender, TargetFinishedEventArgs args)
{
- indent--;
- if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
- WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".",
- args.TargetName, args.ProjectFile));
- WriteLine (String.Empty);
+ GetBuildRecord (sender).TargetFinishedHandler (args);
}
-
public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
{
- if (this.verbosity == LoggerVerbosity.Diagnostic)
- WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
- indent++;
+ GetBuildRecord (sender).TaskStartedHandler (args);
}
-
public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
{
- indent--;
- if (this.verbosity == LoggerVerbosity.Diagnostic)
- WriteLine (String.Format ("Done executing task \"{0}\"",args.TaskName));
+ GetBuildRecord (sender).TaskFinishedHandler (args);
}
-
public void MessageHandler (object sender, BuildMessageEventArgs args)
{
- if (IsMessageOk (args)) {
- WriteLine (args.Message);
- }
+ GetBuildRecord (sender).MessageHandler (args);
}
-
public void WarningHandler (object sender, BuildWarningEventArgs args)
{
- if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))
- WriteLineWithoutIndent (FormatWarningEvent (args));
- warningCount++;
+ GetBuildRecord (sender).WarningHandler (args);
}
-
public void ErrorHandler (object sender, BuildErrorEventArgs args)
{
- if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal))
- WriteLineWithoutIndent (FormatErrorEvent (args));
- errorCount++;
+ GetBuildRecord (sender).ErrorHandler (args);
}
[MonoTODO]
public void CustomEventHandler (object sender, CustomBuildEventArgs args)
{
+ build_records [sender].CustomHandler (args);
}
-
- private void WriteLine (string message)
- {
- for (int i = 0; i < indent; i++)
- Console.Write ('\t');
- writeHandler (message);
- }
-
- private void WriteLineWithoutIndent (string message)
+
+ void SetColor (ConsoleColor color)
{
- writeHandler (message);
+ if (use_colors)
+ colorSet (color);
}
-
- private void WriteHandlerFunction (string message)
+
+ void ResetColor ()
{
- Console.WriteLine (message);
+ if (use_colors)
+ colorReset ();
}
private void ParseParameters ()
{
string[] splittedParameters = parameters.Split (';');
- foreach (string s in splittedParameters ) {
- switch (s) {
- case "PerformanceSummary":
- this.performanceSummary = true;
- break;
- case "NoSummary":
- this.showSummary = false;
- break;
- default:
- throw new ArgumentException ("Invalid parameter.");
- }
- }
+ foreach (string s in splittedParameters )
+ ApplyParameter (s, null);
}
public virtual void Shutdown ()
{
- }
-
- private string FormatErrorEvent (BuildErrorEventArgs args)
- {
- // FIXME: show more complicated args
- if (args.LineNumber != 0 && args.ColumnNumber != 0) {
- return String.Format ("{0}({1},{2}): {3} error {4}: {5}", args.File, args.LineNumber, args.ColumnNumber,
- args.Subcategory, args.Code, args.Message);
- } else {
- return String.Format ("{0}: {1} error {2}: {3}", args.File, args.Subcategory, args.Code,
- args.Message);
- }
- }
+ if (eventSource == null)
+ return;
- private string FormatWarningEvent (BuildWarningEventArgs args)
- {
- // FIXME: show more complicated args
- if (args.LineNumber != 0 && args.ColumnNumber != 0) {
- return String.Format ("{0}({1},{2}): {3} warning {4}: {5}", args.File, args.LineNumber, args.ColumnNumber,
- args.Subcategory, args.Code, args.Message);
- } else {
- return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
- args.Message);
- }
- }
-
- private bool IsMessageOk (BuildMessageEventArgs bsea)
- {
- if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
- return true;
- } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
- return true;
- } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
- return true;
- } else
- return false;
+ eventSource.BuildStarted -= BuildStartedHandler;
+ eventSource.BuildFinished -= BuildFinishedHandler;
+
+ eventSource.ProjectStarted -= PushEvent;
+ eventSource.ProjectFinished -= PopEvent;
+
+ eventSource.TargetStarted -= PushEvent;
+ eventSource.TargetFinished -= PopEvent;
+
+ eventSource.TaskStarted -= PushEvent;
+ eventSource.TaskFinished -= PopEvent;
+
+ eventSource.MessageRaised -= MessageHandler;
+ eventSource.WarningRaised -= WarningHandler;
+ eventSource.ErrorRaised -= ErrorHandler;
}
-
- private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
- {
- if (v == LoggerVerbosity.Diagnostic) {
- return LoggerVerbosity.Diagnostic <= verbosity;
- } else if (v == LoggerVerbosity.Detailed) {
- return LoggerVerbosity.Detailed <= verbosity;
- } else if (v == LoggerVerbosity.Normal) {
- return LoggerVerbosity.Normal <= verbosity;
- } else if (v == LoggerVerbosity.Minimal) {
- return LoggerVerbosity.Minimal <= verbosity;
- } else if (v == LoggerVerbosity.Quiet) {
- return true;
- } else
- return false;
- }
public string Parameters {
get {
if (value == null)
throw new ArgumentNullException ();
parameters = value;
- if (parameters != String.Empty)
- ParseParameters ();
+ ParseParameters ();
}
}
public bool ShowSummary {
- get { return showSummary; }
- set { showSummary = value; }
+ get { return config.ShowSummary; }
+ set { config.ShowSummary = value; }
}
public bool SkipProjectStartedText {
get { return writeHandler; }
set { writeHandler = value; }
}
+
+ class BuildRecord
+ {
+ readonly ConsoleLogger parent;
+
+ readonly List<BuildEvent> events;
+ readonly Dictionary<string, List<string>> errorsTable;
+ readonly Dictionary<string, List<string>> warningsTable;
+ readonly SortedDictionary<string, PerfInfo> targetPerfTable, tasksPerfTable;
+ List<string> errors, warnings;
+ int indent;
+ int errorCount;
+ int warningCount;
+ bool projectFailed;
+ DateTime buildStart;
+
+ string current_events_string;
+
+ ConsoleColor eventColor {
+ get { return parent.eventColor; }
+ }
+ LoggerVerbosity verbosity {
+ get { return parent.Verbosity; }
+ }
+
+ public BuildRecord (ConsoleLogger parent)
+ {
+ this.parent = parent;
+
+ this.indent = 0;
+ this.errorCount = 0;
+ this.warningCount = 0;
+ errors = new List<string> ();
+ warnings = new List<string> ();
+ events = new List<BuildEvent> ();
+ errorsTable = new Dictionary<string, List<string>> ();
+ warningsTable = new Dictionary<string, List<string>> ();
+ targetPerfTable = new SortedDictionary<string, PerfInfo> ();
+ tasksPerfTable = new SortedDictionary<string, PerfInfo> ();
+ }
+
+ internal void PushEvent<T> (object sender, T args) where T: BuildStatusEventArgs
+ {
+ BuildEvent be = new BuildEvent {
+ Sender = sender,
+ EventArgs = args,
+ StartHandlerHasExecuted = false,
+ ConsoleLogger = this.parent
+ };
+
+ events.Add (be);
+ current_events_string = null;
+ }
+
+ void PopEvent<T> (object sender, T finished_args) where T: BuildStatusEventArgs
+ {
+ PopEvent (finished_args);
+ }
+
+ internal void PopEvent<T> (T finished_args) where T: BuildStatusEventArgs
+ {
+ if (events.Count == 0)
+ throw new InvalidOperationException ("INTERNAL ERROR: Trying to pop from an empty events stack");
+
+ BuildEvent be = events [events.Count - 1];
+ if (parent.config.PerformanceSummary || verbosity == LoggerVerbosity.Diagnostic) {
+ var args = be.EventArgs;
+ TargetStartedEventArgs tgt_args = args as TargetStartedEventArgs;
+ if (tgt_args != null) {
+ AddPerfInfo (tgt_args.TargetName, args.Timestamp, targetPerfTable);
+ } else {
+ TaskStartedEventArgs tsk_args = args as TaskStartedEventArgs;
+ if (tsk_args != null)
+ AddPerfInfo (tsk_args.TaskName, args.Timestamp, tasksPerfTable);
+ }
+ }
+
+ be.ExecuteFinishedHandler (finished_args);
+ events.RemoveAt (events.Count - 1);
+ current_events_string = null;
+ }
+
+ public void ResetBuildState ()
+ {
+ // Reset
+ events.Clear ();
+ errorsTable.Clear ();
+ warningsTable.Clear ();
+ targetPerfTable.Clear ();
+ tasksPerfTable.Clear ();
+ errors.Clear ();
+ warnings.Clear ();
+
+ indent = 0;
+ errorCount = 0;
+ warningCount = 0;
+ projectFailed = false;
+ }
+
+ void AddPerfInfo (string name, DateTime start, IDictionary<string, PerfInfo> perf_table)
+ {
+ PerfInfo pi;
+ if (!perf_table.TryGetValue (name, out pi)) {
+ pi = new PerfInfo ();
+ perf_table [name] = pi;
+ }
+
+ pi.Time += DateTime.Now - start;
+ pi.NumberOfCalls ++;
+ }
+
+ public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ WriteLine (String.Empty);
+ WriteLine (String.Format ("Build started {0}.", args.Timestamp));
+ WriteLine ("__________________________________________________");
+ }
+ buildStart = args.Timestamp;
+
+ PushEvent (sender, args);
+ }
+
+ public void BuildFinishedHandler (BuildFinishedEventArgs args)
+ {
+ BuildFinishedHandlerActual (args);
+
+ ResetBuildState ();
+ }
+
+ void BuildFinishedHandlerActual (BuildFinishedEventArgs args)
+ {
+ if (!IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ PopEvent (args);
+ return;
+ }
+
+ TimeSpan timeElapsed = args.Timestamp - buildStart;
+ if (parent.config.PerformanceSummary || verbosity == LoggerVerbosity.Diagnostic)
+ DumpPerformanceSummary ();
+
+ if (args.Succeeded == true && !projectFailed) {
+ WriteLine ("Build succeeded.");
+ } else {
+ WriteLine ("Build FAILED.");
+ }
+ if (warnings.Count > 0) {
+ WriteLine (Environment.NewLine + "Warnings:");
+ SetColor (parent.warningColor);
+
+ WriteLine (String.Empty);
+ foreach (KeyValuePair<string, List<string>> pair in warningsTable) {
+ if (!String.IsNullOrEmpty (pair.Key))
+ WriteLine (pair.Key);
+
+ string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
+ foreach (string msg in pair.Value)
+ WriteLine (String.Format ("{0}{1}", indent_str, msg));
+
+ WriteLine (String.Empty);
+ }
+
+ ResetColor ();
+ }
+
+ if (errors.Count > 0) {
+ WriteLine ("Errors:");
+ SetColor (parent.errorColor);
+
+ WriteLine (String.Empty);
+ foreach (KeyValuePair<string, List<string>> pair in errorsTable) {
+ if (!String.IsNullOrEmpty (pair.Key))
+ WriteLine (pair.Key);
+
+ string indent_str = String.IsNullOrEmpty (pair.Key) ? String.Empty : "\t";
+ foreach (string msg in pair.Value)
+ WriteLine (String.Format ("{0}{1}", indent_str, msg));
+
+ WriteLine (String.Empty);
+ }
+ ResetColor ();
+ }
+
+ if (parent.ShowSummary == true){
+ WriteLine (String.Format ("\t {0} Warning(s)", warningCount));
+ WriteLine (String.Format ("\t {0} Error(s)", errorCount));
+ WriteLine (String.Empty);
+ WriteLine (String.Format ("Time Elapsed {0}", timeElapsed));
+ }
+
+ PopEvent (args);
+ }
+
+ public void ProjectStartedHandler (ProjectStartedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ SetColor (eventColor);
+ WriteLine (String.Format ("Project \"{0}\" ({1} target(s)):", args.ProjectFile,
+ String.IsNullOrEmpty (args.TargetNames) ? "default" : args.TargetNames));
+ ResetColor ();
+ DumpProperties (args.Properties);
+ DumpItems (args.Items);
+ }
+ }
+
+ public void ProjectFinishedHandler (ProjectFinishedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ if (indent == 1)
+ indent --;
+ SetColor (eventColor);
+ WriteLine (String.Format ("Done building project \"{0}\".{1}", args.ProjectFile,
+ args.Succeeded ? String.Empty : "-- FAILED"));
+ ResetColor ();
+ WriteLine (String.Empty);
+ }
+ if (!projectFailed)
+ // no project has failed yet, so update the flag
+ projectFailed = !args.Succeeded;
+ }
+
+ public void TargetStartedHandler (TargetStartedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ indent++;
+ SetColor (eventColor);
+ WriteLine (String.Empty);
+ WriteLine (String.Format ("Target {0}:",args.TargetName));
+ ResetColor ();
+ }
+ }
+
+ public void TargetFinishedHandler (TargetFinishedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
+ (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
+ SetColor (eventColor);
+ WriteLine (String.Format ("Done building target \"{0}\" in project \"{1}\".{2}",
+ args.TargetName, args.ProjectFile,
+ args.Succeeded ? String.Empty : "-- FAILED"));
+ ResetColor ();
+ WriteLine (String.Empty);
+ }
+ indent--;
+ }
+
+ public void TaskStartedHandler (TaskStartedEventArgs args)
+ {
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
+ SetColor (eventColor);
+ WriteLine (String.Format ("Task \"{0}\"",args.TaskName));
+ ResetColor ();
+ }
+ indent++;
+ }
+
+ public void TaskFinishedHandler (TaskFinishedEventArgs args)
+ {
+ indent--;
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed) ||
+ (!args.Succeeded && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal))) {
+ SetColor (eventColor);
+ if (args.Succeeded)
+ WriteLine (String.Format ("Done executing task \"{0}\"", args.TaskName));
+ else
+ WriteLine (String.Format ("Task \"{0}\" execution -- FAILED", args.TaskName));
+ ResetColor ();
+ }
+ }
+
+ public void MessageHandler (BuildMessageEventArgs args)
+ {
+ if (IsMessageOk (args)) {
+ if (parent.no_message_color) {
+ ExecutePendingEventHandlers ();
+ WriteLine (args.Message);
+ } else {
+ ExecutePendingEventHandlers ();
+ SetColor (args.Importance == MessageImportance.High ? parent.highMessageColor : parent.messageColor);
+ WriteLine (args.Message);
+ ResetColor ();
+ }
+ }
+ }
+
+ public void WarningHandler (BuildWarningEventArgs args)
+ {
+ string msg = FormatWarningEvent (args);
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
+ ExecutePendingEventHandlers ();
+ SetColor (parent.warningColor);
+ WriteLineWithoutIndent (msg);
+ ResetColor ();
+ }
+ warnings.Add (msg);
+
+ List<string> list = null;
+ if (!warningsTable.TryGetValue (EventsAsString, out list))
+ warningsTable [EventsAsString] = list = new List<string> ();
+ list.Add (msg);
+
+ warningCount++;
+ }
+
+ public void ErrorHandler (BuildErrorEventArgs args)
+ {
+ string msg = FormatErrorEvent (args);
+ if (IsVerbosityGreaterOrEqual (LoggerVerbosity.Quiet)) {
+ ExecutePendingEventHandlers ();
+ SetColor (parent.errorColor);
+ WriteLineWithoutIndent (msg);
+ ResetColor ();
+ }
+ errors.Add (msg);
+
+ List<string> list = null;
+ if (!errorsTable.TryGetValue (EventsAsString, out list))
+ errorsTable [EventsAsString] = list = new List<string> ();
+ list.Add (msg);
+ errorCount++;
+ }
+
+ public void CustomHandler (CustomBuildEventArgs args)
+ {
+ }
+
+ private bool IsVerbosityGreaterOrEqual (LoggerVerbosity v)
+ {
+ if (v == LoggerVerbosity.Diagnostic) {
+ return LoggerVerbosity.Diagnostic <= verbosity;
+ } else if (v == LoggerVerbosity.Detailed) {
+ return LoggerVerbosity.Detailed <= verbosity;
+ } else if (v == LoggerVerbosity.Normal) {
+ return LoggerVerbosity.Normal <= verbosity;
+ } else if (v == LoggerVerbosity.Minimal) {
+ return LoggerVerbosity.Minimal <= verbosity;
+ } else if (v == LoggerVerbosity.Quiet) {
+ return true;
+ } else
+ return false;
+ }
+
+ void DumpItems (IEnumerable items)
+ {
+ if (parent.config.NoItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic) || items == null)
+ return;
+
+ SetColor (eventColor);
+ WriteLine (String.Empty);
+ WriteLine ("Initial Items:");
+ ResetColor ();
+ if (items == null)
+ return;
+
+ var items_table = new SortedDictionary<string, List<ITaskItem>> ();
+ foreach (DictionaryEntry de in items) {
+ string key = (string)de.Key;
+ if (!items_table.ContainsKey (key))
+ items_table [key] = new List<ITaskItem> ();
+
+ items_table [key].Add ((ITaskItem) de.Value);
+ }
+
+ foreach (string name in items_table.Keys) {
+ WriteLine (name);
+ indent ++;
+ foreach (ITaskItem item in items_table [name])
+ WriteLine (item.ItemSpec);
+ indent--;
+ }
+ }
+
+ string EventsAsString {
+ get {
+ if (current_events_string == null)
+ current_events_string = EventsToString ();
+ return current_events_string;
+ }
+ }
+
+ private bool IsMessageOk (BuildMessageEventArgs bsea)
+ {
+ if (bsea.Importance == MessageImportance.High && IsVerbosityGreaterOrEqual (LoggerVerbosity.Minimal)) {
+ return true;
+ } else if (bsea.Importance == MessageImportance.Normal && IsVerbosityGreaterOrEqual (LoggerVerbosity.Normal)) {
+ return true;
+ } else if (bsea.Importance == MessageImportance.Low && IsVerbosityGreaterOrEqual (LoggerVerbosity.Detailed)) {
+ return true;
+ } else
+ return false;
+ }
+
+ void DumpProperties (IEnumerable properties)
+ {
+ if (parent.config.NoItemAndPropertyList || !IsVerbosityGreaterOrEqual (LoggerVerbosity.Diagnostic))
+ return;
+
+ SetColor (eventColor);
+ WriteLine (String.Empty);
+ WriteLine ("Initial Properties:");
+ ResetColor ();
+
+ if (properties == null)
+ return;
+
+ var dict = new SortedDictionary<string, string> ();
+ foreach (DictionaryEntry de in properties)
+ dict [(string)de.Key] = (string)de.Value;
+
+ foreach (KeyValuePair<string, string> pair in dict)
+ WriteLine (String.Format ("{0} = {1}", pair.Key, pair.Value));
+ }
+
+ private void WriteLine (string message)
+ {
+ if (indent > 0) {
+ StringBuilder sb = new StringBuilder ();
+ for (int i = 0; i < indent; i++)
+ sb.Append ('\t');
+
+ string indent_str = sb.ToString ();
+
+ foreach (string line in message.Split (new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries))
+ parent.writeHandler (indent_str + line);
+ } else {
+ parent.writeHandler (message);
+ }
+ }
+
+ void ExecutePendingEventHandlers ()
+ {
+ foreach (var be in events)
+ be.ExecuteStartedHandler ();
+ }
+
+ string EventsToString ()
+ {
+ StringBuilder sb = new StringBuilder ();
+
+ string last_imported_target_file = String.Empty;
+ for (int i = 0; i < events.Count; i ++) {
+ var args = events [i].EventArgs;
+ ProjectStartedEventArgs pargs = args as ProjectStartedEventArgs;
+ if (pargs != null) {
+ sb.AppendFormat ("{0} ({1}) ->\n", pargs.ProjectFile,
+ String.IsNullOrEmpty (pargs.TargetNames) ?
+ "default targets" :
+ pargs.TargetNames);
+ last_imported_target_file = String.Empty;
+ continue;
+ }
+
+ TargetStartedEventArgs targs = args as TargetStartedEventArgs;
+ if (targs != null) {
+ if (targs.TargetFile != targs.ProjectFile && targs.TargetFile != last_imported_target_file)
+ // target from an imported file,
+ // and it hasn't been mentioned as yet
+ sb.AppendFormat ("{0} ", targs.TargetFile);
+
+ last_imported_target_file = targs.TargetFile;
+ sb.AppendFormat ("({0} target) ->\n", targs.TargetName);
+ }
+ }
+
+ return sb.ToString ();
+ }
+
+ void DumpPerformanceSummary ()
+ {
+ SetColor (eventColor);
+ WriteLine ("Target perfomance summary:");
+ ResetColor ();
+
+ foreach (var pi in targetPerfTable.OrderBy (pair => pair.Value.Time))
+ WriteLine (String.Format ("{0,10:0.000} ms {1,-50} {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
+
+ WriteLine (String.Empty);
+
+ SetColor (eventColor);
+ WriteLine ("Tasks perfomance summary:");
+ ResetColor ();
+
+ foreach (var pi in tasksPerfTable.OrderBy (pair => pair.Value.Time))
+ WriteLine (String.Format ("{0,10:0.000} ms {1,-50} {2,5} calls", pi.Value.Time.TotalMilliseconds, pi.Key, pi.Value.NumberOfCalls));
+
+ WriteLine (String.Empty);
+ }
+
+ private string FormatErrorEvent (BuildErrorEventArgs args)
+ {
+ // For some reason we get an 1-char empty string as Subcategory somtimes.
+ string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
+ string subcat = subprefix == "" ? "" : args.Subcategory;
+
+ if (args.LineNumber != 0){
+ if (args.ColumnNumber != 0 && !InEmacs)
+ return String.Format ("{0}({1},{2}): {3}{4}error {5}: {6}",
+ args.File, args.LineNumber, args.ColumnNumber,
+ subprefix, subcat, args.Code, args.Message);
+
+ return String.Format ("{0}({1}): {2}{3}error {4}: {5}",
+ args.File, args.LineNumber,
+ subprefix, subcat, args.Code, args.Message);
+ } else {
+ return String.Format ("{0}: {1}{2}error {3}: {4}", args.File, subprefix, subcat, args.Code,
+ args.Message);
+ }
+ }
+
+ static bool InEmacs = Environment.GetEnvironmentVariable ("EMACS") == "t";
+
+ private string FormatWarningEvent (BuildWarningEventArgs args)
+ {
+ // For some reason we get an 1-char empty string as Subcategory somtimes.
+ string subprefix = args.Subcategory == null || args.Subcategory == "" || args.Subcategory == " " ? "" : " ";
+ string subcat = subprefix == "" ? "" : args.Subcategory;
+
+ // FIXME: show more complicated args
+ if (args.LineNumber != 0){
+
+ if (args.ColumnNumber != 0 && !InEmacs) {
+ return String.Format ("{0}({1},{2}): {3}{4}warning {5}: {6}",
+ args.File, args.LineNumber, args.ColumnNumber,
+ subprefix, subcat, args.Code, args.Message);
+ }
+ return String.Format ("{0}({1}): {2}{3}warning {4}: {5}",
+ args.File, args.LineNumber,
+ subprefix, subcat, args.Code, args.Message);
+ } else {
+ return String.Format ("{0}: {1} warning {2}: {3}", args.File, args.Subcategory, args.Code,
+ args.Message);
+ }
+ }
+
+ void SetColor (ConsoleColor color)
+ {
+ if (parent.use_colors)
+ parent.colorSet (color);
+ }
+
+ void ResetColor ()
+ {
+ if (parent.use_colors)
+ parent.colorReset ();
+ }
+
+ private void WriteLineWithoutIndent (string message)
+ {
+ parent.writeHandler (message);
+ }
+ }
}
-}
-#endif
+ class BuildEvent {
+ public object Sender;
+ public BuildStatusEventArgs EventArgs;
+ public bool StartHandlerHasExecuted;
+ public ConsoleLogger ConsoleLogger;
+
+ public void ExecuteStartedHandler ()
+ {
+ if (StartHandlerHasExecuted)
+ return;
+
+ if (EventArgs is ProjectStartedEventArgs)
+ ConsoleLogger.ProjectStartedHandler (Sender, (ProjectStartedEventArgs)EventArgs);
+ else if (EventArgs is TargetStartedEventArgs)
+ ConsoleLogger.TargetStartedHandler (Sender, (TargetStartedEventArgs)EventArgs);
+ else if (EventArgs is TaskStartedEventArgs)
+ ConsoleLogger.TaskStartedHandler (Sender, (TaskStartedEventArgs)EventArgs);
+ else if (!(EventArgs is BuildStartedEventArgs))
+ throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
+
+ StartHandlerHasExecuted = true;
+ }
+
+ public void ExecuteFinishedHandler (BuildStatusEventArgs finished_args)
+ {
+ if (!StartHandlerHasExecuted)
+ return;
+
+ if (EventArgs is ProjectStartedEventArgs)
+ ConsoleLogger.ProjectFinishedHandler (Sender, finished_args as ProjectFinishedEventArgs);
+ else if (EventArgs is TargetStartedEventArgs)
+ ConsoleLogger.TargetFinishedHandler (Sender, finished_args as TargetFinishedEventArgs);
+ else if (EventArgs is TaskStartedEventArgs)
+ ConsoleLogger.TaskFinishedHandler (Sender, finished_args as TaskFinishedEventArgs);
+ else if (!(EventArgs is BuildStartedEventArgs))
+ throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
+ }
+ }
+
+ class PerfInfo {
+ public TimeSpan Time;
+ public int NumberOfCalls;
+ }
+}