From 4d12531c6c295b47b510c0d1f31ded27d25873a5 Mon Sep 17 00:00:00 2001 From: James Bradt Date: Thu, 8 Sep 2016 18:50:17 -0400 Subject: [PATCH] GH703: Add FileLogger switches to MSBuild Runner Addresses issue #703 Provides the ability to add FileLogger parameters to the MsBuild command line. The various fileloggerparameters (flp) can also be specified. The parameters that can be created are /fl, /fl[1 thru 9], /flp, and /flp[1 thru 9] --- .../Unit/Tools/MSBuild/MSBuildRunnerTests.cs | 63 +++++++++++ .../MSBuild/MSBuildSettingsExtensionsTests.cs | 38 +++++++ .../Tools/MSBuild/MSBuildSettingsTests.cs | 13 +++ .../Tools/MSBuild/MSBuildFileLogger.cs | 106 ++++++++++++++++++ .../Tools/MSBuild/MSBuildFileLoggerOutput.cs | 23 ++++ .../Tools/MSBuild/MSBuildRunner.cs | 32 ++++++ .../Tools/MSBuild/MSBuildSettings.cs | 7 ++ .../MSBuild/MSBuildSettingsExtensions.cs | 45 ++++++++ 8 files changed, 327 insertions(+) create mode 100644 src/Cake.Common/Tools/MSBuild/MSBuildFileLogger.cs create mode 100644 src/Cake.Common/Tools/MSBuild/MSBuildFileLoggerOutput.cs diff --git a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildRunnerTests.cs b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildRunnerTests.cs index 38147c1f21..ecabe5bd9f 100644 --- a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildRunnerTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildRunnerTests.cs @@ -802,6 +802,69 @@ public void Should_Append_Logger_To_Process_Arguments() Assert.Equal("/v:normal /target:Build /logger:B,A;C /logger:E,D /logger:F " + "\"/Working/src/Solution.sln\"", result.Args); } + + [Fact] + public void Should_Append_FileLogger_To_Process_Arguments() + { + // Given + var fixture = new MSBuildRunnerFixture(false); + fixture.Settings.AddFileLogger(new MSBuildFileLogger { AppendToLogFile = false, Encoding = "E", HideVerboseItemAndPropertyList = false, LogFile = "A", MSBuildFileLoggerOutput = MSBuildFileLoggerOutput.All, PerformanceSummaryEnabled = false, ShowCommandLine = false, ShowEventId = false, ShowTimestamp = false, SummaryDisabled = false, Verbosity = Verbosity.Diagnostic }); + + fixture.Settings.AddFileLogger(new MSBuildFileLogger { AppendToLogFile = true, HideVerboseItemAndPropertyList = true, MSBuildFileLoggerOutput = MSBuildFileLoggerOutput.ErrorsOnly, PerformanceSummaryEnabled = true, ShowCommandLine = true, ShowEventId = true, ShowTimestamp = true, SummaryDisabled = true, Verbosity = Verbosity.Minimal }); + + fixture.Settings.AddFileLogger(new MSBuildFileLogger { MSBuildFileLoggerOutput = MSBuildFileLoggerOutput.WarningsOnly, Verbosity = Verbosity.Normal }); + + fixture.Settings.AddFileLogger(new MSBuildFileLogger { Verbosity = Verbosity.Quiet }); + + fixture.Settings.AddFileLogger(new MSBuildFileLogger { Verbosity = Verbosity.Verbose }); + + fixture.Settings.AddFileLogger(new MSBuildFileLogger { }); + + fixture.Settings.AddFileLogger(); + + // When + var result = fixture.Run(); + // Then + Assert.Equal(@"/v:normal /target:Build /fl /flp:logfile=A;Encoding=E;Verbosity=Diagnostic /fl1 /flp1:Append;PerformanceSummary;NoSummary;ErrorsOnly;NoItemAndPropertyList;ShowCommandLine;ShowTimestamp;ShowEventId;Verbosity=Minimal /fl2 /flp2:WarningsOnly;Verbosity=Normal /fl3 /flp3:Verbosity=Quiet /fl4 /flp4:Verbosity=Verbose /fl5 /fl6 ""/Working/src/Solution.sln""", result.Args); + } + + [Fact] + public void Should_Append_Default_FileLogger_To_Process_Arguments() + { + // Given + var fixture = new MSBuildRunnerFixture(false); + + fixture.Settings.AddFileLogger(); + + // When + var result = fixture.Run(); + // Then + Assert.Equal(@"/v:normal /target:Build /fl ""/Working/src/Solution.sln""", result.Args); + } + + [Fact] + public void Should_Throw_Exception_For_Too_Many_FileLoggers() + { + // Given + var fixture = new MSBuildRunnerFixture(false); + + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + fixture.Settings.AddFileLogger(); + + // When + var ex = Assert.Throws(() => fixture.Run()); + // Then + Assert.Equal(@"Too Many FileLoggers", ex.Message); + } } } } \ No newline at end of file diff --git a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsExtensionsTests.cs b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsExtensionsTests.cs index 7a50a76c42..261d0b8f48 100644 --- a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsExtensionsTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsExtensionsTests.cs @@ -299,5 +299,43 @@ public void Should_Return_The_Same_Configuration() Assert.Equal(settings, result); } } + + public sealed class TheAddFileLoggersMethod + { + [Fact] + public void Should_Add_Logger() + { + // Given + var settings = new MSBuildSettings(); + var fileLogger = new MSBuildFileLogger(); + var fileLogger2 = new MSBuildFileLogger { LogFile = "A" }; + + // When + settings.AddFileLogger(fileLogger); + settings.AddFileLogger(fileLogger2); + + // Then + var loggers = settings.FileLoggers.ToArray(); + Assert.Equal(2, loggers.Length); + Assert.Equal(fileLogger, loggers[0]); + Assert.Equal(fileLogger2, loggers[1]); + Assert.Equal("A", loggers[1].LogFile); + } + + [Fact] + public void Should_Return_The_Same_Configuration() + { + // Given + var settings = new MSBuildSettings(); + + // When + var result = settings.AddFileLogger(new MSBuildFileLogger()); + var result1 = settings.AddFileLogger(); + + // Then + Assert.Equal(settings, result); + Assert.Equal(settings, result1); + } + } } } \ No newline at end of file diff --git a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsTests.cs b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsTests.cs index 869d71b1c7..ade3d578ea 100644 --- a/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsTests.cs +++ b/src/Cake.Common.Tests/Unit/Tools/MSBuild/MSBuildSettingsTests.cs @@ -126,5 +126,18 @@ public void Should_Be_Empty_By_Default() Assert.Empty(settings.Loggers); } } + + public sealed class TheFileLoggersProperty + { + [Fact] + public void Should_Be_Empty_By_Default() + { + // Given + var settings = new MSBuildSettings(); + + // Then + Assert.Empty(settings.FileLoggers); + } + } } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/MSBuild/MSBuildFileLogger.cs b/src/Cake.Common/Tools/MSBuild/MSBuildFileLogger.cs new file mode 100644 index 0000000000..bb18f83089 --- /dev/null +++ b/src/Cake.Common/Tools/MSBuild/MSBuildFileLogger.cs @@ -0,0 +1,106 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Cake.Core.Diagnostics; + +namespace Cake.Common.Tools.MSBuild +{ + /// + /// Contains settings for specifying a MSBuild file logger. + /// + public class MSBuildFileLogger + { + /// + /// Initializes a new instance of the class. + /// + public MSBuildFileLogger() + { + } + + /// + /// Gets or sets a value indicating whether PerformanceSummary will Show the time that’s spent in tasks, targets, and projects. + /// + public bool PerformanceSummaryEnabled { get; set; } + + /// + /// Gets or sets a value indicating whether Summary will Show the error and warning summary at the end. + /// + public bool SummaryDisabled { get; set; } + + /// + /// Gets or sets show ErrorsOnly, WarningsOnly, or All + /// + public MSBuildFileLoggerOutput MSBuildFileLoggerOutput { get; set; } + + /// + /// Gets or sets a value indicating whether NoItemAndPropertyList will be set to Don't show the list of items and properties that would appear at the start of each project build if the verbosity level is set to diagnostic. + /// + public bool HideVerboseItemAndPropertyList { get; set; } + + /// + /// Gets or sets a value indicating whether ShowCommandLine. Show TaskCommandLineEvent messages. + /// + public bool ShowCommandLine { get; set; } + + /// + /// Gets or sets a value indicating whether ShowTimestamp. Show the timestamp as a prefix to any message. + /// + public bool ShowTimestamp { get; set; } + + /// + /// Gets or sets a value indicating whether ShowEventId. Show the event ID for each started event, finished event, and message. + /// + public bool ShowEventId { get; set; } + + /// + /// Gets or sets Verbosity. Override the /verbosity setting for this logger. + /// + /// specify the following verbosity levels: q[uiet], m[inimal], n[ormal], v[erbose] (detailed), and diag[nostic]. + /// + public Verbosity? Verbosity { get; set; } + + /// + /// Gets or sets LogFile. The path to the log file into which the build log is written. + /// + /// an empty string will use msbuild.log + /// + public string LogFile { get; set; } + + /// + /// Gets or sets a value indicating whether the build log is appended to the log file or overwrites it. When true, the build log is appended to the log file. + /// + public bool AppendToLogFile { get; set; } + + /// + /// Gets or sets Specifies the encoding for the file (for example, UTF-8, Unicode, or ASCII). + /// + public string Encoding { get; set; } + + /// + /// process the file logger config and return parameters as a string + /// + /// The parameters separated by semi-colons. + public string GetParameters() + { + var parameters = new List(); + parameters.Add(!string.IsNullOrWhiteSpace(LogFile) ? $"logfile={LogFile}" : null); + parameters.Add(!string.IsNullOrWhiteSpace(Encoding) ? $"Encoding={Encoding}" : null); + parameters.Add(AppendToLogFile ? "Append" : null); + parameters.Add(PerformanceSummaryEnabled ? "PerformanceSummary" : null); + parameters.Add(SummaryDisabled ? "NoSummary" : null); + parameters.Add(MSBuildFileLoggerOutput == MSBuildFileLoggerOutput.ErrorsOnly ? "ErrorsOnly" : null); + parameters.Add(MSBuildFileLoggerOutput == MSBuildFileLoggerOutput.WarningsOnly ? "WarningsOnly" : null); + parameters.Add(HideVerboseItemAndPropertyList ? "NoItemAndPropertyList" : null); + parameters.Add(ShowCommandLine ? "ShowCommandLine" : null); + parameters.Add(ShowTimestamp ? "ShowTimestamp" : null); + parameters.Add(ShowEventId ? "ShowEventId" : null); + parameters.Add(Verbosity != null ? $"Verbosity={Verbosity.Value.ToString()}" : null); + + return string.Join(";", parameters.Where(p => p != null)); + } + } +} diff --git a/src/Cake.Common/Tools/MSBuild/MSBuildFileLoggerOutput.cs b/src/Cake.Common/Tools/MSBuild/MSBuildFileLoggerOutput.cs new file mode 100644 index 0000000000..83e90ad274 --- /dev/null +++ b/src/Cake.Common/Tools/MSBuild/MSBuildFileLoggerOutput.cs @@ -0,0 +1,23 @@ +namespace Cake.Common.Tools.MSBuild +{ + /// + /// the type of file logger output to generate + /// + public enum MSBuildFileLoggerOutput + { + /// + /// show errors and warnings + /// + All = 0, + + /// + /// show errors only + /// + ErrorsOnly = 1, + + /// + /// show warnings only + /// + WarningsOnly = 2, + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/MSBuild/MSBuildRunner.cs b/src/Cake.Common/Tools/MSBuild/MSBuildRunner.cs index 49ed20cecc..7d48b3f51e 100644 --- a/src/Cake.Common/Tools/MSBuild/MSBuildRunner.cs +++ b/src/Cake.Common/Tools/MSBuild/MSBuildRunner.cs @@ -111,12 +111,44 @@ private ProcessArgumentBuilder GetArguments(FilePath solution, MSBuildSettings s } } + // Got any file loggers? + if (settings.FileLoggers.Count > 0) + { + var arguments = settings.FileLoggers.Select((logger, indx) => + { + return GetLoggerArgument(indx, logger); + }); + + foreach (var argument in arguments) + { + builder.Append(argument); + } + } + // Add the solution as the last parameter. builder.AppendQuoted(solution.MakeAbsolute(_environment).FullPath); return builder; } + private static string GetLoggerArgument(int indx, MSBuildFileLogger logger) + { + if (indx >= 10) + { + throw new InvalidOperationException("Too Many FileLoggers"); + } + + var cntr = indx == 0 ? string.Empty : indx.ToString(); + var argument = $"/fl{cntr}"; + + var parameters = logger.GetParameters(); + if (!string.IsNullOrWhiteSpace(parameters)) + { + argument = $"{argument} /flp{cntr}:{parameters}"; + } + return argument; + } + private static string GetLoggerArgument(MSBuildLogger logger) { string argument = "/logger:"; diff --git a/src/Cake.Common/Tools/MSBuild/MSBuildSettings.cs b/src/Cake.Common/Tools/MSBuild/MSBuildSettings.cs index 677c06c0ac..3af690ae4d 100644 --- a/src/Cake.Common/Tools/MSBuild/MSBuildSettings.cs +++ b/src/Cake.Common/Tools/MSBuild/MSBuildSettings.cs @@ -17,6 +17,7 @@ public sealed class MSBuildSettings : ToolSettings private readonly HashSet _targets; private readonly Dictionary> _properties; private readonly List _loggers; + private readonly List _fileLoggers; /// /// Gets the targets. @@ -81,6 +82,11 @@ public sealed class MSBuildSettings : ToolSettings /// public ICollection Loggers => _loggers; + /// + /// Gets the file loggers + /// + public ICollection FileLoggers => _fileLoggers; + /// /// Initializes a new instance of the class. /// @@ -89,6 +95,7 @@ public MSBuildSettings() _targets = new HashSet(StringComparer.OrdinalIgnoreCase); _properties = new Dictionary>(StringComparer.OrdinalIgnoreCase); _loggers = new List(); + _fileLoggers = new List(); ToolVersion = MSBuildToolVersion.Default; Configuration = string.Empty; diff --git a/src/Cake.Common/Tools/MSBuild/MSBuildSettingsExtensions.cs b/src/Cake.Common/Tools/MSBuild/MSBuildSettingsExtensions.cs index a8f8b9079f..dcbd53ad58 100644 --- a/src/Cake.Common/Tools/MSBuild/MSBuildSettingsExtensions.cs +++ b/src/Cake.Common/Tools/MSBuild/MSBuildSettingsExtensions.cs @@ -200,5 +200,50 @@ public static MSBuildSettings WithLogger(this MSBuildSettings settings, string l }); return settings; } + + /// + /// Adds a file logger. + /// + /// each file logger will be declared in the order added + /// the first file logger will match up to the /fl parameter + /// the next nine (max) file loggers will match up to the /fl1 through /fl9 respectively + /// + /// The settings. + /// Parameters to be passed to the logger. + /// The same instance so that multiple calls can be chained. + public static MSBuildSettings AddFileLogger(this MSBuildSettings settings, MSBuildFileLogger fileLoggerParameters) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + if (fileLoggerParameters == null) + { + throw new ArgumentNullException(nameof(fileLoggerParameters)); + } + settings.FileLoggers.Add(fileLoggerParameters); + return settings; + } + + /// + /// Adds a file logger with all the default settings. + /// + /// each file logger will be declared in the order added + /// the first file logger will match up to the /fl parameter + /// the next nine (max) file loggers will match up to the /fl1 through /fl9 respectively + /// + /// The settings. + /// The same instance so that multiple calls can be chained. + public static MSBuildSettings AddFileLogger(this MSBuildSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + settings.FileLoggers.Add(new MSBuildFileLogger()); + return settings; + } } } \ No newline at end of file