Example repository of a multi-targeting project build Cake build scripts to build and code-sign both assemblies *and* NuGet packages
This is an example repository on how to target .NET 4.6, .NET 4.7 and UAP 10.0 using a single project.
This project has been written as an example for the blog post about multi-targeting platforms with a single project.
Along the way, this example has been expanded with the following new Cake build scripts:
The scripts are set up in a way that allows a combination of these items as well (e.g. a WPF with an API library) can all be set up. For more information, see the RepositoryTemplate repository.
.\build.ps1 -target package
The scripts are extremely generic. One only has to customize 1 file:
/build.cake
Below is an example script to package a (multi-targeting) component, wpf and uwp app in a single solution:
//=======================================================
// DEFINE PARAMETERS
//=======================================================
// Define the required parameters
var Parameters = new Dictionary<string, object>();
Parameters["SolutionName"] = "Ghk.MultiTargeting";
Parameters["Company"] = "Geert van Horrik";
Parameters["RepositoryUrl"] = string.Format("https://github.com/GeertvanHorrik/{0}", GetBuildServerVariable("SolutionName"));
Parameters["StartYear"] = "2018";
Parameters["UseVisualStudioPrerelease"] = "true";
// Note: the rest of the variables should be coming from the build server,
// see `/deployment/cake/*-variables.cake` for customization options
//
// If required, more variables can be overridden by specifying them via the
// Parameters dictionary, but the build server variables will always override
// them if defined by the build server. For example, to override the code
// sign wild card, add this to build.cake
//
// Parameters["CodeSignWildcard"] = "Orc.EntityFramework";
//=======================================================
// DEFINE COMPONENTS TO BUILD / PACKAGE
//=======================================================
Components.Add("Ghk.MultiTargeting");
Tools.Add("Ghk.MultiTargeting.Tool");
WpfApps.Add("Ghk.MultiTargeting.Example.Wpf");
UwpApps.Add("Ghk.MultiTargeting.Example.Uwp");
TestProjects.Add("Ghk.Multitargeting.Tests");
// Not included:
// DockerImages.Add("Ghk.MultiTargeting.DockerImage");
// WebApps.Add("Ghk.MultiTargeting.WebApp");
// GitHubPages.Add("Ghk.MultiTargeting.GitHubPages");
//=======================================================
// REQUIRED INITIALIZATION, DO NOT CHANGE
//=======================================================
// Now all variables are defined, include the tasks, that
// script will take care of the rest of the magic
#l "./deployment/cake/tasks.cake"
And if you are not using a build server (seriously, you should be), you could choose to customize /deployment/cake/*-variables.cake
as well.
The Cake magic happens in the generic scripts in /deployment/cake/*.cake
Below are a few example targets using exactly the same scripts:
Script | Description |
---|---|
./build.ps1 -Target BuildWpfApps |
Build all WPF apps in the solution |
./build.ps1 -Target Package |
Build & package all components & (WPF & UWP) apps in the solution |
./build.ps1 -Target PackageComponents |
Build & package all components in the solution |
./build.ps1 -Target PackageUwpApps |
Build & package all UWP apps in the solution |
As you can see, there is a pattern:
Note that [SpecificTarget] is optional
Compared to the original multi-targeting example, the following changes were made.
To make sure I could still use the defines we invested in (to maximize code-sharing, we use a lot of #if
in code shared between WPF and UWP). This project creates the following defines:
The project structure adds this line to the project to include the SolutionAssemblyInfo and GlobalSuppressions files which are in the root of the solution.
<Compile Include="..\*.cs" ></Compile>
To allow sharing of code-behind of a view (for example, for custom controls), we need to to a bit of hacking. We create the view inside each platform specific view directory:
After creating the views, one will need to customize the code-behind to add a partial method:
public partial class MyView
{
public MyView()
{
InitializeComponent();
Construct();
}
partial void Construct();
}
Next, create a partial code-behind class inside the regular views folder that contains the shared code:
This view should contain the following code and allows sharing code-behind but have per-platform xaml files:
public partial class MyView
{
partial void Construct()
{
// TODO: Add shared constructor info here
}
}
As an example, a dependency property is added in the shared code-behind that shows how to deal with the different platforms in shared code using defines.