How can I treat MSB3245 (could not resolve reference) warning as an error?

Recently I needed something similar (acting on certain log events) but I couldn’t find a clean solution mainly because I haven’t figured out how to programmatically access the loggers in the msbuild process. I did come up with this though, adapted to your problem the principle is:

  • install a custom logger scanning for the warning
  • build
  • set a static flag if the warning occurs
  • have a custom task check that flag and raise an error if it’s on

Might sound hard, but the code is simple enough:

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Foo
{
  public static class Common
  {
    public static bool errorsOccurred = false;
  }

  public class ScanLogger : Logger
  {
    public override void Initialize( IEventSource eventSource )
    {
      eventSource.MessageRaised += ( s, e ) =>
        Common.errorsOccurred |= e.Message.Contains( "MSB3245" );
    }
  }

  public class CheckErrors : Task
  {
    public override bool Execute()
    {
      if( Common.errorsOccurred == false )
        return true;
      Log.LogError( "errorsOccurred = true" );
      return false;
    }
  }
}

Here’s a sample msbuild script using it:

<UsingTask TaskName="Foo.CheckErrors" AssemblyFile="Foo.dll"/>

<Target Name="MyBuild">
  <Message Text="MSB3245"/> <!-- simulate the build warning -->
  <Foo.CheckErrors /> <!-- this will trigger an error -->
</Target>

And you invoke it like this:

msbuild /logger:Foo.dll my.proj

edit I just needed this again but couldn’t find the original dll nor project file etc anymore – I figured storing just the code and simplest build instructions in git and building it on the fly when needing it is probably cleaner. So basically store the code above in a file customlogger.cs and then somewhere in your build process, before effectively invoking msbuild with the custom logger, build it using

<Target Name="BuildCustomLoggerDll">
  <Csc Sources="$(MSBuildThisFileDirectory)customlogger.cs"
       References="System.dll;mscorlib.dll;Microsoft.Build.Framework.dll;Microsoft.Build.Utilities.v4.0.dll"
       TargetType="Library" OutputAssembly="$(MSBuildThisFileDirectory)CustomLogger.dll"/>
</Target>

update In response to comments: trying this again today I’m not sure the original code actually ever worked (well, it does for the sample message shown but not for actual warning MSB3245), since it hooks message events only whereas the ResolveAssemblyReference emits an actual warning event and moreover the warning number isn’t typically contained contained in the message. This does the trick though:

public class ScanLogger : Logger
{
  public override void Initialize( IEventSource eventSource )
  {
    eventSource.WarningRaised += ( s, e ) => Common.errorsOccurred |= e.Code == "MSB3245";
  }
}

Leave a Comment