4d7b7d9b73
This upgrade is needed in order to support reading and editing project files that use Sdks as well as other new features. A common example in 3.2 is having to specify a PackageReference version with a child element rather than the attribute. This is no longer the case now. Partial cherry-pick off3bcd5f8dd
Most of the other changes from that commit were already partially cherry-picked in3928fe200f
.
198 lines
7 KiB
C#
198 lines
7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Security;
|
|
using Microsoft.Build.Framework;
|
|
|
|
namespace GodotTools.BuildLogger
|
|
{
|
|
public class GodotBuildLogger : ILogger
|
|
{
|
|
public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
|
|
|
|
public string Parameters { get; set; }
|
|
public LoggerVerbosity Verbosity { get; set; }
|
|
|
|
public void Initialize(IEventSource eventSource)
|
|
{
|
|
if (null == Parameters)
|
|
throw new LoggerException("Log directory was not set.");
|
|
|
|
var parameters = Parameters.Split(new[] {';'});
|
|
|
|
string logDir = parameters[0];
|
|
|
|
if (string.IsNullOrEmpty(logDir))
|
|
throw new LoggerException("Log directory was not set.");
|
|
|
|
if (parameters.Length > 1)
|
|
throw new LoggerException("Too many parameters passed.");
|
|
|
|
string logFile = Path.Combine(logDir, "msbuild_log.txt");
|
|
string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
|
|
|
|
try
|
|
{
|
|
if (!Directory.Exists(logDir))
|
|
Directory.CreateDirectory(logDir);
|
|
|
|
logStreamWriter = new StreamWriter(logFile);
|
|
issuesStreamWriter = new StreamWriter(issuesFile);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (ex is UnauthorizedAccessException
|
|
|| ex is ArgumentNullException
|
|
|| ex is PathTooLongException
|
|
|| ex is DirectoryNotFoundException
|
|
|| ex is NotSupportedException
|
|
|| ex is ArgumentException
|
|
|| ex is SecurityException
|
|
|| ex is IOException)
|
|
{
|
|
throw new LoggerException("Failed to create log file: " + ex.Message);
|
|
}
|
|
else
|
|
{
|
|
// Unexpected failure
|
|
throw;
|
|
}
|
|
}
|
|
|
|
eventSource.ProjectStarted += eventSource_ProjectStarted;
|
|
eventSource.TaskStarted += eventSource_TaskStarted;
|
|
eventSource.MessageRaised += eventSource_MessageRaised;
|
|
eventSource.WarningRaised += eventSource_WarningRaised;
|
|
eventSource.ErrorRaised += eventSource_ErrorRaised;
|
|
eventSource.ProjectFinished += eventSource_ProjectFinished;
|
|
}
|
|
|
|
void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
|
|
{
|
|
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
|
|
|
|
if (e.ProjectFile.Length > 0)
|
|
line += $" [{e.ProjectFile}]";
|
|
|
|
WriteLine(line);
|
|
|
|
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
|
|
$@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
|
|
issuesStreamWriter.WriteLine(errorLine);
|
|
}
|
|
|
|
void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
|
|
{
|
|
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
|
|
|
|
if (!string.IsNullOrEmpty(e.ProjectFile))
|
|
line += $" [{e.ProjectFile}]";
|
|
|
|
WriteLine(line);
|
|
|
|
string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
|
|
$@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
|
|
issuesStreamWriter.WriteLine(warningLine);
|
|
}
|
|
|
|
private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
|
|
{
|
|
// BuildMessageEventArgs adds Importance to BuildEventArgs
|
|
// Let's take account of the verbosity setting we've been passed in deciding whether to log the message
|
|
if (e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)
|
|
|| e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)
|
|
|| e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
|
|
{
|
|
WriteLineWithSenderAndMessage(string.Empty, e);
|
|
}
|
|
}
|
|
|
|
private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
|
|
{
|
|
// TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
|
|
// To keep this log clean, this logger will ignore these events.
|
|
}
|
|
|
|
private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
|
|
{
|
|
WriteLine(e.Message);
|
|
indent++;
|
|
}
|
|
|
|
private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
|
|
{
|
|
indent--;
|
|
WriteLine(e.Message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a line to the log, adding the SenderName
|
|
/// </summary>
|
|
private void WriteLineWithSender(string line, BuildEventArgs e)
|
|
{
|
|
if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
|
WriteLine(line);
|
|
}
|
|
else
|
|
{
|
|
WriteLine(e.SenderName + ": " + line);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a line to the log, adding the SenderName and Message
|
|
/// (these parameters are on all MSBuild event argument objects)
|
|
/// </summary>
|
|
private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
|
|
{
|
|
if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Well, if the sender name is MSBuild, let's leave it out for prettiness
|
|
WriteLine(line + e.Message);
|
|
}
|
|
else
|
|
{
|
|
WriteLine(e.SenderName + ": " + line + e.Message);
|
|
}
|
|
}
|
|
|
|
private void WriteLine(string line)
|
|
{
|
|
for (int i = indent; i > 0; i--)
|
|
{
|
|
logStreamWriter.Write("\t");
|
|
}
|
|
|
|
logStreamWriter.WriteLine(line);
|
|
}
|
|
|
|
public void Shutdown()
|
|
{
|
|
logStreamWriter.Close();
|
|
issuesStreamWriter.Close();
|
|
}
|
|
|
|
private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
|
|
{
|
|
return Verbosity >= checkVerbosity;
|
|
}
|
|
|
|
private StreamWriter logStreamWriter;
|
|
private StreamWriter issuesStreamWriter;
|
|
private int indent;
|
|
}
|
|
|
|
internal static class StringExtensions
|
|
{
|
|
public static string CsvEscape(this string value, char delimiter = ',')
|
|
{
|
|
bool hasSpecialChar = value.IndexOfAny(new[] {'\"', '\n', '\r', delimiter}) != -1;
|
|
|
|
if (hasSpecialChar)
|
|
return "\"" + value.Replace("\"", "\"\"") + "\"";
|
|
|
|
return value;
|
|
}
|
|
}
|
|
}
|