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:
- Build multi-targeting class libraries (components)
- Build WPF apps
- Build UWP apps
- Build Chocolatey tools
- Build GitHub pages
- Build web apps (Octopus Deploy and Azure Web Apps)
- Build docker images
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.
- Open powershell with the repository as working directory
.\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:
- Build[SpecificTarget]
- Package[SpecificTarget]
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:
- .NET 4.6 => NET; NET46
- .NET 4.7 => NET; NET47
- UAP 10.0 => UAP; NETFX_CORE
- .NET Standard 2.0 => NS; NS20; NETSTANDARD; NETSTANDARD2_0
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" />
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:
- Platforms\net\Views\MyView.xaml (+ .cs)
- Platforms\uap10.0\Views\MyView.xaml (+ .cs)
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:
- Views\MyView.xaml.cs
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.
- Multi-targeting the world: a single project to rule them all (Oren Novotny - January 4, 2017)