Migrating a Cross-targeted project.json .NET Core Library to csproj Using the .NET Core CLI

Published on Friday, March 24, 2017

Today I migrated several of our internal C# libraries we use at CSG that are using .NET Core from the project.json to csproj format using the .NET Core CLI tools. Everything went well, but I ran into one snag with trying to build the NuGet packages. Here are my steps I went through to migrate and resolve the errors I encountered.

First, I started with deleting the global.json file that was being used to pin the sdk/cli tooling to a pre-csproj version. I opened the project in the command line and ran the migration command, which is dotnet migrate.

dotnet-migrate

So far so good. I went ahead and tried to build a Release build with a NuGet package by running dotnet build –configuration Release, which is what our continuous integration build script is doing to generate the NuGet packages.

dotnet-build

Well crap, the build command was failing with the following errors:

error MSB4018: The "PackTask" task failed unexpectedly.
error MSB4018: System.IO.FileNotFoundException: File not found: '...\bin\Release\net461\Csg.Data.Dapper.dll'.

There were some various other flavors of this error involving the netstandard1.6 path instead of net461, but they were all similar. It appeared to be failing when trying to build the nupkg because of missing build output files. What was weirder, is that running the Build command (CTRL-SHIFT-B) in Visual Studio 2017 26228.4 was showing the same errors in the Output window, but the build was completing with a “Build: 1 succeeded, 0 failed, 0 skipped”. It wasn’t picking up on the fact that the dotnet pack command was failing. This seems like an unrelated problem with VS2017, so I went back to the command line build. I tried a view variations of dotnet build using the –framework argument, which led me to believe it was some issue with the multi-targeting being used in the project.

These projects are targeting both the full .NET Framework 4.6.1 and as well as NET Standard 1.6. Before the migration, the frameworks section of the project.json looked like this:

"frameworks": {
  "netstandard1.6": {
    "dependencies": {
      "NETStandard.Library": "1.6.0"
    }
  },
  "net461": {
  }
}

It’s worth mentioning here that there is no support via the Visual Studio 2017 UI (Project properties page) for configuring multiple target frameworks as mentioned by Damian Edwards in one of the recent ASP.NET Community Standup’s. The equivalent csproj syntax looks like this:

<PropertyGroup>
...
  <Authors>Justin R. Buchanan</Authors>
  <TargetFrameworks>netstandard1.6;net461</TargetFrameworks>
  <DebugType>portable</DebugType>
...
</PropertyGroup>

Note the framework list is semi-colon delimited in a <TargetFrameworks> element instead of <TargetFramework>.

While I was in the csproj I noticed it had migrated this project.json scripts content:

"scripts": {
  "postcompile": \[
    "dotnet pack --no-build --configuration %compile:Configuration%"
  \]
}

to this csproj equivalent:

<Target Name="PostcompileScript" AfterTargets="Build" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
  <Exec Command="dotnet pack --no-build --configuration $(Configuration)" />
</Target>

I removed the new tag entirely, and then manually ran dotnet build and then dotnet pack, and yay, it worked!

EDIT: I was reading over this again today and noticed the Condition on the that seems like it should have prevented me from having this error (althought it wouldn’t have generated a NuGet package). I’ll have to do more research on exactly when $(IsCrossTargetingBuild) evaluates to true. It seems like I would have still needed to add the new dotnet pack step outlined below though.

dotnet-build-yay
dotnet-pack-yay

The only change I had to make at this point was to to add an extra step to our build.cmd (used by our build server) to run the dotnet pack command. Previously we had not needed this because the dotnet build command was running the pack in the postcompile steps. Now it looks something like this:

SET SOLUTION=Csg.Data.Dapper.sln
SET BUILD\_CONFIG=Release
...
dotnet build %SOLUTION% --configuration %BUILD\_CONFIG%
...
dotnet pack %SOLUTION% --no-build --configuration %BUILD\_CONFIG%
...

I’m not sure if this is a bug, or if I’m doing something else wrong, or both, but this got me past the error and I was able to continue migrating the rest of our libraries from project.json to csproj.