Creating a cross platform package – Part 1

With Optimizely’s transition to .NET5 last year, developers of add-on packages will need to follow suit.

The complexity of delivering a package that supports both frameworks will vary depending on the type of package you are trying to migrate. For example, if you have delivered an admin module based on webforms you will need to re-write this so that it is accessed via the main navigation. In this case, it is probably best to use this in the .Net Framework version as well. In short, you will need to really consider how you refactor your module to support both environments.

This multi-part blog post will take you through the process, with part 1 focusing on converting your existing project to use the new project format and part 2 focusing on how to modify the code and project to support multiple targets.

Migrate to the new project format

The easiest way to do this is probably to create a new project and then bring your code across.

Set the correct framework version

<TargetFramework>net5.0</TargetFramework>

When the project is created it will target either net5.0 or net6.0. This needs to be changed to match the framework version of the original project, i.e. net471.

<TargetFramework>net471</TargetFramework>

Move nuget package references

The nuget package references are no longer managed in the ‘packages.config’ file, they are now part of the project file.

It is straightforward to migrate the references across; ‘packages‘ becomes ‘ItemGroup‘ and ‘package‘ becomes ‘PackageReference

<ItemGroup>
    <PackageReference Include="EPiServer.Framework" Version="[11.1.0,12)" />
    <PackageReference Include="EPiServer.Framework.AspNet" Version="[11.1.0,12)" />
    <PackageReference Include="EPiServer.CMS.UI.Core" Version="[11.1.0,12)" />
</ItemGroup>

Remove nuspec file

Again this information is included in the project file. For the most part, transitioning to the new format is relatively straightforward with similar approaches, but some areas (such as adding files created during the build) have to be done differently.

Metadata

This is the information about the nuget package.

<?xml version="1.0" encoding="utf-8"?> 
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">    				
   <metadata> 
        <!-- Required elements--> 
	<id></id> 
	<version></version> 
	<description></description> 
	<authors></authors> 
	<!-- Optional elements --> 
	<!-- ... --> 
    </metadata> 
    <!-- Optional 'files' node --> 
</package>

changes to

<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <!-- Required elements--> 
    <PackageId></PackageId>
    <Version></Version>
    <Authors></Authors>
    <Description></Description>
    <!-- Optional elements -->
    <RepositoryUrl></RepositoryUrl>
    <Title></Title>
    <Tags></Tags>
    <ReleaseNotes></ReleaseNotes>
  </PropertyGroup>
</Project>

Content Files

You may need to include additional, or remove files from the nuget package. This was handled in the nuspec file with <files> and <contentFiles> nodes.

<files>
    <file src="bin\Debug\*.dll" target="lib" exclude="*.txt" />
</files>

<contentFiles>
     <!-- Include everything in the scripts folder except exe files -->
     <files include="cs/net45/scripts/*" exclude="**/*.exe"  
            buildAction="None" copyToOutput="true" />
</contentFiles>

changes to

<ItemGroup>
  <Content Remove="src\**" />
  <Content Remove="node_modules\**" />
  <Content Remove="*.json" /> 

  <Content Include="deploy\**" Exclude="src\**\*">
    <Pack>true</Pack>
    <PackagePath>content</PackagePath>
    <PackageCopyToOutput>true</PackageCopyToOutput>
  </Content>
</ItemGroup>

NOTE: If you want to include files that are created during the build process then you need to take a different approach. i.e. a separate front-end build process whose output needs to be included.

You will need to create a target file, and reference this in your project. The targets file should be named the same as the built project.

<Content Include="build\net461\<project-name>.targets" PackagePath="build\net461\<project-name>.targets" />

<project-name>.target

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
    <ItemGroup>
        <SourceScripts Include="$(MSBuildThisFileDirectory)..\..\contentFiles\any\any\modules\_protected\**\*"/>
    </ItemGroup>

    <Target Name="CopyFiles" BeforeTargets="Build">
        <Copy
            SourceFiles="@(SourceScripts)"
            DestinationFolder="$(MSBuildProjectDirectory)\modules\_protected\%(RecursiveDir)"
        />
    </Target>
</Project>

Addtional project settings

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>

When set to true will automatically create the nuget file when the project is built.

<AddRazorSupportForMvc>true</AddRazorSupportForMvc>

Is required when the project includes Razer files.

<RestoreSources>
  https://api.nuget.org/v3/index.json;
  https://nuget.optimizely.com/feed/packages.svc;
</RestoreSources>

Can be used to set the location of the package sources; these can be either external or from the local file system.

Build and Test

Build the project and resolve any issues you encounter, these should be minor.

Once the package is generated you should test to ensure that it contains the correct content.

Wrapping things up

At this point, you should have a solution that builds your project and creates a nuget package but still targets a single framework.

In the next part, I will cover how to convert to target multiple frameworks.

1 thought on “Creating a cross platform package – Part 1

  1. Pingback: Creating a cross platform package – Part 2 | jhoose

Leave a Reply