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

Mark PelfMark Pelf
6 min read

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

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 Build Results

Here is a list of build results. Most of my builds succeeded.

NOTE: These project settings all worked well somewhere around .NET 8.0.0. Later, with the upgrade of .NET runtime to later versions of .NET 8.0 and .NET 9.0 and an upgrade to Visual Studio, some of those projects stopped working. It looks like they introduced breaking changes in the build tools. Logic is still sound and build types are the same, just the build tools started to behave a bit differently. A new build configurations and build scripts are needed. 

NOTE: All these build results are with .NET framework version around .NET 8.0.0. With the change in .NET version, file sizes changed (typically smaller). But build types are still the same. It looks like they improved/changed their build tools. 


===Build 1=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-41 files
-13.6MB

===Build 2=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-4 files
-13.6MB

===Build 3-Build that I was not able to make==================================================
Location:
c:\tmpNetBundle\BundleExample01\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. 

===Build 4=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-500 files
-183MB

===Build 5=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-9 files
-167MB

===Build 6=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-9 files
-38.4MB

===Build 7=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-500 files
-183MB

===Build 8=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-9 files
-204MB

===Build 9=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-9 files
-41.7MB
===Build 10=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-41 files
-13.6MB
(flags do not work on this level of build)
===Build 11=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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
-13.6MB
===Build 12=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-500 files
-183MB

===Build 13=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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
-73.2MB

===Build 14=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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
-20.4MB
===Build 15=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-500 files
-183MB

===Build 16=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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
-85.7MB

===Build 17=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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
-22MB
===Build 18=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-44 file
-22.3MB
===Build 19=======================================================================
Location: 
c:\tmpNetBundle\BundleExample01\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:
-10 file
-19.5MB
==============================================================

3 Analyzing Build Results

I will use dotPeek by JetBrains ([5]), a powerful Decompiler to have a look into some of the build results. That product is free, I do not know how they make money from it, but it is free.

4 Build 1 – Framework-dependent, normal build

NOTE: All these build results are with .NET framework version around .NET 8.0.0. With the change in .NET version, file sizes changed (typically smaller). But build types are still the same. It looks like they improved/changed their build tools.

Location:
c:\tmpNetBundle\BundleExample01\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

Build result Content: -41 files, -13.6MB

Here are some screenshots from detPeek decompiler .

Comments:

  • This is a classical build of .NET8 console application

  • You get 2 files, ConsoleApp1.exe and ConsoleApp1.dll, and supporting libraries

  • ConsoleApp1.exe is not an assembly, it is just a loader with an embedded file name of the assembly that is supposed to load

  • ConsoleApp1.dll is the real .NET assembly, that contains app code

5 To be continued

To be continued in the next article of the series.

6 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/

0
Subscribe to my newsletter

Read articles from Mark Pelf directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mark Pelf
Mark Pelf

A Software Engineer from Belgrade, Serbia.