diff --git a/dir.props b/dir.props index 407c0c2ffda..35cbb91755d 100644 --- a/dir.props +++ b/dir.props @@ -308,6 +308,8 @@ $(DefineConstants);FEATURE_REGISTRYHIVE_DYNDATA $(DefineConstants);FEATURE_RESGEN $(DefineConstants);FEATURE_RESOURCE_EXPOSURE + + $(DefineConstants);FEATURE_RESOURCEMANAGER_GETRESOURCESET $(DefineConstants);FEATURE_RESX_RESOURCE_READER $(DefineConstants);FEATURE_RTLMOVEMEMORY $(DefineConstants);FEATURE_RUN_EXE_IN_TESTS diff --git a/ref/net46/Microsoft.Build/Microsoft.Build.cs b/ref/net46/Microsoft.Build/Microsoft.Build.cs index 13424e130a9..a5b7e11a2ea 100644 --- a/ref/net46/Microsoft.Build/Microsoft.Build.cs +++ b/ref/net46/Microsoft.Build/Microsoft.Build.cs @@ -940,6 +940,7 @@ public BuildParameters(Microsoft.Build.Evaluation.ProjectCollection projectColle public System.Collections.Generic.ICollection Toolsets { get { throw null; } } public System.Globalization.CultureInfo UICulture { get { throw null; } set { } } public bool UseSynchronousLogging { get { throw null; } set { } } + public System.Collections.Generic.ISet WarningsAsErrors { get { throw null; } set { } } public Microsoft.Build.Execution.BuildParameters Clone() { throw null; } public Microsoft.Build.Evaluation.Toolset GetToolset(string toolsVersion) { throw null; } } diff --git a/src/XMakeBuildEngine/BackEnd/BuildManager/BuildManager.cs b/src/XMakeBuildEngine/BackEnd/BuildManager/BuildManager.cs index 9f3d793288e..92872a5e736 100644 --- a/src/XMakeBuildEngine/BackEnd/BuildManager/BuildManager.cs +++ b/src/XMakeBuildEngine/BackEnd/BuildManager/BuildManager.cs @@ -424,7 +424,7 @@ public void BeginBuild(BuildParameters parameters) } // Set up the logging service. - ILoggingService loggingService = CreateLoggingService(_buildParameters.Loggers, _buildParameters.ForwardingLoggers); + ILoggingService loggingService = CreateLoggingService(_buildParameters.Loggers, _buildParameters.ForwardingLoggers, _buildParameters.WarningsAsErrors); _nodeManager.RegisterPacketHandler(NodePacketType.LogMessage, LogMessagePacket.FactoryForDeserialization, loggingService as INodePacketHandler); try @@ -575,7 +575,11 @@ public BuildSubmission PendBuildRequest(BuildRequestData requestData) public BuildResult BuildRequest(BuildRequestData requestData) { BuildSubmission submission = PendBuildRequest(requestData); - return submission.Execute(); + BuildResult result = submission.Execute(); + + SetOverallResultIfWarningsAsErrors(result); + + return result; } /// @@ -621,6 +625,12 @@ public void EndBuild() if (loggingService != null) { + // Override the build success if the user specified /warnaserror and any errors were logged outside of a build submission. + if (_overallBuildSuccess && loggingService.HasBuildSubmissionLoggedErrors(BuildEventContext.InvalidSubmissionId)) + { + _overallBuildSuccess = false; + } + loggingService.LogBuildFinished(_overallBuildSuccess); } @@ -1578,6 +1588,9 @@ private void ReportResultsToSubmission(BuildResult result) if (_buildSubmissions.ContainsKey(result.SubmissionId)) { BuildSubmission submission = _buildSubmissions[result.SubmissionId]; + + SetOverallResultIfWarningsAsErrors(result); + submission.CompleteResults(result); // If the request failed because we caught an exception from the loggers, we can assume we will receive no more logging messages for @@ -1723,7 +1736,7 @@ private void OnProjectStarted(object sender, ProjectStartedEventArgs e) /// /// Creates a logging service around the specified set of loggers. /// - private ILoggingService CreateLoggingService(IEnumerable loggers, IEnumerable forwardingLoggers) + private ILoggingService CreateLoggingService(IEnumerable loggers, IEnumerable forwardingLoggers, ISet warningsAsErrors) { int cpuCount = _buildParameters.MaxNodeCount; @@ -1750,6 +1763,7 @@ private ILoggingService CreateLoggingService(IEnumerable loggers, IEnum loggingService.OnLoggingThreadException += _loggingThreadExceptionEventHandler; loggingService.OnProjectStarted += _projectStartedEventHandler; loggingService.OnProjectFinished += _projectFinishedEventHandler; + loggingService.WarningsAsErrors = warningsAsErrors; try { @@ -1820,6 +1834,23 @@ private I ExpectPacketType(INodePacket packet, NodePacketType expectedType) w return castPacket; } + /// + /// Sets the overall result of a build only if the user had specified /warnaserror and there were any errors. + /// This ensures the old behavior stays intact where builds could succeed even if a failure was logged. + /// + private void SetOverallResultIfWarningsAsErrors(BuildResult result) + { + if (result.OverallResult == BuildResultCode.Success) + { + ILoggingService loggingService = ((IBuildComponentHost)this).LoggingService; + + if (loggingService.HasBuildSubmissionLoggedErrors(result.SubmissionId)) + { + result.SetOverallResult(overallResult: false); + } + } + } + /// /// Shutdown the logging service /// diff --git a/src/XMakeBuildEngine/BackEnd/BuildManager/BuildParameters.cs b/src/XMakeBuildEngine/BackEnd/BuildManager/BuildParameters.cs index 3249229a840..34459823cfc 100644 --- a/src/XMakeBuildEngine/BackEnd/BuildManager/BuildParameters.cs +++ b/src/XMakeBuildEngine/BackEnd/BuildManager/BuildParameters.cs @@ -200,6 +200,11 @@ public class BuildParameters : INodePacketTranslatable /// private bool _onlyLogCriticalEvents = false; + /// + /// A list of warnings to treat as errors. + /// + private ISet _warningsAsErrors = null; + /// /// The location of the toolset definitions. /// @@ -320,6 +325,7 @@ private BuildParameters(BuildParameters other) _disableInProcNode = other._disableInProcNode; _logTaskInputs = other._logTaskInputs; _logInitialPropertiesAndItems = other._logInitialPropertiesAndItems; + _warningsAsErrors = other._warningsAsErrors == null ? null : new HashSet(other._warningsAsErrors, StringComparer.OrdinalIgnoreCase); } #if FEATURE_THREAD_PRIORITY @@ -583,6 +589,15 @@ public bool OnlyLogCriticalEvents set { _onlyLogCriticalEvents = value; } } + /// + /// A list of warnings to treat as errors. To treat all warnings as errors, set this to an empty . + /// + public ISet WarningsAsErrors + { + get { return _warningsAsErrors; } + set { _warningsAsErrors = value; } + } + /// /// Locations to search for toolsets. /// diff --git a/src/XMakeBuildEngine/BackEnd/Components/Logging/BuildEventArgTransportSink.cs b/src/XMakeBuildEngine/BackEnd/Components/Logging/BuildEventArgTransportSink.cs index 2305a0e6c55..bce7890e6de 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Logging/BuildEventArgTransportSink.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Logging/BuildEventArgTransportSink.cs @@ -81,6 +81,19 @@ public bool HaveLoggedBuildFinishedEvent set; } + /// + /// This property is ignored by this event sink and relies on the receiver to treat warnings as errors. + /// + public ISet WarningsAsErrors + { + get; + set; + } + + /// + /// This property is ignored by this event sink and relies on the receiver to keep track of whether or not any errors have been logged. + /// + public ISet BuildSubmissionIdsThatHaveLoggedErrors { get; } = null; #endregion #region IBuildEventSink Methods diff --git a/src/XMakeBuildEngine/BackEnd/Components/Logging/EventSourceSink.cs b/src/XMakeBuildEngine/BackEnd/Components/Logging/EventSourceSink.cs index 7b11f16d6d1..c023231b9bf 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Logging/EventSourceSink.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Logging/EventSourceSink.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- using System; +using System.Collections.Generic; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -129,6 +130,24 @@ public bool HaveLoggedBuildFinishedEvent get; set; } + + /// + /// A list of warnings to treat as errors. If null, nothing is treated as an error. If an empty set, all warnings are treated as errors. + /// + public ISet WarningsAsErrors + { + get; + set; + } + + /// + /// A list of build submission IDs that have logged errors. If an error is logged outside of a submission, the submission ID is . + /// + public ISet BuildSubmissionIdsThatHaveLoggedErrors + { + get; + } = new HashSet(); + #endregion #region Methods @@ -203,7 +222,34 @@ public void Consume(BuildEventArgs buildEvent) } else if (buildEvent is BuildWarningEventArgs) { - this.RaiseWarningEvent(null, (BuildWarningEventArgs)buildEvent); + BuildWarningEventArgs warningEvent = (BuildWarningEventArgs) buildEvent; + + // Treat this warning as an error if an empty set of warnings was specified or this code was specified + if (WarningsAsErrors != null && (WarningsAsErrors.Count == 0 || WarningsAsErrors.Contains(warningEvent.Code))) + { + BuildErrorEventArgs errorEvent = new BuildErrorEventArgs( + warningEvent.Subcategory, + warningEvent.Code, + warningEvent.File, + warningEvent.LineNumber, + warningEvent.ColumnNumber, + warningEvent.EndLineNumber, + warningEvent.EndColumnNumber, + warningEvent.Message, + warningEvent.HelpKeyword, + warningEvent.SenderName, + warningEvent.Timestamp) + { + BuildEventContext = warningEvent.BuildEventContext, + ProjectFile = warningEvent.ProjectFile, + }; + + this.RaiseErrorEvent(null, errorEvent); + } + else + { + this.RaiseWarningEvent(null, warningEvent); + } } else if (buildEvent is BuildErrorEventArgs) { @@ -308,6 +354,9 @@ private void RaiseMessageEvent(object sender, BuildMessageEventArgs buildEvent) /// ExceptionHandling.IsCriticalException exceptions will not be wrapped private void RaiseErrorEvent(object sender, BuildErrorEventArgs buildEvent) { + // Keep track of build submissions that have logged errors. If there is no build context, add BuildEventContext.InvalidSubmissionId. + BuildSubmissionIdsThatHaveLoggedErrors.Add(buildEvent?.BuildEventContext?.SubmissionId ?? BuildEventContext.InvalidSubmissionId); + if (ErrorRaised != null) { try diff --git a/src/XMakeBuildEngine/BackEnd/Components/Logging/ILoggingService.cs b/src/XMakeBuildEngine/BackEnd/Components/Logging/ILoggingService.cs index 6aab65dfc10..9e4f0c1e068 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Logging/ILoggingService.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Logging/ILoggingService.cs @@ -153,8 +153,24 @@ bool RunningOnRemoteNode get; set; } + + /// + /// Set of warnings to treat as errors. An empty non-null set will treat all warnings as errors. + /// + ISet WarningsAsErrors + { + get; + set; + } #endregion + /// + /// Determines if the specified submission has logged an errors. + /// + /// The ID of the build submission. A value of "0" means that an error was logged outside of any build submission. + /// true if the build submission logged an errors, otherwise false. + bool HasBuildSubmissionLoggedErrors(int submissionId); + #region Register /// @@ -447,6 +463,20 @@ bool HaveLoggedBuildFinishedEvent set; } + /// + /// A list of warnings to treat as errors. If null, nothing is treated as an error. If an empty set, all warnings are treated as errors. + /// + ISet WarningsAsErrors + { + get; + set; + } + + /// + /// A list of build submissions that have logged errors. + /// + ISet BuildSubmissionIdsThatHaveLoggedErrors { get; } + #endregion /// /// Entry point for a sink to consume an event. @@ -461,7 +491,7 @@ bool HaveLoggedBuildFinishedEvent void Consume(BuildEventArgs buildEvent); /// - /// Shutsdown the sink and any resources it may be holding + /// Shuts down the sink and any resources it may be holding /// void ShutDown(); } diff --git a/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs b/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs index 55d343b4f2f..0d0c55e08fd 100644 --- a/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs +++ b/src/XMakeBuildEngine/BackEnd/Components/Logging/LoggingService.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Threading; @@ -218,6 +219,11 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler, IBu /// private LoggerMode _logMode = NativeMethodsShared.IsMono ? LoggerMode.Synchronous : LoggerMode.Asynchronous; + /// + /// A list of warnings to treat as errors. + /// + private ISet _warningsAsErrors = null; + #endregion #endregion @@ -444,6 +450,33 @@ public LoggerMode LoggingMode } } + /// + /// Get of warnings to treat as errors. An empty non-null set will treat all warnings as errors. + /// + public ISet WarningsAsErrors + { + get { return _warningsAsErrors; } + set { _warningsAsErrors = value; } + } + + /// + /// Determines if the specified submission has logged an errors. + /// + /// The ID of the build submission. A value of "0" means that an error was logged outside of any build submission. + /// true if the build submission logged an errors, otherwise false. + public bool HasBuildSubmissionLoggedErrors(int submissionId) + { + // Warnings as errors are not tracked if the user did not specify to do so + if (WarningsAsErrors == null) + { + return false; + } + + // Determine if any of the event sinks have logged an error with this submission ID + return (_filterEventSource != null && _filterEventSource.BuildSubmissionIdsThatHaveLoggedErrors.Contains(submissionId)) + || (_eventSinkDictionary != null && _eventSinkDictionary.Values.Any(i => i.BuildSubmissionIdsThatHaveLoggedErrors.Contains(submissionId))); + } + /// /// Return whether or not the LoggingQueue has any events left in it /// @@ -784,7 +817,10 @@ public bool RegisterDistributedLogger(ILogger centralLogger, LoggerDescription f IForwardingLogger localForwardingLogger = null; // create an eventSourceSink which the central logger will register with to receive the events from the forwarding logger - EventSourceSink eventSourceSink = new EventSourceSink(); + EventSourceSink eventSourceSink = new EventSourceSink + { + WarningsAsErrors = WarningsAsErrors == null ? null : new HashSet(WarningsAsErrors, StringComparer.OrdinalIgnoreCase) + }; // If the logger is already in the list it should not be registered again. if (_iloggerList.Contains(centralLogger)) @@ -1096,8 +1132,11 @@ private void CreateFilterEventSource() { if (_filterEventSource == null) { - _filterEventSource = new EventSourceSink(); - _filterEventSource.Name = "Sink for Distributed/Filter loggers"; + _filterEventSource = new EventSourceSink + { + Name = "Sink for Distributed/Filter loggers", + WarningsAsErrors = WarningsAsErrors == null ? null : new HashSet(WarningsAsErrors, StringComparer.OrdinalIgnoreCase) + }; } } diff --git a/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs b/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs index a6fa40adb9f..318b4cd6011 100644 --- a/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs +++ b/src/XMakeBuildEngine/BackEnd/Shared/BuildResult.cs @@ -639,6 +639,15 @@ internal BuildResult Clone() return result; } + /// + /// Sets the overall result. + /// + /// true if the result is success, otherwise false. + internal void SetOverallResult(bool overallResult) + { + _baseOverallResult = false; + } + /// /// Creates the target result dictionary. /// diff --git a/src/XMakeBuildEngine/UnitTests/BackEnd/BuildManager_Tests.cs b/src/XMakeBuildEngine/UnitTests/BackEnd/BuildManager_Tests.cs index d37f27e5d6d..2dc1be36d1d 100644 --- a/src/XMakeBuildEngine/UnitTests/BackEnd/BuildManager_Tests.cs +++ b/src/XMakeBuildEngine/UnitTests/BackEnd/BuildManager_Tests.cs @@ -3594,6 +3594,101 @@ public void Regress265010() } } + /// + /// Verifies that all warnings are treated as errors and that the overall build result is a failure. + /// + [Fact] + public void WarningsAreTreatedAsErrorsAll() + { + string contents = ObjectModelHelpers.CleanupFileContents(@" + + + + + + +"); + _parameters.WarningsAsErrors = new HashSet(); + + Project project = CreateProject(contents, ObjectModelHelpers.MSBuildDefaultToolsVersion, _projectCollection, true); + ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); + _buildManager.BeginBuild(_parameters); + BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1" })); + _buildManager.EndBuild(); + + Assert.Equal(0, _logger.WarningCount); + Assert.Equal(2, _logger.ErrorCount); + + Assert.Equal(BuildResultCode.Failure, result1.OverallResult); + Assert.True(result1.HasResultsForTarget("target1")); + } + + /// + /// Verifies that only the specified warnings are treated as errors and that the overall build result is a failure. + /// + [Fact] + public void WarningsAreTreatedAsErrorsSpecific() + { + string contents = ObjectModelHelpers.CleanupFileContents(@" + + + + + + + +"); + _parameters.WarningsAsErrors = new HashSet { "ABC123" }; + + Project project = CreateProject(contents, ObjectModelHelpers.MSBuildDefaultToolsVersion, _projectCollection, true); + ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); + _buildManager.BeginBuild(_parameters); + BuildResult result1 = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1" })); + _buildManager.EndBuild(); + + Assert.Equal(2, _logger.WarningCount); + Assert.Equal(1, _logger.ErrorCount); + + Assert.Equal(BuildResultCode.Failure, result1.OverallResult); + Assert.True(result1.HasResultsForTarget("target1")); + } + + /// + /// Verifies that when building targets which emit warnings, they still show as succeeding but the overall build result is a failure. + /// + [Fact] + public void WarningsAreTreatedAsErrorsButTargetsStillSucceed() + { + string contents = ObjectModelHelpers.CleanupFileContents(@" + + + + + + + + +"); + _parameters.WarningsAsErrors = new HashSet { "ABC123" }; + + Project project = CreateProject(contents, ObjectModelHelpers.MSBuildDefaultToolsVersion, _projectCollection, true); + ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); + _buildManager.BeginBuild(_parameters); + BuildResult buildResult = _buildManager.BuildRequest(new BuildRequestData(instance, new string[] { "target1", "target2" })); + _buildManager.EndBuild(); + + Assert.Equal(0, _logger.WarningCount); + Assert.Equal(1, _logger.ErrorCount); + + Assert.Equal(BuildResultCode.Failure, buildResult.OverallResult); + Assert.True(buildResult.HasResultsForTarget("target1")); + Assert.True(buildResult.HasResultsForTarget("target2")); + // The two targets should still show as success because they don't know their warning was changed to an error + // Logging a warning as an error does not change execution, only the final result of the build + Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target1"].ResultCode); + Assert.Equal(TargetResultCode.Success, buildResult.ResultsByTarget["target2"].ResultCode); + } + /// /// Helper for cache tests. Builds a project and verifies the right cache files are created. /// diff --git a/src/XMakeBuildEngine/UnitTests/BackEnd/EventSourceSink_Tests.cs b/src/XMakeBuildEngine/UnitTests/BackEnd/EventSourceSink_Tests.cs index 553eecaa7af..58ad2633ffc 100644 --- a/src/XMakeBuildEngine/UnitTests/BackEnd/EventSourceSink_Tests.cs +++ b/src/XMakeBuildEngine/UnitTests/BackEnd/EventSourceSink_Tests.cs @@ -83,6 +83,93 @@ public void ConsumeEventsGoodEventsNoHandlers() eventHelper.RaiseBuildEvent(RaiseEventHelper.GenericStatusEvent); } + /// + /// Verifies that a warning is logged as an error when it's warning code specified. + /// + [Fact] + public void TreatWarningsAsErrorWhenSpecified() + { + BuildWarningEventArgs expectedBuildEvent = RaiseEventHelper.Warning; + + EventSourceSink eventSourceSink = new EventSourceSink() + { + WarningsAsErrors = new HashSet + { + "123", + expectedBuildEvent.Code, + "ABC", + }, + }; + + RaiseEventHelper raiseEventHelper = new RaiseEventHelper(eventSourceSink); + EventHandlerHelper eventHandlerHelper = new EventHandlerHelper(eventSourceSink, null); + + raiseEventHelper.RaiseBuildEvent(RaiseEventHelper.Warning); + + Assert.IsType(eventHandlerHelper.RaisedEvent); + + BuildErrorEventArgs actualBuildEvent = (BuildErrorEventArgs) eventHandlerHelper.RaisedEvent; + + Assert.Equal(expectedBuildEvent.Code, actualBuildEvent.Code); + Assert.Equal(expectedBuildEvent.File, actualBuildEvent.File); + Assert.Equal(expectedBuildEvent.ProjectFile, actualBuildEvent.ProjectFile); + Assert.Equal(expectedBuildEvent.Subcategory, actualBuildEvent.Subcategory); + Assert.Equal(expectedBuildEvent.HelpKeyword, actualBuildEvent.HelpKeyword); + Assert.Equal(expectedBuildEvent.Message, actualBuildEvent.Message); + Assert.Equal(expectedBuildEvent.SenderName, actualBuildEvent.SenderName); + Assert.Equal(expectedBuildEvent.ColumnNumber, actualBuildEvent.ColumnNumber); + Assert.Equal(expectedBuildEvent.EndColumnNumber, actualBuildEvent.EndColumnNumber); + Assert.Equal(expectedBuildEvent.EndLineNumber, actualBuildEvent.EndLineNumber); + Assert.Equal(expectedBuildEvent.LineNumber, actualBuildEvent.LineNumber); + Assert.Equal(expectedBuildEvent.BuildEventContext, actualBuildEvent.BuildEventContext); + Assert.Equal(expectedBuildEvent.ThreadId, actualBuildEvent.ThreadId); + Assert.Equal(expectedBuildEvent.Timestamp, actualBuildEvent.Timestamp); + } + + /// + /// Verifies that a warning is not treated as an error when other warning codes are specified. + /// + [Fact] + public void NotTreatWarningsAsErrorWhenNotSpecified() + { + BuildWarningEventArgs expectedBuildEvent = RaiseEventHelper.Warning; + + EventSourceSink eventSourceSink = new EventSourceSink() + { + WarningsAsErrors = new HashSet + { + "123", + "ABC", + }, + }; + + RaiseEventHelper raiseEventHelper = new RaiseEventHelper(eventSourceSink); + EventHandlerHelper eventHandlerHelper = new EventHandlerHelper(eventSourceSink, null); + + raiseEventHelper.RaiseBuildEvent(RaiseEventHelper.Warning); + + Assert.Equal(expectedBuildEvent, eventHandlerHelper.RaisedEvent); + } + + /// + /// Verifies that a warning is not treated as an error when other warning codes are specified. + /// + [Fact] + public void TreatWarningsAsErrorWhenAllSpecified() + { + EventSourceSink eventSourceSink = new EventSourceSink() + { + WarningsAsErrors = new HashSet(), + }; + + RaiseEventHelper raiseEventHelper = new RaiseEventHelper(eventSourceSink); + EventHandlerHelper eventHandlerHelper = new EventHandlerHelper(eventSourceSink, null); + + raiseEventHelper.RaiseBuildEvent(RaiseEventHelper.Warning); + + Assert.IsType(eventHandlerHelper.RaisedEvent); + } + #region TestsThrowingLoggingExceptions /// diff --git a/src/XMakeBuildEngine/UnitTests/BackEnd/MockLoggingService.cs b/src/XMakeBuildEngine/UnitTests/BackEnd/MockLoggingService.cs index 7ec91aa0fa6..c3559fac92b 100644 --- a/src/XMakeBuildEngine/UnitTests/BackEnd/MockLoggingService.cs +++ b/src/XMakeBuildEngine/UnitTests/BackEnd/MockLoggingService.cs @@ -147,6 +147,16 @@ public string[] PropertiesToSerialize set; } + /// + /// List of warnings to treat as errors. + /// + public ISet WarningsAsErrors + { + get; + set; + } + + /// /// Is the logging service on a remote node, this is used to determine if properties need to be serialized /// @@ -475,6 +485,11 @@ public void LogTelemetry(BuildEventContext buildEventContext, string eventName, { } + public bool HasBuildSubmissionLoggedErrors(int submissionId) + { + return false; + } + #endregion } } diff --git a/src/XMakeCommandLine/CommandLineSwitches.cs b/src/XMakeCommandLine/CommandLineSwitches.cs index 8dba43b5e11..be2786a7d77 100644 --- a/src/XMakeCommandLine/CommandLineSwitches.cs +++ b/src/XMakeCommandLine/CommandLineSwitches.cs @@ -102,6 +102,7 @@ internal enum ParameterizedSwitch ClientToServerPipeHandle, ServerToClientPipeHandle, #endif + WarningsAsErrors, NumberOfParameterizedSwitches } @@ -171,7 +172,8 @@ internal ParameterizedSwitchInfo string duplicateSwitchErrorMessage, bool multipleParametersAllowed, string missingParametersErrorMessage, - bool unquoteParameters + bool unquoteParameters, + bool emptyParametersAllowed ) { this.switchNames = switchNames; @@ -180,6 +182,7 @@ bool unquoteParameters this.missingParametersErrorMessage = missingParametersErrorMessage; this.unquoteParameters = unquoteParameters; this.parameterizedSwitch = parameterizedSwitch; + this.emptyParametersAllowed = emptyParametersAllowed; } // names of the switch (without leading switch indicator) @@ -198,6 +201,8 @@ bool unquoteParameters internal bool unquoteParameters; // the switch id internal ParameterizedSwitch parameterizedSwitch; + // indicates if empty parameters are allowed and if so an empty string will be added to the list of parameter values + internal bool emptyParametersAllowed; } @@ -240,41 +245,42 @@ bool unquoteParameters // WARNING: keep this map in the same order as the ParameterizedSwitch enumeration private static readonly ParameterizedSwitchInfo[] s_parameterizedSwitchesMap = { - //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - // Switch Names Switch Id Duplicate Switch Error Multi Params? Missing Parameters Error Unquote? - //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - new ParameterizedSwitchInfo( new string[] { null }, ParameterizedSwitch.Project, "DuplicateProjectSwitchError", false, null, true ), - new ParameterizedSwitchInfo( new string[] { "target", "t"}, ParameterizedSwitch.Target, null, true, "MissingTargetError", true ), - new ParameterizedSwitchInfo( new string[] { "property", "p" }, ParameterizedSwitch.Property, null, true, "MissingPropertyError", true ), - new ParameterizedSwitchInfo( new string[] { "logger", "l" }, ParameterizedSwitch.Logger, null, false, "MissingLoggerError", false ), - new ParameterizedSwitchInfo( new string[] { "distributedlogger", "dl" }, ParameterizedSwitch.DistributedLogger, null, false, "MissingLoggerError", false ), - new ParameterizedSwitchInfo( new string[] { "verbosity", "v" }, ParameterizedSwitch.Verbosity, null, false, "MissingVerbosityError", true ), + //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + // Switch Names Switch Id Duplicate Switch Error Multi Params? Missing Parameters Error Unquote? Empty? + //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + new ParameterizedSwitchInfo( new string[] { null }, ParameterizedSwitch.Project, "DuplicateProjectSwitchError", false, null, true, false ), + new ParameterizedSwitchInfo( new string[] { "target", "t"}, ParameterizedSwitch.Target, null, true, "MissingTargetError", true, false ), + new ParameterizedSwitchInfo( new string[] { "property", "p" }, ParameterizedSwitch.Property, null, true, "MissingPropertyError", true, false ), + new ParameterizedSwitchInfo( new string[] { "logger", "l" }, ParameterizedSwitch.Logger, null, false, "MissingLoggerError", false, false ), + new ParameterizedSwitchInfo( new string[] { "distributedlogger", "dl" }, ParameterizedSwitch.DistributedLogger, null, false, "MissingLoggerError", false, false ), + new ParameterizedSwitchInfo( new string[] { "verbosity", "v" }, ParameterizedSwitch.Verbosity, null, false, "MissingVerbosityError", true, false ), #if FEATURE_XML_SCHEMA_VALIDATION - new ParameterizedSwitchInfo( new string[] { "validate", "val" }, ParameterizedSwitch.Validate, null, false, null, true ), + new ParameterizedSwitchInfo( new string[] { "validate", "val" }, ParameterizedSwitch.Validate, null, false, null, true, false ), #endif - new ParameterizedSwitchInfo( new string[] { "consoleloggerparameters", "clp" }, ParameterizedSwitch.ConsoleLoggerParameters, null, false, "MissingConsoleLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "nodemode", "nmode" }, ParameterizedSwitch.NodeMode, null, false, null, false ), - new ParameterizedSwitchInfo( new string[] { "maxcpucount", "m" }, ParameterizedSwitch.MaxCPUCount, null, false, "MissingMaxCPUCountError", true ), - new ParameterizedSwitchInfo( new string[] { "ignoreprojectextensions", "ignore" }, ParameterizedSwitch.IgnoreProjectExtensions, null, true, "MissingIgnoreProjectExtensionsError", true ), - new ParameterizedSwitchInfo( new string[] { "toolsversion","tv" }, ParameterizedSwitch.ToolsVersion, null, false, "MissingToolsVersionError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters", "flp" }, ParameterizedSwitch.FileLoggerParameters, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters1", "flp1" }, ParameterizedSwitch.FileLoggerParameters1, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters2", "flp2" }, ParameterizedSwitch.FileLoggerParameters2, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters3", "flp3" }, ParameterizedSwitch.FileLoggerParameters3, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters4", "flp4" }, ParameterizedSwitch.FileLoggerParameters4, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters5", "flp5" }, ParameterizedSwitch.FileLoggerParameters5, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters6", "flp6" }, ParameterizedSwitch.FileLoggerParameters6, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters7", "flp7" }, ParameterizedSwitch.FileLoggerParameters7, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters8", "flp8" }, ParameterizedSwitch.FileLoggerParameters8, null, false, "MissingFileLoggerParameterError", true ), - new ParameterizedSwitchInfo( new string[] { "fileloggerparameters9", "flp9" }, ParameterizedSwitch.FileLoggerParameters9, null, false, "MissingFileLoggerParameterError", true ), + new ParameterizedSwitchInfo( new string[] { "consoleloggerparameters", "clp" }, ParameterizedSwitch.ConsoleLoggerParameters, null, false, "MissingConsoleLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "nodemode", "nmode" }, ParameterizedSwitch.NodeMode, null, false, null, false, false ), + new ParameterizedSwitchInfo( new string[] { "maxcpucount", "m" }, ParameterizedSwitch.MaxCPUCount, null, false, "MissingMaxCPUCountError", true, false ), + new ParameterizedSwitchInfo( new string[] { "ignoreprojectextensions", "ignore" }, ParameterizedSwitch.IgnoreProjectExtensions, null, true, "MissingIgnoreProjectExtensionsError", true, false ), + new ParameterizedSwitchInfo( new string[] { "toolsversion","tv" }, ParameterizedSwitch.ToolsVersion, null, false, "MissingToolsVersionError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters", "flp" }, ParameterizedSwitch.FileLoggerParameters, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters1", "flp1" }, ParameterizedSwitch.FileLoggerParameters1, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters2", "flp2" }, ParameterizedSwitch.FileLoggerParameters2, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters3", "flp3" }, ParameterizedSwitch.FileLoggerParameters3, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters4", "flp4" }, ParameterizedSwitch.FileLoggerParameters4, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters5", "flp5" }, ParameterizedSwitch.FileLoggerParameters5, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters6", "flp6" }, ParameterizedSwitch.FileLoggerParameters6, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters7", "flp7" }, ParameterizedSwitch.FileLoggerParameters7, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters8", "flp8" }, ParameterizedSwitch.FileLoggerParameters8, null, false, "MissingFileLoggerParameterError", true, false ), + new ParameterizedSwitchInfo( new string[] { "fileloggerparameters9", "flp9" }, ParameterizedSwitch.FileLoggerParameters9, null, false, "MissingFileLoggerParameterError", true, false ), #if FEATURE_NODE_REUSE - new ParameterizedSwitchInfo( new string[] { "nodereuse", "nr" }, ParameterizedSwitch.NodeReuse, null, false, "MissingNodeReuseParameterError", true ), + new ParameterizedSwitchInfo( new string[] { "nodereuse", "nr" }, ParameterizedSwitch.NodeReuse, null, false, "MissingNodeReuseParameterError", true, false ), #endif - new ParameterizedSwitchInfo( new string[] { "preprocess", "pp" }, ParameterizedSwitch.Preprocess, null, false, null, true ), + new ParameterizedSwitchInfo( new string[] { "preprocess", "pp" }, ParameterizedSwitch.Preprocess, null, false, null, true, false ), #if !FEATURE_NAMED_PIPES_FULL_DUPLEX - new ParameterizedSwitchInfo( new string[] { "clientToServerPipeHandle", "c2s" }, ParameterizedSwitch.ClientToServerPipeHandle, null, false, null, true ), - new ParameterizedSwitchInfo( new string[] { "serverToClientPipeHandle", "s2c" }, ParameterizedSwitch.ServerToClientPipeHandle, null, false, null, true ) + new ParameterizedSwitchInfo( new string[] { "clientToServerPipeHandle", "c2s" }, ParameterizedSwitch.ClientToServerPipeHandle, null, false, null, true, false ), + new ParameterizedSwitchInfo( new string[] { "serverToClientPipeHandle", "s2c" }, ParameterizedSwitch.ServerToClientPipeHandle, null, false, null, true, false ), #endif + new ParameterizedSwitchInfo( new string[] { "warnaserror", "err" }, ParameterizedSwitch.WarningsAsErrors, null, true, null, true, true ), }; /// @@ -343,7 +349,8 @@ internal static bool IsParameterizedSwitch out string duplicateSwitchErrorMessage, out bool multipleParametersAllowed, out string missingParametersErrorMessage, - out bool unquoteParameters + out bool unquoteParameters, + out bool emptyParametersAllowed ) { parameterizedSwitch = ParameterizedSwitch.Invalid; @@ -351,6 +358,7 @@ out bool unquoteParameters multipleParametersAllowed = false; missingParametersErrorMessage = null; unquoteParameters = false; + emptyParametersAllowed = false; foreach (ParameterizedSwitchInfo switchInfo in s_parameterizedSwitchesMap) { @@ -363,6 +371,7 @@ out bool unquoteParameters multipleParametersAllowed = switchInfo.multipleParametersAllowed; missingParametersErrorMessage = switchInfo.missingParametersErrorMessage; unquoteParameters = switchInfo.unquoteParameters; + emptyParametersAllowed = switchInfo.emptyParametersAllowed; break; } } @@ -454,7 +463,8 @@ internal bool SetParameterizedSwitch string commandLineArg, string switchParameters, bool multipleParametersAllowed, - bool unquoteParameters + bool unquoteParameters, + bool emptyParametersAllowed ) { bool parametersStored = false; @@ -481,12 +491,22 @@ bool unquoteParameters // check if the switch has multiple parameters if (multipleParametersAllowed) { - // store all the switch parameters - int emptyParameters; - _parameterizedSwitches[(int)parameterizedSwitch].parameters.AddRange(QuotingUtilities.SplitUnquoted(switchParameters, int.MaxValue, false /* discard empty parameters */, unquoteParameters, out emptyParameters, s_parameterSeparators)); + if (String.Empty.Equals(switchParameters) && emptyParametersAllowed) + { + // Store a null parameter if its allowed + _parameterizedSwitches[(int) parameterizedSwitch].parameters.Add(null); + parametersStored = true; + } + else + { + // store all the switch parameters + int emptyParameters; + _parameterizedSwitches[(int)parameterizedSwitch].parameters.AddRange(QuotingUtilities.SplitUnquoted(switchParameters, int.MaxValue, false /* discard empty parameters */, unquoteParameters, out emptyParameters, s_parameterSeparators)); - // check if they were all stored successfully i.e. they were all non-empty (after removing quoting, if requested) - parametersStored = (emptyParameters == 0); + // check if they were all stored successfully i.e. they were all non-empty (after removing quoting, if requested) + parametersStored = (emptyParameters == 0); + } + } else { diff --git a/src/XMakeCommandLine/Resources/Strings.resx b/src/XMakeCommandLine/Resources/Strings.resx index a4740198870..18bbfd134f7 100644 --- a/src/XMakeCommandLine/Resources/Strings.resx +++ b/src/XMakeCommandLine/Resources/Strings.resx @@ -586,6 +586,25 @@ Copyright (C) Microsoft Corporation. All rights reserved. MSBuild XML and any tasks and loggers it uses. + + /warnaserror[:code[;code2]] + List of warning codes to treats as errors. Use a semicolon + or a comma to separate multiple warning codes. To treat all + warnings as errors use the switch with no values. + (Short form: /err[:c;[c2]]) + + Example: + /warnaserror:MSB4130 + + When a warning is treated as an error the target will + continue to execute as if it was a warning but the overall + build will fail. + + + LOCALIZATION: "warnaserror" should not be localized. + LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. + + MSBUILD : Configuration error MSB1043: The application could not start. {0} diff --git a/src/XMakeCommandLine/UnitTests/CommandLineSwitches_Tests.cs b/src/XMakeCommandLine/UnitTests/CommandLineSwitches_Tests.cs index a138a7d9531..60bc3af5ee2 100644 --- a/src/XMakeCommandLine/UnitTests/CommandLineSwitches_Tests.cs +++ b/src/XMakeCommandLine/UnitTests/CommandLineSwitches_Tests.cs @@ -4,8 +4,10 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.IO; - +using System.Linq; +using System.Resources; using Microsoft.Build.CommandLine; using Microsoft.Build.Construction; using Microsoft.Build.Framework; @@ -36,8 +38,9 @@ public void BogusSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.False(CommandLineSwitches.IsParameterizedSwitch("bogus", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.False(CommandLineSwitches.IsParameterizedSwitch("bogus", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Invalid, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -250,29 +253,30 @@ public void FileLoggerParametersIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("flp", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("flp", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.FileLoggerParameters, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("FLP", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("FLP", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.FileLoggerParameters, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("fileLoggerParameters", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("fileLoggerParameters", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.FileLoggerParameters, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("FILELOGGERPARAMETERS", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("FILELOGGERPARAMETERS", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.FileLoggerParameters, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -289,29 +293,30 @@ public void NodeReuseParametersIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("nr", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("nr", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.NodeReuse, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("NR", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("NR", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.NodeReuse, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("nodereuse", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("nodereuse", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.NodeReuse, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("NodeReuse", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("NodeReuse", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.NodeReuse, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -328,8 +333,9 @@ public void ProjectSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch(null, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch(null, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Project, parameterizedSwitch); Assert.NotNull(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -337,7 +343,7 @@ public void ProjectSwitchIdentificationTests() Assert.True(unquoteParameters); // for the virtual project switch, we match on null, not empty string - Assert.False(CommandLineSwitches.IsParameterizedSwitch(String.Empty, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.False(CommandLineSwitches.IsParameterizedSwitch(String.Empty, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Invalid, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -353,36 +359,37 @@ public void IgnoreProjectExtensionsSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("ignoreprojectextensions", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("ignoreprojectextensions", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.IgnoreProjectExtensions, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("IgnoreProjectExtensions", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("IgnoreProjectExtensions", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.IgnoreProjectExtensions, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("IGNOREPROJECTEXTENSIONS", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("IGNOREPROJECTEXTENSIONS", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.IgnoreProjectExtensions, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("ignore", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("ignore", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.IgnoreProjectExtensions, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("IGNORE", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("IGNORE", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.IgnoreProjectExtensions, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); @@ -398,36 +405,37 @@ public void TargetSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("target", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("target", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Target, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("TARGET", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("TARGET", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Target, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Target", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Target", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Target, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("t", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("t", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Target, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("T", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("T", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Target, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); @@ -443,36 +451,37 @@ public void PropertySwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("property", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("property", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Property, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("PROPERTY", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("PROPERTY", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Property, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Property", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Property", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Property, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("p", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("p", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Property, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("P", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("P", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Property, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.True(multipleParametersAllowed); @@ -488,36 +497,37 @@ public void LoggerSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("logger", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("logger", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Logger, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.False(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("LOGGER", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("LOGGER", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Logger, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.False(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Logger", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Logger", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Logger, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.False(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("l", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("l", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Logger, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.False(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("L", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("L", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Logger, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -533,36 +543,37 @@ public void VerbositySwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("verbosity", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("verbosity", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Verbosity, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("VERBOSITY", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("VERBOSITY", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Verbosity, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Verbosity", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Verbosity", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Verbosity, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("v", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("v", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Verbosity, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("V", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("V", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Verbosity, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -608,29 +619,30 @@ public void MaxCPUCountSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("m", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("m", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.MaxCPUCount, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("M", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("M", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.MaxCPUCount, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("maxcpucount", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("maxcpucount", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.MaxCPUCount, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.NotNull(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("MAXCPUCOUNT", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("MAXCPUCOUNT", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.MaxCPUCount, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -647,43 +659,44 @@ public void ValidateSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("validate", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("validate", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("VALIDATE", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("VALIDATE", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Validate", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Validate", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("val", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("val", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("VAL", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("VAL", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("Val", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("Val", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Validate, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -700,15 +713,16 @@ public void PreprocessSwitchIdentificationTests() bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool emptyParametersAllowed; - Assert.True(CommandLineSwitches.IsParameterizedSwitch("preprocess", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("preprocess", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Preprocess, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); Assert.Null(missingParametersErrorMessage); Assert.True(unquoteParameters); - Assert.True(CommandLineSwitches.IsParameterizedSwitch("pp", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)); + Assert.True(CommandLineSwitches.IsParameterizedSwitch("pp", out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out emptyParametersAllowed)); Assert.Equal(CommandLineSwitches.ParameterizedSwitch.Preprocess, parameterizedSwitch); Assert.Null(duplicateSwitchErrorMessage); Assert.False(multipleParametersAllowed); @@ -745,7 +759,7 @@ public void SetParameterizedSwitchTests1() { CommandLineSwitches switches = new CommandLineSwitches(); - Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/v:q", "q", false, true)); + Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/v:q", "q", false, true, false)); Assert.Equal("/v:q", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Verbosity)); Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Verbosity)); @@ -758,7 +772,7 @@ public void SetParameterizedSwitchTests1() // set it again -- this is bogus, because the /verbosity switch doesn't allow multiple parameters, but for the // purposes of testing the SetParameterizedSwitch() method, it doesn't matter - Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/verbosity:\"diag\";minimal", "\"diag\";minimal", true, true)); + Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/verbosity:\"diag\";minimal", "\"diag\";minimal", true, true, false)); Assert.Equal("/v:q /verbosity:\"diag\";minimal", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Verbosity)); Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Verbosity)); @@ -788,7 +802,7 @@ public void SetParameterizedSwitchTests2() // fake/missing parameters -- this is bogus because the /target switch allows multiple parameters but we're turning // that off here just for testing purposes - Assert.False(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:\"", "\"", false, true)); + Assert.False(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:\"", "\"", false, true, false)); // switch has been set Assert.Equal("/t:\"", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Target)); @@ -801,7 +815,7 @@ public void SetParameterizedSwitchTests2() Assert.Equal(0, parameters.Length); // more fake/missing parameters - Assert.False(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:A,\"\";B", "A,\"\";B", true, true)); + Assert.False(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:A,\"\";B", "A,\"\";B", true, true, false)); Assert.Equal("/t:\" /t:A,\"\";B", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Target)); Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Target)); @@ -830,7 +844,7 @@ public void SetParameterizedSwitchTests3() Assert.Equal(0, parameters.Length); // don't unquote fake/missing parameters - Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Logger, "/l:\"", "\"", false, false)); + Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Logger, "/l:\"", "\"", false, false, false)); Assert.Equal("/l:\"", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Logger)); Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Logger)); @@ -843,7 +857,7 @@ public void SetParameterizedSwitchTests3() // don't unquote multiple fake/missing parameters -- this is bogus because the /logger switch does not take multiple // parameters, but for testing purposes this is fine - Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Logger, "/LOGGER:\"\",asm;\"p,a;r\"", "\"\",asm;\"p,a;r\"", true, false)); + Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Logger, "/LOGGER:\"\",asm;\"p,a;r\"", "\"\",asm;\"p,a;r\"", true, false, false)); Assert.Equal("/l:\" /LOGGER:\"\",asm;\"p,a;r\"", switches.GetParameterizedSwitchCommandLineArg(CommandLineSwitches.ParameterizedSwitch.Logger)); Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Logger)); @@ -858,6 +872,24 @@ public void SetParameterizedSwitchTests3() Assert.Equal("\"p,a;r\"", parameters[3]); } + [Fact] + public void SetParameterizedSwitchTestsAllowEmpty() + { + CommandLineSwitches switches = new CommandLineSwitches(); + + Assert.True(switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.WarningsAsErrors, "/warnaserror", "", multipleParametersAllowed: true, unquoteParameters: false, emptyParametersAllowed: true)); + + Assert.True(switches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.WarningsAsErrors)); + + string[] parameters = switches[CommandLineSwitches.ParameterizedSwitch.WarningsAsErrors]; + + Assert.NotNull(parameters); + + Assert.True(parameters.Length > 0); + + Assert.Null(parameters.Last()); + } + [Fact] public void AppendErrorTests1() { @@ -970,14 +1002,14 @@ public void AppendParameterizedSwitchesTests1() { CommandLineSwitches switchesLeft = new CommandLineSwitches(); - switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "tempproject.proj", "tempproject.proj", false, true); + switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "tempproject.proj", "tempproject.proj", false, true, false); Assert.True(switchesLeft.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Project)); Assert.False(switchesLeft.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Target)); CommandLineSwitches switchesRight = new CommandLineSwitches(); - switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:build", "build", true, true); + switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:build", "build", true, true, false); Assert.False(switchesRight.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Project)); Assert.True(switchesRight.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Target)); @@ -1008,13 +1040,13 @@ public void AppendParameterizedSwitchesTests2() { CommandLineSwitches switchesLeft = new CommandLineSwitches(); - switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/target:Clean", "Clean", true, true); + switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/target:Clean", "Clean", true, true, false); Assert.True(switchesLeft.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Target)); CommandLineSwitches switchesRight = new CommandLineSwitches(); - switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:\"RESOURCES\";build", "\"RESOURCES\";build", true, true); + switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Target, "/t:\"RESOURCES\";build", "\"RESOURCES\";build", true, true, false); Assert.True(switchesRight.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Target)); @@ -1037,13 +1069,13 @@ public void AppendParameterizedSwitchesTests3() { CommandLineSwitches switchesLeft = new CommandLineSwitches(); - switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "tempproject.proj", "tempproject.proj", false, true); + switchesLeft.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "tempproject.proj", "tempproject.proj", false, true, false); Assert.True(switchesLeft.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Project)); CommandLineSwitches switchesRight = new CommandLineSwitches(); - switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "Rhubarb.proj", "Rhubarb.proj", false, true); + switchesRight.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Project, "Rhubarb.proj", "Rhubarb.proj", false, true, false); Assert.True(switchesRight.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.Project)); @@ -1090,7 +1122,8 @@ public void InvalidToolsVersionErrors() true, new StringWriter(), false, - false); + false, + warningsAsErrors: null); } finally { @@ -1105,7 +1138,7 @@ public void TestHaveAnySwitchesBeenSet() // Check if method works with parameterized switch CommandLineSwitches switches = new CommandLineSwitches(); Assert.False(switches.HaveAnySwitchesBeenSet()); - switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/v:q", "q", false, true); + switches.SetParameterizedSwitch(CommandLineSwitches.ParameterizedSwitch.Verbosity, "/v:q", "q", false, true, false); Assert.True(switches.HaveAnySwitchesBeenSet()); // Check if method works with parameterless switches @@ -1152,6 +1185,173 @@ public void ExtractAnyLoggerParameterPickLast() Assert.Equal("v=q", result); } + /// + /// Verifies that when the /warnaserror switch is not specified, the set of warnings is null. + /// + [Fact] + public void ProcessWarnAsErrorSwitchNotSpecified() + { + CommandLineSwitches commandLineSwitches = new CommandLineSwitches(); + + MSBuildApp.GatherCommandLineSwitches(new ArrayList(new[] { "" }), commandLineSwitches); + + Assert.Null(MSBuildApp.ProcessWarnAsErrorSwitch(commandLineSwitches)); + } + + /// + /// Verifies that the /warnaserror switch is parsed properly when codes are specified. + /// + [Fact] + public void ProcessWarnAsErrorSwitchWithCodes() + { + ISet expectedWarningsAsErors = new HashSet(StringComparer.OrdinalIgnoreCase) { "a", "B", "c", "D", "e" }; + + CommandLineSwitches commandLineSwitches = new CommandLineSwitches(); + + MSBuildApp.GatherCommandLineSwitches(new ArrayList(new[] + { + "\"/warnaserror: a,B ; c \"", // Leading, trailing, leading and trailing whitespace + "/warnaserror:A,b,C", // Repeats of different case + "\"/warnaserror:, ,,\"", // Empty items + "/err:D,d;E,e", // A different source with new items and uses the short form + "/warnaserror:a", // A different source with a single duplicate + "/warnaserror:a,b", // A different source with multiple duplicates + }), commandLineSwitches); + + ISet actualWarningsAsErrors = MSBuildApp.ProcessWarnAsErrorSwitch(commandLineSwitches); + + Assert.NotNull(actualWarningsAsErrors); + + Assert.Equal(expectedWarningsAsErors, actualWarningsAsErrors, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Verifies that an empty /warnaserror switch clears the list of codes. + /// + [Fact] + public void ProcessWarnAsErrorSwitchEmptySwitchClearsSet() + { + CommandLineSwitches commandLineSwitches = new CommandLineSwitches(); + + MSBuildApp.GatherCommandLineSwitches(new ArrayList(new[] + { + "/warnaserror:a;b;c", + "/warnaserror", + }), commandLineSwitches); + + ISet actualWarningsAsErrors = MSBuildApp.ProcessWarnAsErrorSwitch(commandLineSwitches); + + Assert.NotNull(actualWarningsAsErrors); + + Assert.Equal(0, actualWarningsAsErrors.Count); + } + + /// + /// Verifies that when values are specified after an empty /warnaserror switch that they are added to the cleared list. + /// + [Fact] + public void ProcessWarnAsErrorSwitchValuesAfterEmptyAddOn() + { + ISet expectedWarningsAsErors = new HashSet(StringComparer.OrdinalIgnoreCase) { "e", "f", "g" }; + + CommandLineSwitches commandLineSwitches = new CommandLineSwitches(); + + MSBuildApp.GatherCommandLineSwitches(new ArrayList(new[] + { + "/warnaserror:a;b;c", + "/warnaserror", + "/warnaserror:e;f;g", + }), commandLineSwitches); + + ISet actualWarningsAsErrors = MSBuildApp.ProcessWarnAsErrorSwitch(commandLineSwitches); + + Assert.NotNull(actualWarningsAsErrors); + + Assert.Equal(expectedWarningsAsErors, actualWarningsAsErrors, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Verifies that the /warnaserror switch is parsed properly when no codes are specified. + /// + [Fact] + public void ProcessWarnAsErrorSwitchEmpty() + { + CommandLineSwitches commandLineSwitches = new CommandLineSwitches(); + + MSBuildApp.GatherCommandLineSwitches(new ArrayList(new [] { "/warnaserror" }), commandLineSwitches); + + ISet actualWarningsAsErrors = MSBuildApp.ProcessWarnAsErrorSwitch(commandLineSwitches); + + Assert.NotNull(actualWarningsAsErrors); + + Assert.Equal(0, actualWarningsAsErrors.Count); + } + +#if FEATURE_RESOURCEMANAGER_GETRESOURCESET + /// + /// Verifies that help messages are correctly formed with the right width and leading spaces. + /// + [Fact] + public void HelpMessagesAreValid() + { + ResourceManager resourceManager = new ResourceManager("MSBuild.Strings", typeof(AssemblyResources).Assembly); + + const string switchLeadingSpaces = " "; + const string otherLineLeadingSpaces = " "; + const string examplesLeadingSpaces = " "; + + foreach (KeyValuePair item in resourceManager.GetResourceSet(CultureInfo.CurrentUICulture, createIfNotExists: true, tryParents: true) + .Cast().Where(i => i.Key is string && ((string)i.Key).StartsWith("HelpMessage_")) + .Select(i => new KeyValuePair((string)i.Key, (string)i.Value))) + { + string[] helpMessageLines = item.Value.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + + for (int i = 0; i < helpMessageLines.Length; i++) + { + // All lines should be 80 characters or less + Assert.True(helpMessageLines[i].Length <= 80, $"Line {i + 1} of '{item.Key}' should be no longer than 80 characters."); + + if (i == 0) + { + if (helpMessageLines[i].Trim().StartsWith("/") || helpMessageLines[i].Trim().StartsWith("@")) + { + // If the first line in a switch it needs a certain amount of leading spaces + Assert.True(helpMessageLines[i].StartsWith(switchLeadingSpaces), $"Line {i + 1} of '{item.Key}' should start with '{switchLeadingSpaces}'."); + } + else + { + // Otherwise it should have no leading spaces because it's a section + Assert.False(helpMessageLines[i].StartsWith(" "), $"Line {i + 1} of '{item.Key}' should not have any leading spaces."); + } + } + else + { + // Ignore empty lines + if (!String.IsNullOrWhiteSpace(helpMessageLines[i])) + { + + if (item.Key.Contains("Examples")) + { + // Examples require a certain number of leading spaces + Assert.True(helpMessageLines[i].StartsWith(examplesLeadingSpaces), $"Line {i + 1} of '{item.Key}' should start with '{examplesLeadingSpaces}'."); + } + else if (helpMessageLines[i].Trim().StartsWith("/") || helpMessageLines[i].Trim().StartsWith("@")) + { + // Switches require a certain number of leading spaces + Assert.True(helpMessageLines[i].StartsWith(switchLeadingSpaces), $"Line {i + 1} of '{item.Key}' should start with '{switchLeadingSpaces}'."); + } + else + { + // All other lines require a certain number of leading spaces + Assert.True(helpMessageLines[i].StartsWith(otherLineLeadingSpaces), $"Line {i + 1} of '{item.Key}' should start with '{otherLineLeadingSpaces}'."); + } + } + } + } + } + } +#endif + /// /// Verifies that a switch collection has an error registered for the given command line arg. /// diff --git a/src/XMakeCommandLine/XMake.cs b/src/XMakeCommandLine/XMake.cs index c9b1d04bb37..69d26aa88ff 100644 --- a/src/XMakeCommandLine/XMake.cs +++ b/src/XMakeCommandLine/XMake.cs @@ -11,6 +11,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Security; using System.Text; @@ -546,6 +547,7 @@ string [] commandLine TextWriter preprocessWriter = null; bool debugger = false; bool detailedSummary = false; + ISet warningsAsErrors = null; CommandLineSwitches switchesFromAutoResponseFile; CommandLineSwitches switchesNotFromAutoResponseFile; @@ -572,6 +574,7 @@ string [] commandLine ref preprocessWriter, ref debugger, ref detailedSummary, + ref warningsAsErrors, recursing: false )) { @@ -600,7 +603,7 @@ string [] commandLine #if FEATURE_XML_SCHEMA_VALIDATION needToValidateProject, schemaFile, #endif - cpuCount, enableNodeReuse, preprocessWriter, debugger, detailedSummary)) + cpuCount, enableNodeReuse, preprocessWriter, debugger, detailedSummary, warningsAsErrors)) { exitType = ExitType.BuildError; } @@ -891,7 +894,8 @@ internal static bool BuildProject bool enableNodeReuse, TextWriter preprocessWriter, bool debugger, - bool detailedSummary + bool detailedSummary, + ISet warningsAsErrors ) { if (String.Equals(Path.GetExtension(projectFile), ".vcproj", StringComparison.OrdinalIgnoreCase) || @@ -1055,6 +1059,7 @@ bool detailedSummary parameters.ToolsetDefinitionLocations = Microsoft.Build.Evaluation.ToolsetDefinitionLocations.ConfigurationFile | Microsoft.Build.Evaluation.ToolsetDefinitionLocations.Registry; parameters.DetailedSummary = detailedSummary; parameters.LogTaskInputs = logTaskInputs; + parameters.WarningsAsErrors = warningsAsErrors; if (!String.IsNullOrEmpty(toolsVersion)) { @@ -1424,6 +1429,7 @@ internal static void GatherCommandLineSwitches(ArrayList commandLineArgs, Comman bool multipleParametersAllowed; string missingParametersErrorMessage; bool unquoteParameters; + bool allowEmptyParameters; // Special case: for the switch "/m" or "/maxCpuCount" we wish to pretend we saw "/m:" // This allows a subsequent /m:n on the command line to override it. @@ -1444,9 +1450,9 @@ internal static void GatherCommandLineSwitches(ArrayList commandLineArgs, Comman { GatherParameterlessCommandLineSwitch(commandLineSwitches, parameterlessSwitch, switchParameters, duplicateSwitchErrorMessage, unquotedCommandLineArg); } - else if (CommandLineSwitches.IsParameterizedSwitch(switchName, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters)) + else if (CommandLineSwitches.IsParameterizedSwitch(switchName, out parameterizedSwitch, out duplicateSwitchErrorMessage, out multipleParametersAllowed, out missingParametersErrorMessage, out unquoteParameters, out allowEmptyParameters)) { - GatherParameterizedCommandLineSwitch(commandLineSwitches, parameterizedSwitch, switchParameters, duplicateSwitchErrorMessage, multipleParametersAllowed, missingParametersErrorMessage, unquoteParameters, unquotedCommandLineArg); + GatherParameterizedCommandLineSwitch(commandLineSwitches, parameterizedSwitch, switchParameters, duplicateSwitchErrorMessage, multipleParametersAllowed, missingParametersErrorMessage, unquoteParameters, unquotedCommandLineArg, allowEmptyParameters); } else { @@ -1677,7 +1683,8 @@ private static void GatherParameterizedCommandLineSwitch bool multipleParametersAllowed, string missingParametersErrorMessage, bool unquoteParameters, - string unquotedCommandLineArg + string unquotedCommandLineArg, + bool allowEmptyParameters ) { if (// switch must have parameters @@ -1696,7 +1703,7 @@ string unquotedCommandLineArg } // save the parameters after unquoting and splitting them if necessary - if (!commandLineSwitches.SetParameterizedSwitch(parameterizedSwitch, unquotedCommandLineArg, switchParameters, multipleParametersAllowed, unquoteParameters)) + if (!commandLineSwitches.SetParameterizedSwitch(parameterizedSwitch, unquotedCommandLineArg, switchParameters, multipleParametersAllowed, unquoteParameters, allowEmptyParameters)) { // if parsing revealed there were no real parameters, flag an error, unless the parameters are optional if (missingParametersErrorMessage != null) @@ -1786,6 +1793,7 @@ private static bool ProcessCommandLineSwitches ref TextWriter preprocessWriter, ref bool debugger, ref bool detailedSummary, + ref ISet warningsAsErrors, bool recursing ) { @@ -1889,6 +1897,7 @@ bool recursing ref preprocessWriter, ref debugger, ref detailedSummary, + ref warningsAsErrors, recursing: true ); } @@ -1923,6 +1932,8 @@ bool recursing #endif detailedSummary = commandLineSwitches.IsParameterlessSwitchSet(CommandLineSwitches.ParameterlessSwitch.DetailedSummary); + warningsAsErrors = ProcessWarnAsErrorSwitch(commandLineSwitches); + // figure out which loggers are going to listen to build events string[][] groupedFileLoggerParameters = commandLineSwitches.GetFileLoggerParameters(); @@ -2030,6 +2041,37 @@ internal static TextWriter ProcessPreprocessSwitch(string[] parameters) return writer; } + internal static ISet ProcessWarnAsErrorSwitch(CommandLineSwitches commandLineSwitches) + { + // TODO: Parse an environment variable as well? + + if (!commandLineSwitches.IsParameterizedSwitchSet(CommandLineSwitches.ParameterizedSwitch.WarningsAsErrors)) + { + return null; + } + + string[] parameters = commandLineSwitches[CommandLineSwitches.ParameterizedSwitch.WarningsAsErrors]; + + ISet warningsAsErrors = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (string code in parameters + .SelectMany(parameter => parameter?.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[] { null })) + { + if (code == null) + { + // An empty /warnaserror is added as "null". In this case, the list is cleared + // so that all warnings are treated errors + warningsAsErrors.Clear(); + } + else if(!String.IsNullOrWhiteSpace(code)) + { + warningsAsErrors.Add(code.Trim()); + } + } + + return warningsAsErrors; + } + /// /// Uses the input from thinNodeMode switch to start a local node server /// @@ -3137,6 +3179,7 @@ private static void ShowHelpMessage() Console.WriteLine(AssemblyResources.GetString("HelpMessage_18_DistributedLoggerSwitch")); Console.WriteLine(AssemblyResources.GetString("HelpMessage_21_DistributedFileLoggerSwitch")); Console.WriteLine(AssemblyResources.GetString("HelpMessage_11_LoggerSwitch")); + Console.WriteLine(AssemblyResources.GetString("HelpMessage_28_WarnAsErrorSwitch")); #if FEATURE_XML_SCHEMA_VALIDATION Console.WriteLine(AssemblyResources.GetString("HelpMessage_15_ValidateSwitch")); #endif