Treat all warnings as errors

28,209

Solution 1

MSBuild warnings (all start with MSB*) as opposed to CSC warnings cannot be suppressed nor promoted to errors. For the reason the ResolveAssemblyReference task prints its messages on the fly and does not aggregate any of them.

The only feasible solution is reading the MSBuild log files created during the TFS build. I think the most elegant solution is to implement a custom Build CodeActivity. The following is a simple activity that will output to results any files containing a given SearchString:

using System;
using System.Activities;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.TeamFoundation.Build.Client;

namespace MyBuildActivities.FileSystem
{
    [BuildActivity(HostEnvironmentOption.Agent)]
    public sealed class ReadStringFromFile : CodeActivity
    {
        [RequiredArgument]
        public InArgument<IEnumerable<string>> Files { get; set; }

        [RequiredArgument]
        public InArgument<string> SearchString { get; set; }

        public OutArgument<string> Result { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            var files = context.GetValue(Files);
            var searchString = context.GetValue(SearchString);

            var list =
                (files.Where(file => File.ReadAllText(file).Contains(searchString))
                    .Select(file => string.Format("{0} was found at {1}", searchString, file))).ToList();

            if(list.Count > 0)
                Result.Set(context, string.Join(Environment.NewLine, list));
        }
    }
}

Declared in the build process template like so:

xmlns:cfs="clr-namespace:MyBuildActivities.FileSystem;assembly=MyBuildActivities"

Invoked just at the end of the Compile and Test for Configuration sequence:

<Sequence DisplayName="Handle MSBuild Errors">
         <Sequence.Variables>
                 <Variable x:TypeArguments="scg:IEnumerable(x:String)" Name="logFiles" />                                                                                                                 
                 <Variable x:TypeArguments="x:String" Name="readStringFromFileResult" />
         </Sequence.Variables>
         <mtbwa:FindMatchingFiles DisplayName="Find Log Files" MatchPattern="[String.Format(&quot;{0}\**\*.log&quot;, logFileDropLocation)]" Result="[logFiles]" mtbwt:BuildTrackingParticipant.Importance="Low" />
         <cfs:ReadStringFromFile Files="[logFiles]" SearchString="MSB3245" Result="[readStringFromFileResult]" />
         <mtbwa:WriteBuildMessage DisplayName="Write Result" Message="[readStringFromFileResult]" Importance="[Microsoft.TeamFoundation.Build.Client.BuildMessageImportance.High]" />
         <If Condition="[readStringFromFileResult.Count > 0]" DisplayName="If SearchString Was Found" mtbwt:BuildTrackingParticipant.Importance="Low">
                 <If.Then>
                          <Throw DisplayName="Throw Exception" Exception="[New Exception(readStringFromFileResult)]" mtbwt:BuildTrackingParticipant.Importance="Low" />
                 </If.Then>
         </If>                                                                                                              
</Sequence>

I've tested this on TFS 2012 though it should work for TFS 2010 as well.

Solution 2

MSBuild 15 now supports a /warnaserror flag, which forces MSBuild warnings to be treated as errors.

MSBuild 15 is intalled with Visual Studio 2017 but can also be downloaded from GitHub

This GitHub issue explains why it cannot be set via an MSBuild property (tl;dr a property is too late)

Share:
28,209
ken2k
Author by

ken2k

I'm a software architect and .NET/Azure lead dev from France with more than 10 years of experience. I work with the Microsoft technical stack: .NET /.NET Core (EF/EF Core, ASP.Net MVC/Core...) and Azure. I also used to work with techs now considered old: Silverlight framework, Winforms, WPF, Windows Phone, WCF. Not active anymore, because SO is now a huge pile of s*** that becomes more unwelcoming every day. Yeah, it was way better back in the days. Count me out. #old-man-ranting

Updated on July 09, 2022

Comments

  • ken2k
    ken2k almost 2 years

    This should be obvious to do, but I just couldn't make it work...

    What I'm trying to do is simple: I would like my compilation to fail with an error if there is a warning. Yes, the famous TreatWarningsAsErrors...

    I configured it in my C# project properties

    treat warnings as errors

    This results in the expected TreatWarningsAsErrors section in my csproj:

    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    

    So far so good, if I add an useless private variable in my code, it results in a compilation error:

    private int unused;
    

    Error 3 Warning as Error: The field 'XXXX.unused' is never used

    But the whole problem is, I can't make it work for assembly reference issues. If I have a reference to an unknown assembly, the compiler (either devenv or msbuild) throws a warning, but I want an error instead.

    Ultimately, I'm trying to configure a gated check-in TFS build configuration, so TFS would reject a commit in case there is a "The referenced component 'XXXX' could not be found." warning. Something simpler than modifying the build process template would be great.

  • ken2k
    ken2k almost 11 years
    That's what I was afraid of... I understand warnings from MSBuild itself cannot be promoted to errors. What I don't understand is why an unknown assembly reference is not a CSC warning. Anyway, a custom activity is indeed the way to go, even if at first glance it sounds a bit like overkill for the (apparently) simple use case I had. +1, thanks for your answer.
  • user1574808
    user1574808 over 10 years
    To make the solution posted by KMoraz work I had to do the following: 1. Add empty string to Result in the CodeActivity if the string was not found in the files. 2. Change readStringFromFileResult.Count to readStringFromFileResult.Length in the build template. Otherwise I would get a null exception when the string was not found.