Easily supporting multiple target frameworks (TFMs) with VS2017 and Nuget

April 5, 2017

2 comments

In VS2017 it much easier now to support multiple frameworks inside a single .csproj file.
When you create a new project VS2017 (that target .NET Core or .NET Standard) this is what you’ll get inside your *.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
  </PropertyGroup>

</Project>

Cool right? A lot of the boilerplate configuration was removed in order to make the MSBuild format clean and readable.
By default, all the files inside your project folder will be added. If you want to make adjustment you can always add (or remove some of the files).
For example, this is how it will look if I want to explicitly add the Properties folder to my project but exclude the AssemblyInfo.cs file.

  
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
  </PropertyGroup>
 
  <ItemGroup>
    <Folder Include="Properties\" />
    <Compile Remove="Properties\AssemblyInfo.cs" />
  </ItemGroup>
</Project>

Target Framework Moniker TFM

The new MSBuild format support declaration of multiple framework targets inside the same property group. If you need you library to support .NET 4.0, .NET 4.5, .NET Standard 1.6 and .NET Core 1.1, the only change you need to make is to change TargetFramework into plural form TargetFrameworks  and add the framework monikers you wish to support:

  
<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFrameworks>netcoreapp1.1;netstandard1.6;net45;net40</TargetFrameworks>
  </PropertyGroup>
 
  <ItemGroup>
    <Folder Include="Properties\" />
    <Compile Remove="Properties\AssemblyInfo.cs" />
  </ItemGroup>
</Project>

The full list of target frameworks (AKA framework moniker) can be found at: https://docs.microsoft.com/en-us/nuget/schema/target-frameworks#supported-frameworks

Of course, not everything is supported in every framework, so we need to add some conditions inside our code and inside our *.csproj file


MSBuild conditions to the rescue

Suppose our library has a dependency on RestSharp  Nuget package.
At the time of this writing, the RestSharp package for .NET Core is called RestSharp.NetCore  while the package for the full .NET Framework is called RestSharp .
So in order to had the dependency into our *.csproj  file we need to separate the reference into two sections. Fortunately, now that MSBuild support the <PackageReference> tag  this job becomes so much easier

<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFrameworks>netcoreapp1.1;netstandard1.6;net45;net40</TargetFrameworks>
  </PropertyGroup>
 
  <ItemGroup>
    <Folder Include="Properties\" />
    <Compile Remove="Properties\AssemblyInfo.cs" />
  </ItemGroup>
 
  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.1' or '$(TargetFramework)' == 'netstandard1.6' ">
    <PackageReference Include="RestSharp.NetCore" Version="105.2.3" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net40' ">
    <PackageReference Include="RestSharp" Version="105.2.3" />
  </ItemGroup>
</Project>

Auto Nuget packaging

One of the greatest pains that was with the old version of Nuget and MSBuild was the separation of the Nuget packaging from the project build. Basically, you had to create a *.nuspec file, add all the frameworks configuration into it and then add all the build artifacts into each of the sub-framework configuration. This was tedious.
Now, VS2017 integrates Nuget into the project itself

Embedded nuspec inside your *.csproj (Image taken from http://blog.nuget.org/20170316/NuGet-now-fully-integrated-into-MSBuild.html)

After I filled the fields and checked the ‘Generate Nuget Package on build’, this how my *.csproj look

<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
    <TargetFrameworks>netcoreapp1.1;netstandard1.6;net45;net40</TargetFrameworks>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
    <Company>TamirDresher</Company>
    <Authors>TamirDresher</Authors>
    <Description>A very impressive package</Description>
    <Version>1.2.3</Version>
  </PropertyGroup>
 
  <ItemGroup>
    <Folder Include="Properties\" />
    <Compile Remove="Properties\AssemblyInfo.cs" />
  </ItemGroup>
 
  <ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp1.1' or '$(TargetFramework)' == 'netstandard1.6' ">
    <PackageReference Include="RestSharp.NetCore" Version="105.2.3" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net40' ">
    <PackageReference Include="RestSharp" Version="105.2.3" />
  </ItemGroup>
</Project>

Now, when I build my project or click on ‘Package’ from the project context menu, a Nuget package will be created for all the supported frameworks
You can read about other configuration you can now make to your *.csproj here: https://docs.microsoft.com/en-us/dotnet/articles/core/tools/csproj

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

2 comments

  1. Stef HeyenrathApril 12, 2017 ב 22:16

    Did you also manage to build a uap10.0 version with appveyor ?

    Reply
    1. IBlogger
      IBloggerMay 1, 2017 ב 09:52

      I havent tried. But it should work in the very same way

      Reply