-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement /WarnAsError to treat specified warnings as errors #1355
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Biggest concern is the additive-vs-override nature of multiple arguments.
ILoggingService loggingService = ((IBuildComponentHost)this).LoggingService; | ||
|
||
// Check if the user had specified /warnaserror and then override the result if there were any errors. | ||
// This ensures the old behavior stays in tact where builds could succeed even if a failure was logged. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: no space in intact
@@ -621,6 +631,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.WarningsAsErrors != null && loggingService.HasBuildSubmissionLoggedErrors(-1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a named constant somewhere for the magic -1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a quick search and didn't find anything. I saw this line and figured it was okay to use -1. Should I declare a constant like NullSubmissionId
?
if (loggingService.WarningsAsErrors != null && loggingService.HasBuildSubmissionLoggedErrors(submission.SubmissionId)) | ||
{ | ||
result.SetOverallResult(overallResult: false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a good way to abstract this logic? It's pretty repetitive in here. Maybe EnsureFailureIfWarningsAsErrors(BuildResult, ILoggingService)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, I can make a method that does this for two of the places.
@@ -320,6 +325,7 @@ private BuildParameters(BuildParameters other) | |||
_disableInProcNode = other._disableInProcNode; | |||
_logTaskInputs = other._logTaskInputs; | |||
_logInitialPropertiesAndItems = other._logInitialPropertiesAndItems; | |||
_warningsAsErrors = other._warningsAsErrors == null ? null : new HashSet<string>(other._warningsAsErrors, StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably not worth changing this, but I would hazard a guess that for this use case (small sets of inputs), a SortedSet
might actually be faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some limited research on what was faster and from what I could tell there was no difference. Do you know of any guidance on which is faster? I wanted the fastest possible lookup for sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK it's highly dependent on the individual use case, so we probably can't tell for this case without extensive benchmarking.
My understanding is based on Pike's third rule: Fancy algorithms are slow when n is small, and n is usually small. In this case, a hashtable is awesomely super fast, linear lookup . . . but requires hashing and indirection/pointer-following. A sorted set that is small, as I expect this thing to be, could easily use fewer instructions per lookup.
But we'd have to measure to know (Rule 2!) and I don't think this will be worth worrying about, so I'd say to leave it as-is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did some benchmarks. If you have 10 warnings to set as errors and your build logs 10,000 warnings, the fastest is to use HashSet<T>(StringComparer.OrdinalIgnoreCase)
. I tried doing a case sensitive search but things placed in the set are upper case and the item being checked for is uppercased before the check but that was slower. The SortedSet<T>
was just a little bit slower.
In my scenario, it took 1.04 milliseconds to check 10,000 times for the 10 items. I think the perf here is good enough but at least know we know. :emoji-for-the-'the-more-you-know'-public-service-annoncement:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, declare this as an ImmutableHashset and just copy the reference rather than cloning a new one. Theoretically they are slower, with logn lookup. Can you benchmark this one as well? :)
In reply to: 88770185 [](ancestors = 88770185)
@@ -584,6 +590,15 @@ public bool OnlyLogCriticalEvents | |||
} | |||
|
|||
/// <summary> | |||
/// A list of warnings to treat as errors. To treat all warnings as errors, set this to an empty <see cref="HashSet{String}"/>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. The double-use here seems a bit mysterious, in a way it doesn't on the command line. But it's not wrong . . .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to avoid having another property like bool HasUserSpecifiedWarningsAsErrors
so I am using the three states of a reference type; null, not null; or has a value. The usages of these are all internal so we can change it later. I made this decision for simplicity but am open to suggestions.
Assert.Equal(expectedBuildEvent.LineNumber, actualBuildEvent.LineNumber); | ||
Assert.Equal(expectedBuildEvent.BuildEventContext, actualBuildEvent.BuildEventContext); | ||
Assert.Equal(expectedBuildEvent.ThreadId, actualBuildEvent.ThreadId); | ||
Assert.Equal(expectedBuildEvent.Timestamp, actualBuildEvent.Timestamp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes! But I guess it's not worth implementing IEquatable
just for this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I thought about making a Clone()
method as well but I didn't want to go there... This unit test made sure I was properly copying every single property when I mutate the warning into an error (and I did miss one 😄)
|
||
When a warning is treated as an error the target will | ||
continue to execute as if it was a warning but the overall | ||
build result will show as failed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"but the overall build will fail." seems a bit clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha that's what I had originally and then I second guessed myself :sad:
|
||
Assert.NotNull(actualWarningsAsErrors); | ||
|
||
Assert.Equal(expectedWarningsAsErors, actualWarningsAsErrors, StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test implying that the switches are additive? I find that a bit surprising. And definitely I find it surprising that if the last one is a no-code-specified-turn-everything-on setting, that doesn't win.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was imaging a scenario where the list of warning codes was specified in one or more response files, an environment variable and the command-line. I figured I would just conglomerate them into one set. If this is the case, then specifying the switch by itself doesn't add any more codes and some codes already exist so the functionality is to only treat the specified codes as errors.
This is sort of how /p:foo=bar
works minus the ability to clear them all by specifying just /p
. Let's discuss this tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For posterity: we discussed, and decided that the additive model makes sense, but a new no-code-specified setting should override previous specific ones. If you need to clear the setting you can do /warnaserror /warnaserror:X
to get back to a "just X" state.
Assert.Equal(0, actualWarningsAsErrors.Count); | ||
} | ||
|
||
#if !RUNTIME_TYPE_NETCORE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResourceManager.GetResourceSet()
doesn't exist on .NET core. This test is just making sure I typed up the new command-line argument usage correctly and so it technically doesn't matter if it isn't running on .NET Core since it's testing all of the switches. The RESX resources are not #ifdef
'd out, only the display of them.
Should I put a comment?
@@ -1966,7 +1975,7 @@ bool recursing | |||
} | |||
|
|||
ErrorUtilities.VerifyThrow(!invokeBuild || !String.IsNullOrEmpty(projectFile), "We should have a project file if we're going to build."); | |||
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👻 trailing whitespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
grr
5be587d
to
25c00b7
Compare
@@ -36,8 +38,9 @@ public void BogusSwitchIdentificationTests() | |||
bool multipleParametersAllowed; | |||
string missingParametersErrorMessage; | |||
bool unquoteParameters; | |||
bool allowEmptyParmaters; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: Parmaters
_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 an null parameter if its allowed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: an -> a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This originally said "an empty string", thanks for catching it
|
||
Assert.NotNull(actualWarningsAsErrors); | ||
|
||
Assert.Equal(expectedWarningsAsErors, actualWarningsAsErrors, StringComparer.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For posterity: we discussed, and decided that the additive model makes sense, but a new no-code-specified setting should override previous specific ones. If you need to clear the setting you can do /warnaserror /warnaserror:X
to get back to a "just X" state.
@@ -2029,6 +2040,37 @@ internal static TextWriter ProcessPreprocessSwitch(string[] parameters) | |||
return writer; | |||
} | |||
|
|||
internal static ISet<string> ProcessWarnAsErrorSwitch(CommandLineSwitches commandLineSwitches) | |||
{ | |||
// TODO: Parse an environment variable as well? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly about this. Doesn't seem worth it.
I'm going to squash this and re-push. When we're ready I'd like to do a merge commit so I can start on the /NoWarn switch and not have any merge conflicts. |
Specify just /WarnAsError to have all warnings logged as errors as well as have the build fail. Specify a list of warning codes to have just that set of warnings treated as errors as well as have the build fail. Targets will still show as succeeded and the tasks will continue to execute but the overall build result will be a failure. Related to dotnet#68 and will close in my next change to add /NoWarn. * Feature switch FEATURE_RESOURCEMANAGER_GETRESOURCESET * Support for command-line arguments having empty values
Specify just /WarnAsError to have all warnings logged as errors as well as have the build fail.
Specify a comma delimited list of warning codes to have just that set of warnings treated as errors as well as have the build fail.
Targets will still show as succeeded and the tasks will continue to execute but the overall build result will be a failure.
Related to #68 and will close in my next change to add /NoWarn.
Help text should be reviewed as well: