Why is building with one version of MSBuild raising errors looking for assemblies from another version of MSBuild?

1.4k Views Asked by At

My codebase contains about 30 solutions. Most of the projects in these solutions are (C#) .NET Framework, but more recently I've been adding some .NET Standard 2.0 and .NET Core 3.1 projects. All projects in all solutions build perfectly fine in Visual Studio 2019 - just click "Build" and they build.

All projects in all solutions also build in Azure DevOps (...although configuring the build pipelines to reach that point was painful).

As a convenience, I wrote a simple tool that would use the Microsoft.Build NuGet package to cycle through all solutions in the codebase and build them. This would allow me to quickly check whether I'd made any changes in one solution that broke something in another (e.g. in a shared project) without me noticing. This is where the troubles begin.

.NET Framework projects all build fine. However, when building any .NET Core project, I get build errors. These vary slightly from project to project, but all include some error similar to the following:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VisualStudio\v16.0\VSSDK\Microsoft.VsSDK.targets(583,5):
   error MSB4018: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Build, Version=14.0.0.0, 
   Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
   The system cannot find the file specified.

In some cases it might be Microsoft.Build.Framework instead of Microsoft Build, or it might be version 15.0.0.0 instead of 14.0.0.0, and there are always other errors in addition that arise from not being able to find the assembly it is looking for; but this appears to be the core of the problem.

The Microsoft.Build Nuget package is version 16.7.0. The only installed MSBuild version is whichever is installed with with Visual Studio 2019 version 16.7.6, which I think is 16.7.0. None of the affected projects have any apparent dependency on older versions of MSBuild, e.g. in the project their ToolsVersion="16.0", and anyway if there was something wrong with the project it wouldn't build in Visual Studio - not to mention that some of them are brand new as of only the last few days.

So, why is something(?) deciding to look for older versions of Microsoft.Build assemblies (and failing), when I'm building with a current version of Microsoft.Build assemblies, and the projects are all correct, and they all build in Visual Studio? Or - how can I find this out for myself?

1

There are 1 best solutions below

1
Lex Li On BEST ANSWER

It is normal, as building a complex project (and its dependencies) can load many MSBuild extensions into the memory, which can depend on another (usually older) version of MSBuild assemblies.

That's exactly why Microsoft's MSBuild.exe.config contains assembly redirection as below (and you should do the same),

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="msbuildToolsets" type="Microsoft.Build.Evaluation.ToolsetConfigurationSection, Microsoft.Build, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
  </configSections>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
  </startup>
  <runtime>
    <DisableFXClosureWalk enabled="true" />
    <DeferFXClosureWalk enabled="true" />
    <generatePublisherEvidence enabled="false" />
    <AppContextSwitchOverrides value="Switch.System.Security.Cryptography.UseLegacyFipsThrow=false" />
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Framework" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Conversion.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Tasks.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Utilities.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Engine" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.Conversion.Core" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="0.0.0.0-99.9.9.9" newVersion="15.1.0.0" />
      </dependentAssembly>
      <!-- Redirects for components dropped by Visual Studio -->
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Activities.Build" culture="neutral" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="4.0.0.0" newVersion="16.0.0.0" />
        <codeBase version="16.0.0.0" href=".\amd64\Microsoft.Activities.Build.dll" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="XamlBuildTask" culture="neutral" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="4.0.0.0-16.0.0.0" newVersion="16.0.0.0" />
        <codeBase version="16.0.0.0" href=".\amd64\XamlBuildTask.dll" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Build.CPPTasks.Common" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <bindingRedirect oldVersion="16.0.0.0-16.7.0.0" newVersion="16.7.0.0" />
        <codeBase version="16.7.0.0" href="..\..\Microsoft\VC\v160\Microsoft.Build.CPPTasks.Common.dll" />
      </dependentAssembly>
      <!-- Workaround for crash in C++ CodeAnalysis scenarios due to https://github.com/Microsoft/msbuild/issues/1675 -->
      <dependentAssembly>
        <assemblyIdentity name="FxCopTask" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <codeBase version="16.0.0.0" href="..\..\Microsoft\VisualStudio\v16.0\CodeAnalysis\FxCopTask.dll" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.VisualStudio.CodeAnalysis" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <codeBase version="16.0.0.0" href="..\..\Microsoft\VisualStudio\v16.0\CodeAnalysis\Microsoft.VisualStudio.CodeAnalysis.dll" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.VisualStudio.CodeAnalysis.Sdk" culture="neutral" publicKeyToken="b03f5f7f11d50a3a" />
        <codeBase version="16.0.0.0" href="..\..\Microsoft\VisualStudio\v16.0\CodeAnalysis\Microsoft.VisualStudio.CodeAnalysis.Sdk.dll" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
      </dependentAssembly>
    </assemblyBinding>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <!-- To define one or more new toolsets, add an 'msbuildToolsets' element in this file. -->
  <msbuildToolsets default="Current">
    <toolset toolsVersion="Current">
      <property name="MSBuildToolsPath" value="$([MSBuild]::GetCurrentToolsDirectory())" />
      <property name="MSBuildToolsPath32" value="$([MSBuild]::GetToolsDirectory32())" />
      <property name="MSBuildToolsPath64" value="$([MSBuild]::GetToolsDirectory64())" />
      <property name="MSBuildSDKsPath" value="$([MSBuild]::GetMSBuildSDKsPath())" />
      <property name="FrameworkSDKRoot" value="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK\4.8@InstallationFolder)" />
      <property name="MSBuildRuntimeVersion" value="4.0.30319" />
      <property name="MSBuildFrameworkToolsPath" value="$(SystemRoot)\Microsoft.NET\Framework\v$(MSBuildRuntimeVersion)\" />
      <property name="MSBuildFrameworkToolsPath32" value="$(SystemRoot)\Microsoft.NET\Framework\v$(MSBuildRuntimeVersion)\" />
      <property name="MSBuildFrameworkToolsPath64" value="$(SystemRoot)\Microsoft.NET\Framework64\v$(MSBuildRuntimeVersion)\" />
      <property name="MSBuildFrameworkToolsRoot" value="$(SystemRoot)\Microsoft.NET\Framework\" />
      <property name="SDK35ToolsPath" value="$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.0A\WinSDK-NetFx35Tools-x86', 'InstallationFolder', null, RegistryView.Registry32))" />
      <property name="SDK40ToolsPath" value="$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK\4.8\WinSDK-NetFx40Tools-x86', 'InstallationFolder', null, RegistryView.Registry32))" />
      <property name="WindowsSDK80Path" value="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows\v8.1@InstallationFolder)" />
      <property name="VsInstallRoot" value="$([MSBuild]::GetVsInstallRoot())" />
      <property name="MSBuildToolsRoot" value="$(VsInstallRoot)\MSBuild" />
      <property name="MSBuildExtensionsPath" value="$([MSBuild]::GetMSBuildExtensionsPath())" />
      <property name="MSBuildExtensionsPath32" value="$([MSBuild]::GetMSBuildExtensionsPath())" />
      <property name="RoslynTargetsPath" value="$([MSBuild]::GetToolsDirectory32())\Roslyn" />
      <!-- VC Specific Paths -->
      <property name="VCTargetsPath" value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath)','$(MSBuildExtensionsPath32)\Microsoft\VC\v160\'))" />
      <property name="VCTargetsPath14" value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath14)','$([MSBuild]::GetProgramFiles32())\MSBuild\Microsoft.Cpp\v4.0\V140\'))" />
      <property name="VCTargetsPath12" value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath12)','$([MSBuild]::GetProgramFiles32())\MSBuild\Microsoft.Cpp\v4.0\V120\'))" />
      <property name="VCTargetsPath11" value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath11)','$([MSBuild]::GetProgramFiles32())\MSBuild\Microsoft.Cpp\v4.0\V110\'))" />
      <property name="VCTargetsPath10" value="$([MSBuild]::ValueOrDefault('$(VCTargetsPath10)','$([MSBuild]::GetProgramFiles32())\MSBuild\Microsoft.Cpp\v4.0\'))" />
      <property name="AndroidTargetsPath" value="$(MSBuildExtensionsPath32)\Microsoft\MDD\Android\V150\" />
      <property name="iOSTargetsPath" value="$(MSBuildExtensionsPath32)\Microsoft\MDD\iOS\V150\" />
      <projectImportSearchPaths>
        <searchPaths os="windows">
          <property name="MSBuildExtensionsPath" value="$(MSBuildProgramFiles32)\MSBuild" />
          <property name="MSBuildExtensionsPath32" value="$(MSBuildProgramFiles32)\MSBuild" />
          <property name="MSBuildExtensionsPath64" value="$(MSBuildProgramFiles32)\MSBuild" />
          <property name="VSToolsPath" value="$(MSBuildProgramFiles32)\MSBuild\Microsoft\VisualStudio\v$(VisualStudioVersion)" />
        </searchPaths>
      </projectImportSearchPaths>
    </toolset>
  </msbuildToolsets>
</configuration>