The problem
Actual situation
Unreasonably "fat" executables (due to code redundancy) when deploying multiple single-file executables (e.g. lots of small console based tools) that all depend on a set of shared libraries (part of them coming from nuget packages).
Expected situation
An easy way to exclude entire nuget packages (and their implicit dependencies) from single-file deployment.
Solution proposals
Solution 1
Allow ExcludeFromSingleFile to work on PackageReference similar to how it works on ProjectReference. Also, it should affect all implicit dependencies.
or
Solution 2
An option to generate a single-file (apphost + main assembly + required jsons) without embedding any other referenced assemblies. Then the user may opt-in for embedding specific assemblies via IncludeInSingleFile for a PackageReference and / or ProjectReference. Basically this would be the logical inversion of Solution 1.
Additional context
Just an example regarding my particular use case.
I have several (8 for the moment) small(ish) console based tools (~ 150 KB - 1 MB / program), all depending on a set of shared libraries (~ 10 MB in total). I'm doing single-file framework-independent deployments because I cannot enforce the client to do a system wide install of the .NET framework. The actual deployed package is just an archive with all the tools together, the client just unpacks it and uses the tools. The sad thing is that the useful code for each program is just insignificant noise compared to the actual file size.
Some stats on various publishing options:
-
--self-contained -p:PublishSingleFile=true
Each tool is ~ 70 MB
Everything is ~ 550 MB
-
--self-contained -p:PublishSingleFile=true -p:PublishTrimmed=true
Each tool is ~ 25 MB
Everything is ~ 200 MB
-
--self-contained -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableCompressionInSingleFile=true
Each tool is ~ 20 MB
Everything is ~ 150 MB
-
--self-contained -p:PublishSingleFile=false
Each tool is ~ 150 KB - 1 MB (as desired)
Everything is ~ 80 MB
A lot of files to deploy, a chaotic mix of native shared libraries, assemblies, executables and jsons
In the end I'm using the last variant combined with some hacks. I build each project separately with PublishSingleFile=true so the generated executable won't require json files alongside them, but also don't embedded all shared libraries (i.e. don't use nuget packages, only explicit assembly references, set ExcludeFromSingleFile accordingly etc.). On top of that, adding insult to the injury, I had to do a dirty little hack by patching libhostfxr.so to workaround a bug that - while simple by itself - it was critical for a console program (see dotnet/runtime#64606, many thanks to @vitek-karas for already fixing it).
Is it possible (or could it be made possible in the future) for the self-contained deployments to have all required framework specific files (native and managed libraries) in a sub-directory (e.g. ./CoreLibs), all business specific files (native and managed libraries) in another sub-directory (e.g. ./UserLibs) and keep the base directory (e.g. ./) "clean" with only executables in it?
The problem
Actual situation
Unreasonably "fat" executables (due to code redundancy) when deploying multiple single-file executables (e.g. lots of small console based tools) that all depend on a set of shared libraries (part of them coming from nuget packages).
Expected situation
An easy way to exclude entire nuget packages (and their implicit dependencies) from single-file deployment.
Solution proposals
Solution 1
Allow
ExcludeFromSingleFileto work onPackageReferencesimilar to how it works onProjectReference. Also, it should affect all implicit dependencies.or
Solution 2
An option to generate a single-file (apphost + main assembly + required jsons) without embedding any other referenced assemblies. Then the user may opt-in for embedding specific assemblies via
IncludeInSingleFilefor aPackageReferenceand / orProjectReference. Basically this would be the logical inversion of Solution 1.Additional context
Just an example regarding my particular use case.
I have several (8 for the moment) small(ish) console based tools (~ 150 KB - 1 MB / program), all depending on a set of shared libraries (~ 10 MB in total). I'm doing single-file framework-independent deployments because I cannot enforce the client to do a system wide install of the .NET framework. The actual deployed package is just an archive with all the tools together, the client just unpacks it and uses the tools. The sad thing is that the useful code for each program is just insignificant noise compared to the actual file size.
Some stats on various publishing options:
--self-contained -p:PublishSingleFile=true
Each tool is ~ 70 MB
Everything is ~ 550 MB
--self-contained -p:PublishSingleFile=true -p:PublishTrimmed=true
Each tool is ~ 25 MB
Everything is ~ 200 MB
--self-contained -p:PublishSingleFile=true -p:PublishTrimmed=true -p:EnableCompressionInSingleFile=true
Each tool is ~ 20 MB
Everything is ~ 150 MB
--self-contained -p:PublishSingleFile=false
Each tool is ~ 150 KB - 1 MB (as desired)
Everything is ~ 80 MB
A lot of files to deploy, a chaotic mix of native shared libraries, assemblies, executables and jsons
In the end I'm using the last variant combined with some hacks. I build each project separately with PublishSingleFile=true so the generated executable won't require json files alongside them, but also don't embedded all shared libraries (i.e. don't use nuget packages, only explicit assembly references, set ExcludeFromSingleFile accordingly etc.). On top of that, adding insult to the injury, I had to do a dirty little hack by patching
libhostfxr.soto workaround a bug that - while simple by itself - it was critical for a console program (see dotnet/runtime#64606, many thanks to @vitek-karas for already fixing it).Is it possible (or could it be made possible in the future) for the self-contained deployments to have all required framework specific files (native and managed libraries) in a sub-directory (e.g.
./CoreLibs), all business specific files (native and managed libraries) in another sub-directory (e.g../UserLibs) and keep the base directory (e.g../) "clean" with only executables in it?