Skip to content

Commit

Permalink
Merge pull request #94 from onovotny/rids-take-2
Browse files Browse the repository at this point in the history
Build each RID on the inner loop
  • Loading branch information
Oren Novotny authored Jul 10, 2018
2 parents 321123e + 0504810 commit 5865168
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 9 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# MSBuild.Sdk.Extras
# MSBuild.Sdk.Extras

## Summary

Expand All @@ -8,7 +8,21 @@ The primary goal of this project is to enable multi-targeting without you having

See the [blog post](https://oren.codes/2017/01/04/multi-targeting-the-world-a-single-project-to-rule-them-all) for more information.

### Package Name: `MSBuild.Sdk.Extras`
## Advanced Scenarios

This package also enables advanced library scenarios, allowing you to create reference assemblies and per-RuntimeIdentifier targets.

### Reference Assemblies

Reference Assemblies useful in a few scenarios. Please see my [two](https://oren.codes/2018/07/09/create-and-pack-reference-assemblies-made-easy/) [blogs](https://oren.codes/2018/07/03/create-and-pack-reference-assemblies/) for more details.

### Per-RuntimeIdentifier

In some cases involving native interop, it may be necessary to have different runtime versions. NuGet has supported this for a while if you use `PackageReference` by way of its `runtimes` folder in combination with a Reference Assembly. Creating and packing these were manual though.

See [below](#rids) for creating these using the Extras easily.

## Package Name: `MSBuild.Sdk.Extras`

[![MSBuild.Sdk.Extras](https://img.shields.io/nuget/v/MSBuild.Sdk.Extras.svg)](https://nuget.org/packages/MSBuild.Sdk.Extras)
[![MSBuild.Sdk.Extras](https://img.shields.io/myget/msbuildsdkextras/v/MSBuild.Sdk.Extras.svg)](https://myget.org/feed/msbuildsdkextras/package/nuget/MSBuild.Sdk.Extras)
Expand Down Expand Up @@ -152,3 +166,23 @@ Once this package is configured, you can now use any supported TFM in your `Targ
For legacy PCL profiles, the order of the TFM's in the list does not matter but the profile must be an exact match to one of the known profiles. If it's not, you'll get a compile error saying it's unknown. You can see the full list of known profiles here: [Portable Library Profiles by Stephen Cleary](https://portablelibraryprofiles.stephencleary.com/).

If you try to use a framework that you don't have tools installed for, you'll get an error as well saying to check the tools. In some cases this might mean installing an older version of Visual Studio IDE (_like 2015_) to ensure that the necessary targets are installed on the machine.

### <a id="rids"></a>Creating Per-RuntimeIdentifier packages

You'll need a few things

1. Make sure to use `TargetFrameworks` instead of `TargetFramework`, even if you're only building a single target framework. We are piggy-backing off of it's looping capabilities.
2. Set the `RuntimeIdentifiers` property to [valid RID's](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog) ([full list](https://github.com/dotnet/corefx/blob/master/pkg/Microsoft.NETCore.Platforms/runtime.json)), separated by a semi-colon. (`<RuntimeIdentifiers>win;unix</RuntimeIdentifiers>`). These can be set per TFM using a condition on the property. This lets you have multiple TFM's, but only some of which have RID's.
3. For the TFM's that you want want to build separately, set the property `ExtrasBuildEachRuntimeIdentifier` to `true`.

While the Visual Studio context won't show each RID, it'll build for each. The Extras defines a symbol for each RID for use (`win-x86` would be `WIN_X86` and `centos.7-x64` would be `CENTOS_7_X64`). Dots and dashes become underbars.

You will also need a Reference Assembly for the `ref` folder. Please see my [two](https://oren.codes/2018/07/09/create-and-pack-reference-assemblies-made-easy/) [blogs](https://oren.codes/2018/07/03/create-and-pack-reference-assemblies/) articles for details.

When you're done, you should be able to build/pack these and it'll create a NuGet package. Your per-RID targets will be in `runtimes/<rid>/lib/<tfm>` and the Reference Assembly will be in `ref/<tfm>`.

If you need to add native assets into runtimes, the easiest way is to use:

`<None Include="path/to/native.dll" PackagePath="runtimes/<rid>/native" Pack="true" />`


9 changes: 5 additions & 4 deletions Source/MSBuild.Sdk.Extras/Build/Platforms.targets
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<PropertyGroup Condition="'$(DisableImplicitFrameworkDefines)' != 'true'">
<ImplicitFrameworkIdentifierDefine>$(TargetFrameworkIdentifier.TrimStart('.').Replace('.','_').ToUpperInvariant())</ImplicitFrameworkIdentifierDefine>
<ImplicitFrameworkDefine>$(TargetFramework.Replace('.','_').Replace('-','_').ToUpperInvariant())</ImplicitFrameworkDefine>
<_SdkRuntimeIdentifierDefine Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier.Replace('.','_').Replace('-','_').ToUpperInvariant())</_SdkRuntimeIdentifierDefine>
</PropertyGroup>

<PropertyGroup Condition="'$(PlatformTargets)' == ''">
Expand All @@ -30,13 +31,13 @@
<Import Project="$(PlatformTargets)" Condition="Exists('$(PlatformTargets)')"/>

<PropertyGroup Condition="'$(DisableImplicitFrameworkDefines)' != 'true' AND '$(_SdkLanguageSourceName)' != 'VisualBasic'">
<DefineConstants Condition="'$(DefineConstants)' != '' AND !$(DefineConstants.EndsWith(';'))">$(DefineConstants);</DefineConstants>
<DefineConstants>$(DefineConstants)$(_SdkImplicitFrameworkProfileDefine)</DefineConstants>
<DefineConstants Condition="'$(_SdkImplicitFrameworkProfileDefine)' != ''">$(DefineConstants);$(_SdkImplicitFrameworkProfileDefine)</DefineConstants>
<DefineConstants Condition="'$(_SdkRuntimeIdentifierDefine)' != ''">$(DefineConstants);$(_SdkRuntimeIdentifierDefine)</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(DisableImplicitFrameworkDefines)' != 'true' AND '$(_SdkLanguageSourceName)' == 'VisualBasic'">
<FinalDefineConstants Condition="'$(FinalDefineConstants)' != '' AND !$(DefineConstants.EndsWith(','))">$(FinalDefineConstants),</FinalDefineConstants>
<FinalDefineConstants Condition="'$(_SdkImplicitFrameworkProfileDefine)' != ''">$(FinalDefineConstants)$(_SdkImplicitFrameworkProfileDefine)=-1</FinalDefineConstants>
<FinalDefineConstants Condition="'$(_SdkImplicitFrameworkProfileDefine)' != ''">$(FinalDefineConstants),$(_SdkImplicitFrameworkProfileDefine)=-1</FinalDefineConstants>
<FinalDefineConstants Condition="'$(_SdkRuntimeIdentifierDefine)' != ''">$(FinalDefineConstants),$(_SdkRuntimeIdentifierDefine)=-1</FinalDefineConstants>
</PropertyGroup>

<!-- The conditional compilation symbols defined in the above import and the property-group flows to the Main SDK -->
Expand Down
192 changes: 192 additions & 0 deletions Source/MSBuild.Sdk.Extras/Build/RIDs.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>

<Target Name="_SdkGetRidsPerTargetFramework" Returns="@(_SdkRuntimeId)">

<PropertyGroup>
<ExtrasBuildEachRuntimeIdentifier Condition="'$(ExtrasBuildEachRuntimeIdentifier)' == ''">false</ExtrasBuildEachRuntimeIdentifier>
</PropertyGroup>

<ItemGroup>
<_SdkRuntimeIds Include="$(RuntimeIdentifiers)" Condition="'$(RuntimeIdentifiers)' != '' and '$(ExtrasBuildEachRuntimeIdentifier)' == 'true' " TargetFramework="$(TargetFramework)" />

<_SdkRuntimeId Include="@(_SdkRuntimeIds->'%(TargetFramework)')" Rid="%(_SdkRuntimeIds.Identity)" />
<_SdkRuntimeId Include="$(TargetFramework)" Condition="'$(ExtrasBuildEachRuntimeIdentifier)' == 'false'" />
</ItemGroup>
</Target>



<Target Name="_ComputeTargetFrameworkItems" Returns="@(InnerOutput)">
<ItemGroup>
<_TargetFramework Include="$(TargetFrameworks)" />
</ItemGroup>

<MSBuild Projects="$(MSBuildProjectFile)"
BuildInParallel="$(BuildInParallel)"
Properties="TargetFramework=%(_TargetFramework.Identity)"
Targets="_SdkGetRidsPerTargetFramework">
<Output ItemName="_SdkTargetsWithRids" TaskParameter="TargetOutputs" />
</MSBuild>

<ItemGroup>
<_InnerBuildProjects Include="$(MSBuildProjectFile)">
<AdditionalProperties Condition="'%(_SdkTargetsWithRids.Rid)' != ''" >TargetFramework=%(_SdkTargetsWithRids.Identity);RuntimeIdentifier=%(_SdkTargetsWithRids.Rid)</AdditionalProperties>
<AdditionalProperties Condition="'%(_SdkTargetsWithRids.Rid)' == ''" >TargetFramework=%(_SdkTargetsWithRids.Identity)</AdditionalProperties>
</_InnerBuildProjects>
</ItemGroup>
</Target>



<Target Name="_WalkEachTargetPerFramework">
<MSBuild Projects="$(MSBuildProjectFile)"
BuildInParallel="$(BuildInParallel)"
Properties="TargetFramework=%(_TargetFrameworks.Identity)"
Targets="_SdkGetRidsPerTargetFramework">
<Output ItemName="_SdkTargetsWithRids" TaskParameter="TargetOutputs" />
</MSBuild>

<MSBuild
Condition="'$(IncludeBuildOutput)' == 'true' and '%(_SdkTargetsWithRids.Rid)' != ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_SdkGetBuildOutputFilesWithTfm"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);RuntimeIdentifier=%(_SdkTargetsWithRids.Rid)">

<Output
TaskParameter="TargetOutputs"
ItemName="_BuildOutputInPackageWithRid" />
</MSBuild>

<MSBuild
Condition="'$(IncludeBuildOutput)' == 'true' and '%(_SdkTargetsWithRids.Rid)' == ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_GetBuildOutputFilesWithTfm"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);">

<Output
TaskParameter="TargetOutputs"
ItemName="_BuildOutputInPackage" />
</MSBuild>

<MSBuild
Condition="'$(TargetsForTfmSpecificContentInPackage)' != '' and '%(_SdkTargetsWithRids.Rid)' != ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_GetTfmSpecificContentForPackage"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);RuntimeIdentifier=%(_SdkTargetsWithRids.Rid)">

<Output
TaskParameter="TargetOutputs"
ItemName="_PackageFiles"/>
</MSBuild>

<MSBuild
Condition="'$(TargetsForTfmSpecificContentInPackage)' != '' and '%(_SdkTargetsWithRids.Rid)' == ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_GetTfmSpecificContentForPackage"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);">

<Output
TaskParameter="TargetOutputs"
ItemName="_PackageFiles"/>
</MSBuild>

<MSBuild
Condition="'$(IncludeBuildOutput)' == 'true' and '%(_SdkTargetsWithRids.Rid)' != ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_SdkGetDebugSymbolsWithTfm"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);RuntimeIdentifier=%(_SdkTargetsWithRids.Rid)">

<Output
TaskParameter="TargetOutputs"
ItemName="_TargetPathsToSymbolsWithRid" />
</MSBuild>

<MSBuild
Condition="'$(IncludeBuildOutput)' == 'true' and '%(_SdkTargetsWithRids.Rid)' == ''"
Projects="$(MSBuildProjectFullPath)"
Targets="_GetDebugSymbolsWithTfm"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);">

<Output
TaskParameter="TargetOutputs"
ItemName="_TargetPathsToSymbols" />
</MSBuild>

<MSBuild
Condition="'$(IncludeSource)' == 'true' and '%(_SdkTargetsWithRids.Rid)' == ''"
Projects="$(MSBuildProjectFullPath)"
Targets="SourceFilesProjectOutputGroup"
Properties="TargetFramework=%(_SdkTargetsWithRids.Identity);
BuildProjectReferences=false;">

<Output
TaskParameter="TargetOutputs"
ItemName="_SourceFiles" />
</MSBuild>

<MSBuild
Projects="$(MSBuildProjectFullPath)"
Targets="_GetFrameworkAssemblyReferences"
Properties="TargetFramework=%(_TargetFrameworks.Identity);
BuildProjectReferences=false;">

<Output
TaskParameter="TargetOutputs"
ItemName="_FrameworkAssemblyReferences" />
</MSBuild>

<MSBuild
Projects="$(MSBuildProjectFullPath)"
Targets="_GetFrameworksWithSuppressedDependencies"
Properties="TargetFramework=%(_TargetFrameworks.Identity);
BuildProjectReferences=false;">

<Output
TaskParameter="TargetOutputs"
ItemName="_FrameworksWithSuppressedDependencies" />
</MSBuild>

<ItemGroup>

<!-- Include the runtimes files -->
<None Include="@(_BuildOutputInPackageWithRid)" PackagePath="runtimes/%(_BuildOutputInPackageWithRid.Rid)/lib/%(_BuildOutputInPackageWithRid.TargetFramework)" Pack="true" />
<None Include="@(_TargetPathsToSymbolsWithRid)" PackagePath="runtimes/%(_BuildOutputInPackageWithRid.Rid)/lib/%(_BuildOutputInPackageWithRid.TargetFramework)" Pack="true" />
</ItemGroup>

</Target>


<Target Name="_SdkGetBuildOutputFilesWithTfm" DependsOnTargets="_GetBuildOutputFilesWithTfm" Returns="@(BuildOutputInPackage)">

<ItemGroup>
<BuildOutputInPackage Update="@(BuildOutputInPackage)" Rid="$(RuntimeIdentifier)" />
</ItemGroup>

</Target>

<Target Name="_SdkGetDebugSymbolsWithTfm" DependsOnTargets="_GetDebugSymbolsWithTfm" Returns="@(_TargetPathsToSymbolsWithTfm)">

<ItemGroup>
<_TargetPathsToSymbolsWithTfm Update="@(_TargetPathsToSymbolsWithTfm)" Rid="$(RuntimeIdentifier)" />
</ItemGroup>

</Target>


<Target Name="_ExtrasPackageRuntimeFiles" BeforeTargets="_GetPackageFiles" >

<MSBuild Projects="$(MSBuildProjectFile)"
BuildInParallel="$(BuildInParallel)"
Properties="TargetFramework=%(_TargetFramework.Identity)"
Targets="_SdkGetRidsPerTargetFramework">
<Output ItemName="_SdkTargetsWithRids" TaskParameter="TargetOutputs" />
</MSBuild>


</Target>

</Project>
5 changes: 4 additions & 1 deletion Source/MSBuild.Sdk.Extras/Sdk/Sdk.targets
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
Expand All @@ -14,4 +14,7 @@
<Import Project="$(CustomBeforeSdkTargets)" Condition="'$(CustomBeforeSdkTargets)' != ''"/>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"/>

<!-- Replacing build-in targets -->
<Import Project="$(MSBuildThisFileDirectory)..\Build\RIDs.targets"/>

</Project>
11 changes: 9 additions & 2 deletions Tests/ClasslibraryAsSdk/ClasslibraryAsSdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
<PropertyGroup>
<!--<TargetFramework>uap10.0</TargetFramework>-->
<!--<TargetFrameworks>uap10.0;uap10.0.16278;monoandroid70;xamarin.ios10;xamarin.mac20</TargetFrameworks>-->
<TargetFrameworks>uap10.0;uap10.0.16299;monoandroid70;xamarin.ios10;xamarin.mac20;tizen40</TargetFrameworks>
<IsPackable>false</IsPackable>
<TargetFrameworks>netstandard2.0;net46;uap10.0;uap10.0.16299;monoandroid70;xamarin.ios10;xamarin.mac20;tizen40</TargetFrameworks>
<RuntimeIdentifiers Condition="'$(TargetFramework)' == 'netstandard2.0'">win;unix</RuntimeIdentifiers>
<ExtrasBuildEachRuntimeIdentifier Condition="'$(TargetFramework)' == 'netstandard2.0'">true</ExtrasBuildEachRuntimeIdentifier>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>


<ItemGroup>
<ReferenceAssemblyProjectReference Include="..\ClasslibraryAsSdk.Ref\ClasslibraryAsSdk.Ref.csproj" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'xamarin.mac20'">
<Reference Include="System.Xml" />
</ItemGroup>
Expand Down

0 comments on commit 5865168

Please sign in to comment.