6992685b86
Former-commit-id: 0a113cb3a6feb7873f632839b1307cc6033cd595
1044 lines
31 KiB
C#
1044 lines
31 KiB
C#
//
|
|
// ConsoleLogger.cs: Outputs to the console
|
|
//
|
|
// Author:
|
|
// Marek Sieradzki (marek.sieradzki@gmail.com)
|
|
//
|
|
// (C) 2005 Marek Sieradzki
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security;
|
|
using System.Text;
|
|
using Microsoft.Build.Framework;
|
|
|
|
#if MICROSOFT_BUILD_DLL
|
|
namespace Microsoft.Build.Logging
|
|
#else
|
|
namespace Microsoft.Build.BuildEngine
|
|
#endif
|
|
{
|
|
public class ConsoleLogger : ILogger {
|
|
|
|
string parameters;
|
|
LoggerVerbosity verbosity;
|
|
WriteHandler writeHandler;
|
|
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 (verbosity, null, null, null)
|
|
{
|
|
}
|
|
|
|
public ConsoleLogger (LoggerVerbosity verbosity,
|
|
WriteHandler write,
|
|
ColorSetter colorSet,
|
|
ColorResetter colorReset)
|
|
{
|
|
this.verbosity = verbosity;
|
|
if (write == null)
|
|
this.writeHandler += new WriteHandler (WriteHandlerFunction);
|
|
else
|
|
this.writeHandler += write;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WriteHandlerFunction (string message)
|
|
{
|
|
Console.WriteLine (message);
|
|
}
|
|
|
|
bool TryParseConsoleColor (string color_str, ref ConsoleColor color)
|
|
{
|
|
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;
|
|
}
|
|
|
|
ConsoleColor GetBrightColorFor (ConsoleColor color)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
class ConsoleLoggerParameter
|
|
{
|
|
public ConsoleLoggerParameter ()
|
|
{
|
|
ShowSummary = true;
|
|
}
|
|
public bool PerformanceSummary { get; set; }
|
|
public bool ShowSummary { get; set; }
|
|
public bool NoItemAndPropertyList { get; set; }
|
|
}
|
|
|
|
public void ApplyParameter (string parameterName,
|
|
string parameterValue)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
bool TrySplitKeyValuePair (string pair, out string key, out string value)
|
|
{
|
|
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 ();
|
|
}
|
|
|
|
ConcurrentDictionary<object,BuildRecord> build_records = new ConcurrentDictionary<object, BuildRecord> ();
|
|
|
|
object dummy_key = new object ();
|
|
|
|
BuildRecord GetBuildRecord (object sender)
|
|
{
|
|
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).
|
|
var key = sender as IBuildEngine3 ?? dummy_key;
|
|
return build_records.GetOrAdd (key, _ => new BuildRecord (this));
|
|
}
|
|
|
|
public void BuildStartedHandler (object sender, BuildStartedEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).BuildStartedHandler (sender, args);
|
|
}
|
|
|
|
public void BuildFinishedHandler (object sender, BuildFinishedEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).BuildFinishedHandler (args);
|
|
((IDictionary) 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)
|
|
{
|
|
GetBuildRecord (sender).TargetFinishedHandler (args);
|
|
}
|
|
public void TaskStartedHandler (object sender, TaskStartedEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).TaskStartedHandler (args);
|
|
}
|
|
public void TaskFinishedHandler (object sender, TaskFinishedEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).TaskFinishedHandler (args);
|
|
}
|
|
public void MessageHandler (object sender, BuildMessageEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).MessageHandler (args);
|
|
}
|
|
public void WarningHandler (object sender, BuildWarningEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).WarningHandler (args);
|
|
}
|
|
public void ErrorHandler (object sender, BuildErrorEventArgs args)
|
|
{
|
|
GetBuildRecord (sender).ErrorHandler (args);
|
|
}
|
|
|
|
[MonoTODO]
|
|
public void CustomEventHandler (object sender, CustomBuildEventArgs args)
|
|
{
|
|
build_records [sender].CustomHandler (args);
|
|
}
|
|
|
|
void SetColor (ConsoleColor color)
|
|
{
|
|
if (use_colors)
|
|
colorSet (color);
|
|
}
|
|
|
|
void ResetColor ()
|
|
{
|
|
if (use_colors)
|
|
colorReset ();
|
|
}
|
|
|
|
private void ParseParameters ()
|
|
{
|
|
string[] splittedParameters = parameters.Split (';');
|
|
foreach (string s in splittedParameters )
|
|
ApplyParameter (s, null);
|
|
}
|
|
|
|
public virtual void Shutdown ()
|
|
{
|
|
if (eventSource == null)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
public string Parameters {
|
|
get {
|
|
return parameters;
|
|
}
|
|
set {
|
|
if (value == null)
|
|
throw new ArgumentNullException ();
|
|
parameters = value;
|
|
ParseParameters ();
|
|
}
|
|
}
|
|
|
|
public bool ShowSummary {
|
|
get { return config.ShowSummary; }
|
|
set { config.ShowSummary = value; }
|
|
}
|
|
|
|
public bool SkipProjectStartedText {
|
|
get { return skipProjectStartedText; }
|
|
set { skipProjectStartedText = value; }
|
|
}
|
|
|
|
public LoggerVerbosity Verbosity {
|
|
get { return verbosity; }
|
|
set { verbosity = value; }
|
|
}
|
|
|
|
protected WriteHandler WriteHandler {
|
|
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 performance 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 performance 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
var pfa = finished_args as ProjectFinishedEventArgs;
|
|
// FIXME: BuildFinishedHandlerActual sends us BuildFinishedEventArgs via PopEvent
|
|
if (pfa == null)
|
|
return;
|
|
|
|
ConsoleLogger.ProjectFinishedHandler (Sender, pfa);
|
|
} else if (EventArgs is TargetStartedEventArgs) {
|
|
var fa = finished_args as TargetFinishedEventArgs;
|
|
// FIXME: BuildFinishedHandlerActual sends us BuildFinishedEventArgs via PopEvent
|
|
if (fa == null)
|
|
return;
|
|
|
|
ConsoleLogger.TargetFinishedHandler (Sender, fa);
|
|
} else if (EventArgs is TaskStartedEventArgs) {
|
|
// FIXME: BuildFinishedHandlerActual sends us BuildFinishedEventArgs via PopEvent
|
|
if (!(finished_args is TaskFinishedEventArgs))
|
|
return;
|
|
|
|
ConsoleLogger.TaskFinishedHandler (Sender, (TaskFinishedEventArgs) finished_args);
|
|
} else if (!(EventArgs is BuildStartedEventArgs))
|
|
throw new InvalidOperationException ("Unexpected event on the stack, type: " + EventArgs.GetType ());
|
|
}
|
|
}
|
|
|
|
class PerfInfo {
|
|
public TimeSpan Time;
|
|
public int NumberOfCalls;
|
|
}
|
|
}
|