Every time Microsoft releases some build tooling it feels like they forget lots of us use it to build websites. And we do this on build servers that do not have Visual Studio installed using scripts. We even dare to have created websites with old versions of Visual Studio and upgrade them when a new one comes out.

I often find that these scripted builds fail on build servers because they are missing Microsoft.WebApplication.targets, but this week I had a case where they were missing on my dev desktop. Well not missing but not copied to right path some dev tooling was expecting. In this case dotnet build was unable to find the web targets.

The problem

Create a brand new dot net 4.6 full framework (not core) website with VS 2017 15.2 and try and use the new dotnet.exe CLI to build it by running dotnet build in the solution folder. I got this error:

SampleWebApp.csproj(262,3): error MSB4019: The imported project "C:\Program Files\dotnet\sdk\1.0.4\Microsof
t\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.

As usual, it builds fine in Studio 2017. Hmmm, why is a looking in the Core SDK folders? It builds fine if I go to msbuild directly at C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\msbuild but the new CLI tooling does not seem to understand a full framework website. I am pretty sure this used to work but an update seems to have broken it?

The solution

This is a hack, I am sure it will be fixed in an updated dotnet core SDK. There are other ways to solve it no doubt, but my hack was to change the csproj file (as usual). This time I forced the path of VSTools to use the Microsoft.WebApplication.targets file from Studio 2015 (the 2017 ones have a path based on application version - pro, enterprise, buildtools etc - so are a pain to path for easily with all the build tools)

Change the line in the csproj from

<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

To

<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildProgramFiles32)\MSBuild\Microsoft\VisualStudio\v14.0</VSToolsPath>

So that the whole section becomes

<PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildProgramFiles32)\MSBuild\Microsoft\VisualStudio\v14.0</VSToolsPath>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
  <Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
    <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
  </Target>

It is nasty and hard-coded to Studio 2015 (v14!) but does actually work in Studio 2015, 2017, dotnet.exe, msbuild.exe and on your build agents assuming you installed stuff to the default locations, and have 2015 build tools. This is more than can be said for the template web app created in VS 2017 which will not build.

I hope someone at MS is reading and sorts this stuff out. I dream of the day when all the build tools can build all the apps produced by the IDE.