TOC
I always assumed that dotnet publish
is nothing more than a combination of dotnet restore
, dotnet build
and copy
. Well as all assumptions I have in my life I’d say I’m wrong and I don’t even know why.
Build servers are my friends
By now I’ve authored at least 10 CI/CD pipelines for different net core projects and debugged pipelines of colleagues a lot. And I really expected that nothing could go wrong for the most basic of the basic pipelines: Do a windows build of my GUI program.
> dotnet restore --runtime win10-x64
> dotnet build {SubProject} --configuration Release --runtime win10-x64 --no-restore
> dotnet publish {SubProject} --configuration Release --runtime win10-x64 --no-restore --no-build --no-self-contained
What do I get? An exe that complains about: Nothing. It just doesn’t start.
So I drop the --no-self-contained
: and it works. But the output 88mb instead of 10mb. Which is not acceptable. Now instead of chaining the commands I use just the publish command and drop the --no-restore
and --no-build
> dotnet publish {SubProject} --configuration Release --runtime win10-x64 --no-self-contained
AND IT WORKS. So either black magic or dotnet publish
without those flags does much more behind the scene than I think it does
Maybe my project is at fault
So let’s try a minimal reproduction:
dotnet version 3.1.403
WhyWonYouWork.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
</Project>
Program.cs
using System;
namespace WhyWontYouWork
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
You cannot go more basic than this.
> dotnet restore --runtime win10-x64
> dotnet build WhyWontYouWork --configuration Release --runtime win10-x64 --no-restore
> dotnet publish WhyWontYouWork --configuration Release --runtime win10-x64 --no-restore --no-build --no-self-contained
> WhyWontYouWork\bin\Release\netcoreapp3.1\win10-x64\publish\WhyWontYouWork.exe
A fatal error was encountered. The library ‘hostpolicy.dll’ required to execute the application was not found in ‘C:\Program Files\dotnet’.
??????????????????
The files in the folder are
31718 Nov 1 23:22 WhyWontYouWork.deps.json
4096 Nov 1 23:22 WhyWontYouWork.dll*
174592 Nov 1 23:22 WhyWontYouWork.exe*
9356 Nov 1 23:22 WhyWontYouWork.pdb
185 Nov 1 23:22 WhyWontYouWork.runtimeconfig.json
So let’s try this:
> dotnet publish WhyWontYouWork --configuration Release --runtime win10-x64 --no-self-contained
> WhyWontYouWork\bin\Release\netcoreapp3.1\win10-x64\publish\WhyWontYouWork.exe
Hello World!
The files in the folder of the working build are
491 Nov 1 23:32 WhyWontYouWork.deps.json
4096 Nov 1 23:32 WhyWontYouWork.dll*
174592 Nov 1 23:32 WhyWontYouWork.exe*
9356 Nov 1 23:32 WhyWontYouWork.pdb
154 Nov 1 23:32 WhyWontYouWork.runtimeconfig.json
The runtimeconfig.json of the failed build looks like this:
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"includedFrameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "3.1.9"
}
]
}
}
vs from the working one:
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
}
}
}
And the failed build deps:
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1/win10-x64",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.1": {},
".NETCoreApp,Version=v3.1/win10-x64": {
"WhyWontYouWork/1.0.0": {
"dependencies": {
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64": "3.1.9"
},
"runtime": {
"WhyWontYouWork.dll": {}
}
},
"runtimepack.Microsoft.NETCore.App.Runtime.win-x64/3.1.9": {
"runtime": {
"Microsoft.CSharp.dll": {
"assemblyVersion": "4.0.5.0",
"fileVersion": "4.700.20.47203"
},
// + 800 lines more dependencies of "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/3.1.9"
Meanwhile the working builds WHOLE deps
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v3.1/win10-x64",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v3.1": {},
".NETCoreApp,Version=v3.1/win10-x64": {
"WhyWontYouWork/1.0.0": {
"runtime": {
"WhyWontYouWork.dll": {}
}
}
}
},
"libraries": {
"WhyWontYouWork/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
Also that missing ‘hostpolicy.dll’ is listed in the long list of dependencies. So WHAT exactly is the one-liner dotnet publish passing to the restore and build that makes the difference ? I tried reading through the dotnet cli code to see if something jumps out but I just don’t see anything that is suspicious.
After googling around for a while I came across this issue: https://github.com/dotnet/runtime/issues/3569 which doesn’t answer my main question at all: WHY THE FUCK ARE THOSE TWO APPROACHES NOT THE SAME.