.NET8/9 – Testing different Build/Deployment modes – Part6

.NET8/9 – Testing different Build/Deployment modes – Part6

A practical guide to different Build/Publish/Deployment modes available in .NET8/9

Abstract: We practically show examples of 15 different .NET8/9 build modes, including Framework-Dependent and Framework-Independent, how to bundle an app into a single-file, how to trim app bundles from unused libraries, and how to build an Ahead-of-time (AOT) precompiled app.

1 .NET8/9 toolset supports different build/publish/deployment modes

I was experimenting with different project properties/flags, and I developed several proof-of-concept build projects showcasing different build/publish/deployment modes available in .NET8/9, using Microsoft tools.

1.2 Articles in this series

For technical reasons, I will organize this text into several articles:

  • .NET8/9 – Testing different Build/Deployment modes – Part1
  • .NET8/9 – Testing different Build/Deployment modes – Part2
  • .NET8/9 – Testing different Build/Deployment modes – Part3
  • .NET8/9 – Testing different Build/Deployment modes – Part4
  • .NET8/9 – Testing different Build/Deployment modes – Part5
  • .NET8/9 – Testing different Build/Deployment modes – Part6

2 New build configurations for NET 8.0.11 and NET 9.0.0

New build configurations/scripts for NET_8.0.11/SDK_8.0.404 and NET_9.0.0/SDK_9.0.101 environments are here. Build types and the rationale are as before, just some new build tricks were made. Any better C# developer should be able to understand these build settings.


These are builds in NET_8.0.11/SDK_8.0.404 environment.
All these builds worked also in NET_9.0.0/SDK_9.0.101 environment. 
===Build 1=======================================================================
Location: 
->\ConsoleApp1\bin\Release\net8.0-windows\win-x64\

Publish mode:
1)Release; 2)framework-dependent; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed; 10)NotAot   

Works! App started and it runs properly.

Content:
-47 files
-13.3 MB

-----Here is project file---------------
<!--ConsoleApp1.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="call Framework_SingleFile_Win-X64.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 2=======================================================================
Location: 
->\ConsoleApp1\Framework_SingleFile_Win-X64\

Publish mode:
1)Release; 2)framework-dependent; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed; 10)NotAot   

Works! App started and it runs properly.

Content:
-2 files
-13.3 MB

----Use previous project file and this script-------------------
Framework_SingleFile_Win-X64.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp1.csproj --nologo --no-restore --runtime win-x64 --use-current-runtime  --configuration Release   -p:PublishSingleFile=true --no-self-contained --output ./Framework_SingleFile_Win-X64 

===Build 3-Build that I was not able to make==================================================
Location:
->\ConsoleApp4\Framework_SingleFile_Win-X64_Trimmed\

Publish mode:
1)Release; 2)framework-dependent; 3)Platform-specific-win-x64; 4)Single-file; 5)Trimmed; 6)Not-precompiled; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed  

I was looking into options, but was not able to make "Framework-dependent_Single-file_Trimmed" build.
It looks like Trimmed builds are possible with Microsoft tools in .NET8 only for self-contained (which is opposite to framework-dependent) mode builds.
I was getting this when tried:
Error (active)  NETSDK1102  Optimizing assemblies for size is not supported for the selected publish configuration. Please ensure that you are publishing a self-contained app. 

-----Here is project file---------------
<!--ConsoleApp1.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="Framework_SingleFile_Win-X64_Trimmed.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

----Use previous project file and this script-------------------
Framework_SingleFile_Win-X64_Trimmed.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp4.csproj --nologo --no-restore --runtime win-x64 --use-current-runtime  --configuration Release   -p:PublishSingleFile=true --no-self-contained -p:PublishTrimmed=true --output ./Framework_SingleFile_Win-X64_Trimmed

===Build 4=======================================================================
Location: 
->\ConsoleApp2\bin\Release\net8.0-windows\win-x64\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-255 files
-90 MB

-----Here is project file---------------
<!--ConsoleApp2.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
        <SelfContained>true</SelfContained>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

    <Target Name="RunAfterBuild2" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_Trimmed.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 5=======================================================================
Location: 
->\ConsoleApp2\SelfContained_SingleFile_win-x64\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-2 files
-75 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp2.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=false  -p:PublishTrimmed=false --output ./SelfContained_SingleFile_win-x64

===Build 6=======================================================================
Location: 
->\ConsoleApp2\SelfContained_SingleFile_win-x64_Trimmed\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Trimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-2 files
-12 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_Trimmed.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp2.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=false  -p:PublishTrimmed=true --output ./SelfContained_SingleFile_win-x64_Trimmed

===Build 7=======================================================================
Location: 
->\ConsoleApp3\bin\Release\net8.0-windows\win-x64\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)Not-precompiled; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  
(I think above is correct interpretation. I am not sure because there are many flags/properties in
the project file, but it seems they do not have effect until "dotnet publish" is called)

Works! App started and it runs properly.

Content:
-255 files
-90 MB

-----Here is project file---------------
<!--ConsoleApp3.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
        <SelfContained>true</SelfContained>
        <PublishReadyToRun>true</PublishReadyToRun>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_ReadyToRun.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

    <Target Name="RunAfterBuild2" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 8=======================================================================
Location: 
->\ConsoleApp3\SelfContained_SingleFile_win-x64_ReadyToRun\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)ReadyToRun; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-2 files
-108 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_ReadyToRun.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp3.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishTrimmed=false -p:PublishReadyToRun=true   --output ./SelfContained_SingleFile_win-x64_ReadyToRun

===Build 9=======================================================================
Location: 
->\ConsoleApp3\SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Trimmed; 6)ReadyToRun; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-2 files
-15 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp3.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=true  -p:PublishTrimmed=true --output ./SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun

===Build 10=======================================================================
Location: 
->\ConsoleApp1C\bin\Release\net8.0-windows\win-x64\

Publish mode:
1)Release; 2)framework-dependent; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile ; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-47 files
-13 MB
(flags do not work on this level of build)

-----Here is project file---------------
<!--ConsoleApp1C.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
        <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract> 
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="Framework_SingleFile_Win-X64.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 11=======================================================================
Location: 
->\ConsoleApp1C\Framework_SingleFile_Win-X64\

Publish mode:
1)Release; 2)framework-dependent; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesInSingleFile ; 9)assembliesNotCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-1 file
-14 MB

----Use previous project file and this script-------------------
Framework_SingleFile_Win-X64.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp1C.csproj --nologo --no-restore --runtime win-x64 --use-current-runtime  --configuration Release   -p:PublishSingleFile=true --no-self-contained --output ./Framework_SingleFile_Win-X64 

===Build 12=======================================================================
Location: 
->\ConsoleApp2C\bin\Release\net8.0-windows\win-x64\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesInSingleFile; 9)assembliesCompressed ; 10)NotAot   

Works! App started and it runs properly.

Content:
-255 files
-90 MB

-----Here is project file---------------
<!--ConsoleApp2C.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
        <SelfContained>true</SelfContained>
        <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
        <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

    <Target Name="RunAfterBuild2" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_Trimmed.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 13=======================================================================
Location: 
->\ConsoleApp2C\SelfContained_SingleFile_win-x64\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesInSingleFile; 9)assembliesCompressed; 10)NotAot   

Works! App started and it runs properly.

Content:
-1 file
-39 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp2C.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=false  -p:PublishTrimmed=false --output ./SelfContained_SingleFile_win-x64

===Build 14=======================================================================
Location: 
->\ConsoleApp2C\SelfContained_SingleFile_win-x64_Trimmed\

Publish mode:
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Trimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesInSingleFile; 9)assembliesCompressed ; 10)NotAot    

Works! App started and it runs properly.

Content:
-1 file
-10 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_Trimmed.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp2C.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=false  -p:PublishTrimmed=true --output ./SelfContained_SingleFile_win-x64_Trimmed

===Build 15=======================================================================
Location: 
->\ConsoleApp3C\bin\Release\net8.0-windows\win-x64\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed ; 10)NotAot  
(I think above is correct interpretation. I am not sure because there are many flags/properties in
the project file, but it seems they do not have effect until "dotnet publish" is called)

Works! App started and it runs properly.

Content:
-255 files
-90 MB

-----Here is project file---------------
<!--ConsoleApp3C.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <DebugType>embedded</DebugType>
        <PublishSingleFile>true</PublishSingleFile>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
        <SelfContained>true</SelfContained>
        <PublishReadyToRun>true</PublishReadyToRun>
        <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
        <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_ReadyToRun.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

    <Target Name="RunAfterBuild2" AfterTargets="Build">
        <Exec Command="call SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 16=======================================================================
Location: 
->\ConsoleApp3C\SelfContained_SingleFile_win-x64_ReadyToRun\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Untrimmed; 6)ReadyToRun; 7)PdbEmbeded; 8)binariesInSingleFile; 9)assembliesCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-1 file
-51 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_ReadyToRun.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp3C.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishTrimmed=false -p:PublishReadyToRun=true   --output ./SelfContained_SingleFile_win-x64_ReadyToRun

===Build 17=======================================================================
Location: 
->\ConsoleApp3C\SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Single-file; 5)Trimmed; 6)ReadyToRun; 7)PdbEmbeded; 8)binariesInSingleFile; 9)assembliesCompressed ; 10)NotAot  

Works! App started and it runs properly.

Content:
-1 file
-12 MB

----Use previous project file and this script-------------------
SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp3C.csproj --nologo --no-restore --runtime win-x64 --configuration Release   -p:PublishSingleFile=true -p:SelfContained=true -p:PublishReadyToRun=true  -p:PublishTrimmed=true --output ./SelfContained_SingleFile_win-x64_Trimmed_ReadyToRun

===Build 18=======================================================================
Location: 
->\ConsoleApp5\bin\Release\net8.0-windows\win-x64\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Untrimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed; 10)PublishAot 

Works! App started and it runs properly.

Content:
-228 file
-90 MB

-----Here is project file---------------
<!--ConsoleApp5.csproj +++++++++++++++++++++++++++++++++++++-->
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0-windows</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <PlatformTarget>x64</PlatformTarget>
        <Platforms>AnyCPU;x64</Platforms>
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
        <!--<DebugType>embedded</DebugType>
        <PublishTrimmed>true</PublishTrimmed>
        <IsTrimmable>true</IsTrimmable>
        <SelfContained>true</SelfContained>
        <IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
        <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
        <PublishSingleFile>true</PublishSingleFile>-->
        <PublishAot>true</PublishAot>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\ClassLibrary1\ClassLibraryA.csproj" />
    </ItemGroup>

    <Target Name="RunAfterBuild1" AfterTargets="Build">
        <Exec Command="win-x64_Trimmed_AOT.cmd" Condition=" '$(BuildingInsideVisualStudio)' == 'true' "/>
    </Target>

</Project>

===Build 19=======================================================================
Location: 
->\ConsoleApp5\win-x64_Trimmed_AOT\

Publish mode :
1)Release; 2)self-contained; 3)Platform-specific-win-x64; 4)Un-bundled; 5)Trimmed; 6)NotR2R; 7)PdbEmbeded; 8)binariesNotInSingleFile; 9)assembliesNotCompressed; 10)PublishAot 

Works! App started and it runs properly.

Content:
-3 file
-9 MB

----Use previous project file and this script-------------------
win-x64_Trimmed_AOT.cmd
echo .NET SDK version:
dotnet --version
dotnet publish ConsoleApp5.csproj --nologo --no-restore --runtime win-x64 --configuration Release  -p:SelfContained=true  -p:PublishTrimmed=true --output ./win-x64_Trimmed_AOT

==============================================================

3 Code on GitHub

I am putting example build projects on GitHub [99]. There are 3 solutions:

  • _BundleExample01_NET_8.0.0_SDK8.0.100.sln (read comments, this is broken in .NET 8.0.11)
  • _BundleExample01_NET_8.0.11_SDK8.0.404.sln (locked to SDK_8.0.404)
  • _BundleExample01_NET_9.0.0_SDK9.0.101.sln (locked to SDK_9.0.101)

4 Conclusion

In this series of articles , we showed 15 different ways to build a .NET8/9 application, depending on the options wanted. There are probably more options or combinations of build properties/flags, but we can call this proof-of-concept testing successful and broad enough. We got most of the build/publish options working properly, according to the documentation.

I was in these articles aiming for some “intermediate level of practical skills” that a modern C#/.NET developer should have. Of course, this is a very interesting topic for every software engineer to get familiar with.

It was a bit of a surprise to discover that build settings are a bit of a “moving target”, and that build tools behavior changed between .NET 8.0.0 and .NET 8.0.11 and that I needed to change my build scripts accordingly.

One very interesting technology offered as a part of .NET8/9 environment is “ReadyToRun” build option, which is a form of ahead-of-time (AOT) compilation. More details can be found at [7]. Looks to me like a complicated and serious topic, to really understand how it works from the inside. But I do not see the point of really studying it in detail unless you work in Microsoft in their .NET runtime/core department. An average C#/.NET engineer just needs to know how to activate properly the build options and benefits of it.

We also showed how to do a native Ahead-Of-Time compilation (AOT) deployment model. That can be attractive in some cases too.

4.1 Full Understanding of Build Flags Hardly Reachable

My general feeling is that a full understanding of all the build options in C# is hardly reachable. What you have are “self-explanatory” names of build flags for C# project settings and vague textual explanations in the online Microsoft documentation. Those “self-explanatory” names of build flags can sometimes be misleading, and vague online documentation a bit obscures what exactly is happening behind during the build process.

For example, an average C#/ASP.NET developer can not just focus on build options, there are other things to do, like CSS, JS, SQL, so spending too much time just on C# build options is not possible.

I grew up as a developer in C/C++ environment 25 years ago, and then I really had a full understanding of compiling and link processes during build, and of many build flags I was setting on and off. Now I feel that the build process in C# is a bit vague to me, as to what exactly are consequences of particular build flags that I am flipping on and off.

It looks to me that sometimes some of the build flags are just “general guidelines” of what the build result will be and the full interpretation of some of those build flags is left to the discretion of the Microsoft .NET team. On average C# developer is just to set appropriate build flags and check that the build result works/executes and that is it. Otherwise, how to explain that for some build configuration in .NET8.0.0 you get as a result 2 files, and in then in .NET8.0.11 for the same build options you get 4 files. Or for another build configuration in .NET8.0.0 you get 500 files, and .NET8.0.11 for the same build options you get 250 files.

ReadyToRun (R2R) build option is a bit of a mystery to me. I was reading the article [7] and it left me a bit puzzled. Here are some things they said: 1) …file size is larger with it as it contains the native code in addition to the IL… 2) … supports a broad range of options with regard to partial vs. full compilation… 3)…Startup performance is better with R2R… 4) code is ready to run right away without jitting… 5) …R2R compiled code typically runs a bit slower than JIT compiled code… It also seems possible that they change the internal structure of R2R files in different .NET versions.

Obviously, mmany concepts in R2R look different from what we were used to having in C/C++ environment build process. An average C#/ASP.NET developer will just flip that flag on, and get the build running, and only those Microsoft guys know what exactly is being packaged in those build artifacts and what is being partially or fully compiled.

A bit of a problem for an average C#/ASP.NET developer is that without a good understanding what are exact consequences of particular build variants, one can not make a really good decision as to which one is the most appropriate for his/her particular app situation. I think the best thing is just to follow Microsoft's general guidelines as to which build options to use in which scenario and complement that with a bit of your own trial-and-error experiments.

5 References

[1] .NET application publishing overview
https://learn.microsoft.com/en-us/dotnet/core/deploying/

[2] Self-contained deployment runtime roll forward
https://learn.microsoft.com/en-us/dotnet/core/deploying/runtime-patch-selection

[3] https://www.red-gate.com/products/smartassembly/

[4] https://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli#api-incompatibility

[5] https://www.jetbrains.com/decompiler/

[7] https://devblogs.microsoft.com/dotnet/conversation-about-ready-to-run/

[99] https://github.com/MarkPelf/ArticlesCode , folder 86_Net8BuildModes_Part6

Leave a Comment