Monday, November 27, 2017

How to multi-target a .NET Core class library

I am developing an OWIN middleware library and I wanted to build a single set of source files for multiple .Net Platforms/version. My first thought WHICH IS VERY WRONG was to create multiple csproj files and then manually create the nuspec file that would bring them all together.

It turns out I was going to do way too much work. It is actually very simple, the worse part is that one must manually edit the csproj file itself.

Suppose we start with a very basic csproj file. This one is from a project that builds a piece of OWIN Middleware.


<project sdk="Microsoft.NET.Sdk"> <propertygroup> <targetframework>netcoreapp1.0</targetframework> ... NuGet tags removed for brevity </propertygroup> <itemgroup> <packagereference include="Microsoft.AspNetCore" version="1.0.5"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="1.1.2"> </itemgroup> </project>

As you can see the above project targets netcoreapp1.0. (look in the targetframework tag) I wanted to be able to also target netcoreapp2.0. To do this we need to change four things in the csproj file


  1. The existing itemgroup need a condition added to them to specify that they are for the currently targeted framework (netcoreapp1.0 in our example)
  2. The targetframework tag needed an S added to the end to make it targetframeworks
  3. Add the new target frame work to the list of frameworks in the targetframeworks tag
  4. New itemsgroup sections, with conditions, need to be added to support compilation of the additional target frameworks

Note: The above steps apply regardless of what additional framework(s) you are targeting. For a list of the framework monikers that should be used see Target frameworks

Now I will walk you thru the changes one at a time. First lets add the condition for netcoreapp1.0 to the itemgroup that contains the packagereferences.


<project sdk="Microsoft.NET.Sdk"> <propertygroup> <targetframework>netcoreapp1.0</targetframework> ... NuGet tags removed for brevity </propertygroup> <itemgroup Condition="'$(TargetFramework)'=='netcoreapp1.0'"> <packagereference include="Microsoft.AspNetCore" version="1.0.5"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="1.1.2"> </itemgroup> </project>

Adding the highlighted piece will make that itemgroup apply only when compiling for netcoreapp1.0

Next lets update the target framework tag


<project sdk="Microsoft.NET.Sdk"> <propertygroup> <targetframeworks>netcoreapp1.0</targetframeworks> ... NuGet tags removed for brevity </propertygroup> <itemgroup Condition="'$(TargetFramework)'=='netcoreapp1.0'"> <packagereference include="Microsoft.AspNetCore" version="1.0.5"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="1.1.2"> </itemgroup> </project>


Don't forget to update the start and end tags. Now lets add the additional framework we want to target, netcoreapp2.0 in this case.


<project sdk="Microsoft.NET.Sdk"> <propertygroup> <targetframeworks>netcoreapp1.0;netcoreapp2.0</targetframeworks> ... NuGet tags removed for brevity </propertygroup> <itemgroup Condition="'$(TargetFramework)'=='netcoreapp1.0'"> <packagereference include="Microsoft.AspNetCore" version="1.0.5"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="1.1.2"> </itemgroup> </project>

Lastly lets add the package references needed for this project to compile on when targeting the netcoreapp2.0 platform.


<project sdk="Microsoft.NET.Sdk"> <propertygroup> <targetframeworks>netcoreapp1.0;netcoreapp2.0</targetframeworks> ... NuGet tags removed for brevity </propertygroup> <itemgroup Condition="'$(TargetFramework)'=='netcoreapp1.0'"> <packagereference include="Microsoft.AspNetCore" version="1.0.5"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="1.1.2"> </itemgroup> <itemgroup Condition="'$(TargetFramework)'=='netcoreapp1.0'"> <packagereference include="Microsoft.AspNetCore" version="2.0.0"> <packagereference include="Microsoft.AspNetCore.Http.Features" version="2.0.0"> <packagereference include="Microsoft.Extensions.Logging.Abstractions" version="2.0.0"> </itemgroup> </project>

Now binaries for both platforms will be built when the project is compiled. If the project is configured to create a NuGet package via the GeneratePackageOnBuild tag the NuGet package will contain binaries for both platforms. If you do this to a project containing tests. you will then have tests for all supported platforms.

A few last details:
  • Once you edit the csproj by hand for multi-targetting, Visual Studio 2017 doesn't seem to be able to correctly make changes to the file. Each time I have tried to use the UI to make a change, I have then had to re-edit the file by hand to handle the multi0-targetting.
  • If you have an item group that needs multiple conditions. for example if you wanted to also target .net452, you need to add an Or between the conditions. It would look like this: <ItemGroup Condition="'$(TargetFramework)'=='netcoreapp1.0' Or '$(TargetFramework)'=='net452'>

Edit (4/30/2018): Note: In retrospect this post should have been titled how to multi-target a .Net Core application. For a class library it is best to target .Net Standard so that it is useable across multiple .Net platforms. For a more detailed explanation of .Net Standard see this blog post: Introducing .Net Standard.

1 comment:

  1. Golden Nugget Casino & Hotel - MapyRO
    Find Golden Nugget Casino 상주 출장안마 & Hotel, Las Vegas, 과천 출장샵 NV, United States, Great Smokies, and other places to 대구광역 출장마사지 stay near 익산 출장안마 the Golden 시흥 출장마사지 Nugget Casino & Hotel.

    ReplyDelete

The 2024 State of DevOps Report and the Importance of Internal Development Platforms

On the State of DevOps Report The State of DevOps Report, published annually by the DevOps Research and Assessment (DORA) team, has been a c...