Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Multi-Target with .NET Standard and Portable Library MSBuild 15

Porting to .NET standard version, you may be source identical to a portable library, but the platforms supported may not.

For example .NET standard 1.6 might be the lowest version that has your api's available from portable Profile47. Profile47 supports .net 4.5 or later, nut .NET Standard 1.6 only supports 4.6.1 and later!

With the new multi-targeting with the new msbuild 15 csproj/fsproj (also VS2017) is it possible to compile for both a Portable Library and .Net Standard at the same time to make the transition easier?

like image 440
jbtule Avatar asked Dec 24 '22 16:12

jbtule


1 Answers

Yes, but it's not as obvious as cross compiling .netstandard with .net45 or .net40 as the aren't easy monikers for your portable libraries predefined.

In the new sdk's Microsoft.NET.TargetFrameworkInference.targets it says:

For cases where inference is not supported, identifier, version and profile can be specified explicitly as follows:

   <PropertyGroup>
     <TargetFrameworks>portable-net451+win81;xyz1.0</TargetFrameworks>
   <PropertyGroup>
   <PropertyGroup Condition="'$(TargetFramework)' == 'portable-net451+win81'">
     <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
     <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
     <TargetFrameworkProfile>Profile44</TargetFrameworkProfile>
   </PropertyGroup>
   <PropertyGroup Condition="'$(TargetFramework)' == 'xyz1.0'">
     <TargetFrameworkIdentifier>Xyz</TargetFrameworkVersion>
   <PropertyGroup>

TargetFrameworkProfile You get that from your old csproj/fsproj. That's the Profile identifier your portable library. You can also look it up from Nuget Target Frameworks as well.

TargetFrameworkIdentifier is easy, it's .NETPortable

TargetFrameworkProfile Probably also can get from your old csproj/fsprog. But you can also check C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\ and check the v4.0, v4.5, and v4.6 Profile directories and find your Profile. A listing of those directories is as follows:

v4.0

Profile1/    Profile143/  Profile2/    Profile3/    Profile4/   Profile6/
Profile102/  Profile147/  Profile225/  Profile328/  Profile41/  Profile88/
Profile104/  Profile154/  Profile23/   Profile336/  Profile42/  Profile92/
Profile131/  Profile158/  Profile24/   Profile344/  Profile46/  Profile95/
Profile136/  Profile18/   Profile240/  Profile36/   Profile47/  Profile96/
Profile14/   Profile19/   Profile255/  Profile37/   Profile5/

v4.5

Profile111/  Profile259/  Profile49/  Profile7/  Profile75/  Profile78/

v4.6

Profile151/  Profile157/  Profile31/  Profile32/  Profile44/  Profile84/

Does the moniker you choose for your portable targets? Yes and no, it matters for automatically packaging for nuget, so it's best to use the value from Nuget Target Frameworks with one caveat. I'm not sure if it's because of the newest version of nuget, but it seems to want the TargetFrameworkVersion added to portable in the moniker. So for Profile47, which is v4.0, should actually be portable40-net45+sl5+win8

Another item to add a compiler define which you might find useful using DefineConstants. You can make it anything you want, it's been historically the PROFILEXXX in caps, just add it to the Conditional Group.

<DefineConstants>$(DefineConstants);PROFILE47</DefineConstants>

The final thing to note, default Reference includes are not provided so you will have to add your own.

<ItemGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8'">
  <Reference Include="System" />
  <Reference Include="System.Core" />
  <Reference Include="System.Windows" />
</ItemGroup>

CSharp Example

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

  <PropertyGroup>
    <TargetFrameworks>netstandard1.6;portable40-net45+sl5+win8</TargetFrameworks>
    <Description>YOUR DESCRIPTION</Description>
    <Company>YOUR COMPANY</Company>
    <Authors> YOUR AUTHORS </Authors>
    <Copyright>YOUR COPYRIGHT</Copyright>
    <AssemblyVersion>YOUR VERSION</AssemblyVersion>
    <FileVersion>YOUR VERSION</FileVersion>
    <PackageProjectUrl>YOUR PROJECT URL</PackageProjectUrl>
    <PackageLicenseUrl>YOUR LICENSE</PackageLicenseUrl>
    <PackageTags>YOUR TAGS</PackageTags>
    <IncludeSymbols>True</IncludeSymbols>
    <IncludeSource>True</IncludeSource>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
    <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
    <Version>YOUR NUGET VERSION</Version>
  </PropertyGroup>

  <PropertyGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8'">
     <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
     <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
     <TargetFrameworkProfile>Profile47</TargetFrameworkProfile>
     <DefineConstants>$(DefineConstants);PROFILE47</DefineConstants>
   </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8'">
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Windows" />
  </ItemGroup>

</Project>

F# Example

*Caveat: F# requires beta version of Mono for Mac or Visual Studio 2017 preview 15.3 presently for this fsproj. Also a change from examples includes a PackageReference to FSharp.Compiler.Tools and FSharp.Core 4.1 the last portable supported FSharp versions (which is relatively quite recent.)

<Project Sdk="FSharp.NET.Sdk;Microsoft.NET.Sdk" ToolsVersion="15.0">

  <PropertyGroup>
    <TargetFrameworks>netstandard1.6;portable40-net45+sl5+win8</TargetFrameworks>
    <Description>YOUR DESCRIPTION</Description>
    <Company>YOUR COMPANY</Company>
    <Authors> YOUR AUTHORS </Authors>
    <Copyright>YOUR COPYRIGHT</Copyright>
    <AssemblyVersion>YOUR VERSION</AssemblyVersion>
    <FileVersion>YOUR VERSION</FileVersion>
    <PackageProjectUrl>YOUR PROJECT URL</PackageProjectUrl>
    <PackageLicenseUrl>YOUR LICENSE</PackageLicenseUrl>
    <PackageTags>YOUR TAGS</PackageTags>
    <IncludeSymbols>True</IncludeSymbols>
    <IncludeSource>True</IncludeSource>
    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
    <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
    <Version>YOUR NUGET VERSION</Version>
  </PropertyGroup>

  <PropertyGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8'">
     <TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
     <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
     <TargetFrameworkProfile>Profile47</TargetFrameworkProfile>
     <DefineConstants>$(DefineConstants);PROFILE47</DefineConstants>
   </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='portable40-net45+sl5+win8'">
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Windows" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="FSharp.Core" Version="4.1.*" />
    <PackageReference Include="FSharp.Compiler.Tools" Version="4.1.*" PrivateAssets="All" 
    />
    <PackageReference Include="FSharp.NET.Sdk" Version="1.0.*" PrivateAssets="All" 
    />
  </ItemGroup>

  <ItemGroup>
    <Compile Include="YourModule1.fs" />
    <Compile Include="YourModule2.fs" />
  </ItemGroup>

</Project>
like image 182
jbtule Avatar answered Dec 26 '22 07:12

jbtule