Published on: 23 October 2020
Author: Ramesh Kanjinghat
With dotnet core 3.0 and later versions we have the option to deploy executables as self-contained. Along with the executable the folder contains all the dependencies including the runtime. This deployment mode is not without any challenges.
The first challenge is the publish folder is cluttered with hundreds of files. This grows as we add more dependencies to the project. It will be much easier if users don’t have to search for the correct executable from these hundreds of files. There are alternate ways like a shortcut or a powershell script that runs correct exe etc.
The second challenge is the size. Not all applications need all the runtime assemblies. We could significantly reduce the publish folder size if we can get rid of the unused files.
Self-contained deployment model along with File Publish Options, Produce single file and Trim unused files can produce a much trimmed single executable files.
Please check my blog, https://dhrutara.net/2020/05/17/dotnet-core-deployment-models/, to learn more about the deployment models and File Publish Options.
Warp is written in Rust so, Hubert Rybak created a wrapper around wrap called https://github.com/Hubert-Rybak/dotnet-warp/graphs/contributors
I have tested dotnet-warp on few of my internal projects and it definitely produces trimmer executable than the built-in one.
In the below image you will see that the executable produced by dotnet-warp is 5MB lighter than built-in produced executable. In this case it might not be much of a gain but I have noticed that as the dependencies grow the difference tend to increase.
dotnet-warp
dotnet-warp is a dotnet tool. That means we can install and use dotnet-warp as any other dotnet tools.
How to install dotnet-warp?
Simple, just run
dotnet tool install --global dotnet-warp --version 1.1.0
This installs the tool globally,
Globally doesn’t mean at machine level instead it is at user level. Once installed globally the tool can be executed by that specific user without giving the whole exe path. To install it locally remove the flag –global
Arguments
To make it easy to follow I am going to take help of a sample dotnet core console application.
Application details
- Let’s call it Dhrutara.DotnetWarp.
- I am using Visual Studio 2019
- Runtime is dotnet core 3.1
- Added NewtonSoft.Json as dependency but I am not going to use it.
The only required argument for dotnet-warp is the project file. This could be either relative or absolute.
So, let’s begin with the only required argument and see what happens
- Open either windows Powershell or command prompt.
- Navigate to the project folder.
- Run below command.
1dotnet-warp ".\Dhrutara.DotnetWarp.csproj"
Navigating to project folder is an optional step. If not in the project folder then we have to just give the whole file path.
You can see that the executable is created in the same folder as where the project file resides. That is because dotnet-warp creates the executable in the same location where the command is ran.
If you want to deploy it to a different location then you can pass the target folder using “–output” option.
1dotnet-warp ".\Dhrutara.DotnetWarp.csproj" --output ".\publish\Dhrutara.DotnetWarp.exe"
executable file full name, including extension. dotnet-warp internally uses the default dotnet core compiler and hence it also allows us to pass additional arguments to the compiler. The format to pass compiler options is
1-p:<argument name>=<argument value>
By default dotnet-warp compiles in release mode. let us assume we want to compile Dhrutara.DotnetWarp in debug mode and also want to mention the version of the executable as 1.2.1. Below command can be used to achieve this.
1dotnet-warp ".\Dhrutara.DotnetWarp.csproj" --output ".\publish\Dhrutara.DotnetWarp.exe" -p:configuration="debug" -p:Version="1.2.1"
How to use dotnet-warp with Azure Devops CI/CD pipeline?
Well, we just learnt that. We just have to add a powershell task in the deployment stage abs run the commands we have been testing.
1- task: PowerShell@2 2displayName: 'Warp it' 3inputs: 4targetType: 'inline' 5script: | 6New-Item -Path "$(deployFolder)" -ItemType Directory 7dotnet tool update dotnet-warp --global 8dotnet-warp "$(Build.SourcesDirectory)\Dhrutara.DotnetWarp\Dhrutara.DotnetWarp.csproj" --output "$(deployFolder)\Dhrutara.DotnetWarp.exe" --verbose -p:configuration="${{parameters.buildConfiguration}}" -p:runtime="${{parameters.runtimeIdentifier}}"
You can see there are 3 commands, line numbers 6,7 and 8, in the above script.
Step 1: dotnet-warp won’t create the folder if it doesn’t exist. So, I have a command to create the target folder.
Step 2: Then update command to install/update dotnet-warp globally.
dotnet install command fails if the tool is already installed. dotnet update command on the other hand installs the tool if missing, updates if the tool exists and a later version is available and does nothing if the version is latest.
Step 3: Run dotnet-warp command to produce the trimmed, self-contained and single executable file.
Tidbits
- If you have used dotnet tools before you might have noticed that I am calling dotnet-warp directly without the main dotnet command. The usual way is dotnet dotnet-warp but I call it directly like dotnet-warp. Azure Pipeline throws “Sequence contains more than one matching element” exception when try to call tools preceded with dotnet command. you can read more about this here. After some trial and errors i was able to run the command the way i have given below. If this approach ever fails in future then you can replace dotnet-warp with dotnet dotnet-warp in Step 3
- For the whole sample code, including the pipeline, check the github repository, https://github.com/dhrutara-net/Dhrutara.DotnetWarp.