GodotBuildLogger.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. using System;
  2. using System.IO;
  3. using System.Security;
  4. using Microsoft.Build.Framework;
  5. using GodotTools.Core;
  6. namespace GodotTools.BuildLogger
  7. {
  8. public class GodotBuildLogger : ILogger
  9. {
  10. public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
  11. public string Parameters { get; set; }
  12. public LoggerVerbosity Verbosity { get; set; }
  13. public void Initialize(IEventSource eventSource)
  14. {
  15. if (null == Parameters)
  16. throw new LoggerException("Log directory was not set.");
  17. var parameters = Parameters.Split(new[] { ';' });
  18. string logDir = parameters[0];
  19. if (string.IsNullOrEmpty(logDir))
  20. throw new LoggerException("Log directory was not set.");
  21. if (parameters.Length > 1)
  22. throw new LoggerException("Too many parameters passed.");
  23. string logFile = Path.Combine(logDir, "msbuild_log.txt");
  24. string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
  25. try
  26. {
  27. if (!Directory.Exists(logDir))
  28. Directory.CreateDirectory(logDir);
  29. logStreamWriter = new StreamWriter(logFile);
  30. issuesStreamWriter = new StreamWriter(issuesFile);
  31. }
  32. catch (Exception ex)
  33. {
  34. if (ex is UnauthorizedAccessException
  35. || ex is ArgumentNullException
  36. || ex is PathTooLongException
  37. || ex is DirectoryNotFoundException
  38. || ex is NotSupportedException
  39. || ex is ArgumentException
  40. || ex is SecurityException
  41. || ex is IOException)
  42. {
  43. throw new LoggerException("Failed to create log file: " + ex.Message);
  44. }
  45. else
  46. {
  47. // Unexpected failure
  48. throw;
  49. }
  50. }
  51. eventSource.ProjectStarted += eventSource_ProjectStarted;
  52. eventSource.TaskStarted += eventSource_TaskStarted;
  53. eventSource.MessageRaised += eventSource_MessageRaised;
  54. eventSource.WarningRaised += eventSource_WarningRaised;
  55. eventSource.ErrorRaised += eventSource_ErrorRaised;
  56. eventSource.ProjectFinished += eventSource_ProjectFinished;
  57. }
  58. void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
  59. {
  60. string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
  61. if (e.ProjectFile.Length > 0)
  62. line += $" [{e.ProjectFile}]";
  63. WriteLine(line);
  64. string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
  65. $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
  66. issuesStreamWriter.WriteLine(errorLine);
  67. }
  68. void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
  69. {
  70. string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
  71. if (!string.IsNullOrEmpty(e.ProjectFile))
  72. line += $" [{e.ProjectFile}]";
  73. WriteLine(line);
  74. string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
  75. $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
  76. issuesStreamWriter.WriteLine(warningLine);
  77. }
  78. private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
  79. {
  80. // BuildMessageEventArgs adds Importance to BuildEventArgs
  81. // Let's take account of the verbosity setting we've been passed in deciding whether to log the message
  82. if (e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)
  83. || e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)
  84. || e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
  85. {
  86. WriteLineWithSenderAndMessage(string.Empty, e);
  87. }
  88. }
  89. private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
  90. {
  91. // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
  92. // To keep this log clean, this logger will ignore these events.
  93. }
  94. private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
  95. {
  96. WriteLine(e.Message);
  97. indent++;
  98. }
  99. private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
  100. {
  101. indent--;
  102. WriteLine(e.Message);
  103. }
  104. /// <summary>
  105. /// Write a line to the log, adding the SenderName
  106. /// </summary>
  107. private void WriteLineWithSender(string line, BuildEventArgs e)
  108. {
  109. if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
  110. {
  111. // Well, if the sender name is MSBuild, let's leave it out for prettiness
  112. WriteLine(line);
  113. }
  114. else
  115. {
  116. WriteLine(e.SenderName + ": " + line);
  117. }
  118. }
  119. /// <summary>
  120. /// Write a line to the log, adding the SenderName and Message
  121. /// (these parameters are on all MSBuild event argument objects)
  122. /// </summary>
  123. private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
  124. {
  125. if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
  126. {
  127. // Well, if the sender name is MSBuild, let's leave it out for prettiness
  128. WriteLine(line + e.Message);
  129. }
  130. else
  131. {
  132. WriteLine(e.SenderName + ": " + line + e.Message);
  133. }
  134. }
  135. private void WriteLine(string line)
  136. {
  137. for (int i = indent; i > 0; i--)
  138. {
  139. logStreamWriter.Write("\t");
  140. }
  141. logStreamWriter.WriteLine(line);
  142. }
  143. public void Shutdown()
  144. {
  145. logStreamWriter.Close();
  146. issuesStreamWriter.Close();
  147. }
  148. private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
  149. {
  150. return Verbosity >= checkVerbosity;
  151. }
  152. private StreamWriter logStreamWriter;
  153. private StreamWriter issuesStreamWriter;
  154. private int indent;
  155. }
  156. }