Runtime: Reduce size of PublishSingleFile binary

Created on 5 May 2019  Â·  41Comments  Â·  Source: dotnet/runtime

With C++ you can make a single file executable:

~~~c
$ cat aaaaa.cpp

include

int main() {
std::cout << "bbbbb ccccc" << std::endl;
}

$ x86_64-w64-mingw32-g++ -s -static -std=c++14 -o aaaaa.exe aaaaa.cpp
$ ./aaaaa.exe
bbbbb ccccc
~~~

with a decent size:

~
$ wc -c aaaaa.exe
868864 aaaaa.exe
~

with C# you can make a single file executable:

~sh
$ dotnet new console -o aaaaa
$ dotnet publish -r win-x64 /p:PublishSingleFile=true aaaaa
$ cd aaaaa/bin/Debug/netcoreapp3.0/win-x64/publish
$ ./aaaaa.exe
Hello World!
~

but the size is 80 times larger:

~
$ wc -c aaaaa.exe
70272264 aaaaa.exe
~

it seems that DLLs are being loaded that dont need to be. Using these commands:

~sh
cd aaaaa/bin/Debug/netcoreapp3.0/win-x64
ldd aaaaa.exe |
awk '{print $1}' |
while read each
do
if [ -f "$each" ]
then
echo "$each"
fi
done |
xargs wc -c aaaaa.exe
~

Result:

~
337408 aaaaa.exe
18696 api-ms-win-core-file-l1-2-0.dll
18696 api-ms-win-core-file-l2-1-0.dll
21256 api-ms-win-core-localization-l1-2-0.dll
19208 api-ms-win-core-processthreads-l1-1-1.dll
19208 api-ms-win-core-synch-l1-2-0.dll
19208 api-ms-win-core-timezone-l1-1-0.dll
22792 api-ms-win-crt-convert-l1-1-0.dll
20744 api-ms-win-crt-filesystem-l1-1-0.dll
19720 api-ms-win-crt-heap-l1-1-0.dll
19208 api-ms-win-crt-locale-l1-1-0.dll
27912 api-ms-win-crt-math-l1-1-0.dll
23304 api-ms-win-crt-runtime-l1-1-0.dll
24840 api-ms-win-crt-stdio-l1-1-0.dll
24840 api-ms-win-crt-string-l1-1-0.dll
21256 api-ms-win-crt-time-l1-1-0.dll
1016584 ucrtbase.DLL
1674880 total
~

So if we only use required DLL the size is 40 times smaller. Does
dotnet publish have a way to "strip" the executable, or only include needed
DLL?

area-AssemblyLoader-coreclr

Most helpful comment

@cup @Symbai @tomrus88

In the upcoming Preview 6, we plan to integrate the ILLinker into the .NET core SDK. Once this is ready, you'll be able to use the linker and single-exe options seamlessly, as in:

dotnet publish -r <rid> /p:PublishTrimmed=true /p:PublishSingleFile=true

This will generate a self-contained executable for the app, with unnecessary native and managed DLLs removed. There are things we can do to further reduce the binary size, but I think this will largely address the concerns you've raised above.

@sbomer can you please provide the file-count and single-file size for HelloWorld app when using the linker with native-file trimming? Thanks.

All 41 comments

@cup What about the DLLs mentioned above indicate they aren't needed? Those API sets are native dependencies for the VM itself and have nothing to do with the managed application. The api-ms-win-crt-stdio-l1-1-0.dll is most definitely going to be needed if like the C++ example a string is being written to stdout. The other aspect of this is the ability for the single binary to be run on Windows version that don't have the necessary API sets (e.g. Windows 7). Basically those files are definitely needed by the VM in most scenarios.

@AaronRobinsonMSFT it seems you wholly misunderstood my post.

the DLLs i listed are ones that are required, as you said.

its the other 200 or so that arent, or so it would appear.

so to repeat, is there a way to only included needed DLLs in the executable?

@cup Ah. Sorry about that, I see the ldd command now. Sorry for the confusion, but the response is generally the same. The VM doesn't know what the application is going to do _or_ on which Windows platform it may be executed on. I agree that in this example it would be generally simple to reduce the set, but this is the trivial case. However, what about applications that consume plug-ins or if additional diagnostic knobs (e.g. ComPlus_*) are set for debugging a crash in the future?

This isn't to say the developer can't declare "trust me, these are all the binaries I will need", but that would require a rather large leap of faith for anyone, even one of the runtime developers, to be confident the VM isn't going to need one of them in some future scenario.

The issue about size for single-exe is definitely a concern and reducing that is something we are working on. I am just unsure if asking the developer to provide a list of what they believe are needed is a path for success in anything more complicated than a "hello world" example.

cc @swaroop-sridhar @jeffschwMSFT

@AaronRobinsonMSFT why would we need to "ask the developer" anything?

with C and C++, you just compile with -static and any needed libraries are
statically linked at compile time.

is something like that orthogonal to the C# build system? does .NET Core really
have no way to detect what libraries are actually used in a program and link
only those?

@cup Are we talking about native libraries or managed libraries here?

For managed libraries there is definitely a way to reduce the size. Details about the tooling can be found at https://github.com/dotnet/core/blob/master/samples/linker-instructions.md. This is apart of the work I referred to above.

For native libraries it is a bit more complicated. In particular on Windows one of the reasons has to do with downlevel support through API-sets as well as delay loading of other binaries that may or may not be needed. The thought being there is no reason to sacrifice process memory if the binary is never used so it may be delay loaded to reduce the runtime memory footprint.

Just add --self-contained false. I get 335KB for a console hello world.

Currently the default for publish (carryover from 2.* days) is to create self-contained applications. This means the entire runtime and all of the framework is included in the app.

If you use --self-contained false the app is built as framework-dependent. It means it requires the runtime and the framework to be installed on the machine. On the other hand it can be really small.

@vitek-karas isn't whole point of single file distribution is that it doesn't have any external dependencies? Be it runtime, framework or any other dependency...

@tomrus88 one of the use cases for single-file is definitely the "no dependency" distribution you mention. In that case, as of now .NET Core doesn't have a solution which would provide "small" package size. Projects like ILLinker should help, right now though the size improvements it offers are nowhere near good enough to make the apps comparable in size to the C++ sample above.

There are other use cases though. One such case is where it's OK to require globally installed runtime, but the desire is to have the application itself as a single executable. For example for ease of deployment. In these cases the above mentioned setting can get the application size to really small numbers.

@AaronRobinsonMSFT

The VM doesn't know what the application is going to do _or_ on which Windows platform it may be executed on.

It can know that, if I specify RID of e.g. win10-x64 during publish. I was surprised that the size of a self-contained single-file binary for win-x64 is exactly the same as for win10-x64. Isn't that something that could be improved?

Isn't that something that could be improved?

@svick Absolutely it can be improved. I didn't realize we have that specific of a RID. If the option exists to specify win or win10 then we could definitely reduce that scenario much easier - good catch.

A real fix would be something like C or C++ where only the used libraries are
statically linked, perhap automatically or with switch like -lhostfxr.

@cup That is what the self contained basically provides to you in a final binary - except there is no link time code gen that can reduce the native footprint due to all the issues I have mentioned above. A managed solution does exist - https://github.com/dotnet/core/blob/master/samples/linker-instructions.md. As far as making the managed linker just work that is definitely what we are working toward. However. as I mentioned above the native solution is not as simple as is assumed here for all the reasons I have previously mentioned.

@AaronRobinsonMSFT I was unable to use ILLink.Task as well, but got completely different errors.

f:\Dev\testapp>dotnet --version
3.0.100-preview4-011223
dotnet new console -o testapp
cd testapp
dotnet new nuget
edit nuget.config as described in docs
dotnet add package ILLink.Tasks -v 0.1.6-prerelease.19253.1
dotnet publish -c release -r win-x64 -o out

Got following errors

C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018: The "ILLink" task failed unexpectedly. [f:\Dev\testapp\testapp.csproj]
C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018: System.NullReferenceException: Object reference not set to an instance of an object. [f:\Dev\testapp\testapp.csproj]
C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018:    at ILLink.Tasks.ILLink.GenerateCommandLineCommands() [f:\Dev\testapp\testapp.csproj]
C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018:    at Microsoft.Build.Utilities.ToolTask.Execute() [f:\Dev\testapp\testapp.csproj]
C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018:    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute() [f:\Dev\testapp\testapp.csproj]
C:\Users\TOM_RUS\.nuget\packages\illink.tasks\0.1.6-prerelease.19253.1\build\ILLink.Tasks.targets(247,5): error MSB4018:    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) [f:\Dev\testapp\testapp.csproj]

@cup I am not an owner nor contributor to the ILLink tool. Both the coreclr and corefx repos consume ILLink and we use it to reduce assembly footprint in various scenarios. The instructions aside, I was pointing out the existence of a tool that does what is being requested but only for managed assemblies. It is also the tool we will be using longer term to address the size issue in single file scenarios with respect to managed assemblies.

@cup and @tomrus88 If you are hitting issues there is a feedback section to request help. Based on the documentation in those instructions it appears they are for 2.0 at present so probably need to be updated. Definitely worth filing an issue on that. I would suggest adding a /bl to the build command and examine the .binlog for exactly what is failing. It is possible the error is simple or could be a blocking issue for consuming those targets for 3.0. External support and integration is something that is being considered for the 3.0 timeframe though.

Removing unnecessary dlls from self contained applications is much needed. Without single file it throws more than 250(!) dlls in the output directory which is a mess. The mono linker isn't working most if the time especially with WPF or Winforms applications and last time I've checked it didn't even worked for .NET Core 3 at all.

I think there should be a native / inbuilt solution that self contained only copies the required dlls. It should work like all the native compilers. Copying the whole framework was acceptable for .NET Core 2 and previous where applications were meant to be server applications. Now that .NET Core 3 also supports end user desktop applications you cannot share a folder with over 250 files or binaries with over 70mb where the actual application code is less than 50 lines of code.

cc @swaroop-sridhar

@cup @Symbai @tomrus88

In the upcoming Preview 6, we plan to integrate the ILLinker into the .NET core SDK. Once this is ready, you'll be able to use the linker and single-exe options seamlessly, as in:

dotnet publish -r <rid> /p:PublishTrimmed=true /p:PublishSingleFile=true

This will generate a self-contained executable for the app, with unnecessary native and managed DLLs removed. There are things we can do to further reduce the binary size, but I think this will largely address the concerns you've raised above.

@sbomer can you please provide the file-count and single-file size for HelloWorld app when using the linker with native-file trimming? Thanks.

@swaroop-sridhar I've tried daily build of .net core and got following results using single file publish:
Console App 68.4 MB without trimming, 28.2 MB with trimming
WinForms App 136 MB without trimming, 51.8 MB with trimming
WPF App 136 MB without trimming, fails to publish with trimming, build log attached
msbuild.zip
image

Thanks @tomrus88 for giving the mono/linker a try and publishing the sizes. The gap with WPF is an issue that we are tracking. One potential cause of the issue is that we loose the assembly roots defined in xaml, there are others. We hope to have changes to have this work more often in an upcoming preview.

PublishTrimmed option now failing on all project types (Console/WinForms/WPF) as of latest daily .NET Core build (I have 3.0.100-preview6-012077 installed atm). Probably has something to do with this https://github.com/mono/linker/pull/567 being merged.

@tomrus88 thank you for letting us know - I'm taking a look now. What is the failure you're seeing?

@tomrus88 thank you for letting us know - I'm taking a look now. What is the failure you're seeing?

Looks like you already found out what's wrong :)

So now trimming works again, and there's latest results based on 3.0.100-preview6-012099 daily build:
Console App 67.7 MB without trimming (got smaller), 32.3 MB with trimming (got bigger)
WinForms App 165 MB without trimming (got bigger), 65.0 MB with trimming (got bigger)
WPF App 165 MB without trimming (got bigger), 65.0 MB with trimming

I also find it interesting, that without using single file publish, WinForms App and WPF App is only 149MB, where does those extra 16MB come from?

Also there's 2 pairs of identical files in publish folder when not using single file publish, not sure why we need those:
mscordaccore.dll = mscordaccore_amd64_amd64_4.6.27724.71.dll
sos.dll = sos_amd64_amd64_4.6.27724.71.dll

@tomrus88 I appreciate the interest and the numbers you've shared, thanks! The numbers for trimming are different because we've been changing the default settings to be more conservative (which will make it tend to work better for apps/frameworks that use reflection - like WPF, but won't be able to remove as much code).

About the extra 16MB: Your original numbers are closer to what I've been seeing. I can't tell without looking at your publish output, but I've noticed that sometimes extra files from my project get included when I do a publish. Could you try doing a clean build?

My understanding is that sos may be removed from the output soon - @mikem8361, could you address this?

Could you try doing a clean build?

@sbomer Doing clean build doesn't seems to change anything, still getting same 165MB single file, 65MB single file trimmed and ~150MB without single file for WinForms project.

Commands I used to publish:
dotnet publish -c Release -r win-x64
dotnet publish -c Release -r win-x64 /p:PublishSingleFile=true
dotnet publish -c Release -r win-x64 /p:PublishTrimmed=true /p:PublishSingleFile=true

And outputs:
publish_nosinglefile.zip
publish_singlefile.zip
publish_singlefiletrim.zip

@tomrus88 thanks for pointing out the issue of file size disparity with single-file. This is certainly unexpected, and wasn't the case when using the Preview5 release of dotnet SDK. I'll investigate further. Thanks.

I've noticed even bigger issue with PublishTrimmed option, the WinForms and WPF apps crashing due to missing dll's.

Application: wfcore.exe
CoreCLR Version: 4.6.27724.71
.NET Core Version: 3.0.0-preview6-27727-02
Description: The process was terminated due to an unhandled exception.
Exception Info: System.TypeLoadException: Could not load type 'System.ComponentModel.Component' from assembly 'System.Windows.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
   at wfcore.Program.Main()
Application: wpfcoreapp.exe
CoreCLR Version: 4.6.27728.71
.NET Core Version: 3.0.0-preview6-27728-04
Description: The process was terminated due to an unhandled exception.
Exception Info: System.TypeLoadException: Could not load type 'System.Windows.Markup.IQueryAmbient' from assembly 'System.Threading.Thread, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Update: Tested again on SDK 3.0.100-preview6-012113
Console App 67.5 MB without trimming, 27.2 MB with trimming
WinForms App 164 MB without trimming, 51.0 MB with trimming
WPF App 164 MB without trimming, 65.2 MB with trimming
and WinForms trimmed app is running fine again, WPF trimmed app still crashes, but now with different error

Application: wpfcoreapp.exe
CoreCLR Version: 4.6.27728.71
.NET Core Version: 3.0.0-preview6-27728-04
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Windows.Markup.XamlParseException: The method or operation is not implemented. ---> System.NotImplementedException: The method or operation is not implemented.
   at System.Windows.Baml2006.Baml2006SchemaContext.ResolveBamlType(BamlType bamlType, Int16 typeId)
   at System.Windows.Baml2006.Baml2006SchemaContext.GetXamlType(Int16 typeId)
   at System.Windows.Baml2006.Baml2006Reader.Process_ElementStart()
   at System.Windows.Baml2006.Baml2006Reader.Process_OneBamlRecord()
   at System.Windows.Baml2006.Baml2006Reader.Process_BamlRecords()
   at System.Windows.Baml2006.Baml2006Reader.Read()
   at System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector)
   at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
   --- End of inner exception stack trace ---
   at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
   at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
   at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
   at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
   at System.Windows.Application.LoadBamlStreamWithSyncInfo(Stream stream, ParserContext pc)
   at System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean bSkipJournaledProperties)
   at System.Windows.Application.DoStartup()
   at System.Windows.Application.<.ctor>b__1_0(Object unused)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at wpfcoreapp.App.Main()

I don’t have an exact date but I hope that SOS in coreclr will be removed before Preview 7 towards the next of month.

@sbomer @swaroop-sridhar I've made a separate issue about WPF app crashing with ILLink https://github.com/mono/linker/issues/595

File size information for .NET Core SDK 3.0.100-preview7-012261

Project Type | Self Contained | SingleFile | SingleFile+Trim
------------ | ------------ | ------------- | -------------
Console | 69MB | 69MB | 28.4MB
WinForms | 152MB | 168MB | 101MB
WPF | 152MB | 168MB | 101MB

Bug where single file publish for WinForms/WPF has 16MB (168-152) extra compared to no single file still exist (somehow Console not affected by this bug?).
Trimmed size for WinForms and WPF increased significantly with slight decrease for Console projects. Trimmed WPF single file apps no longer crashing.

Thanks @tomrus88 for the set of numbers. I chatted with @swaroop-sridhar this week about the additional content in the bundle, and we believe the build targets are passing in additional files to bundle. We are looking deeper and will have an update. Happy to hear that WPF trimmed is working as expected.

@jeffschwMSFT 101MB is still too big,do you have further solution?

101MB is still too big,do you have further solution?

Keep in mind that this number has ~15 mb of things that should not be part of it, which we hope to get removed. Starting in Preview6 the linker will be integrated and the full breadth of options for shrinking will be available. Depending on your scenario you can the app down in the 70mb range - but like any linker technology developer intervention will be necessary in order to ensure the app continues to work.

Echoing @jeffschwMSFT: there will be ways for you to do more manual work to further reduce the size, but it won't be easy... it will essentially involve telling the linker about all roots needed by reflection and native code. We'll have updated docs on this in the next preview.

we believe the build targets are passing in additional files to bundle

This bug is tracked in https://github.com/dotnet/sdk/issues/3257

https://github.com/dotnet/sdk/issues/3257 is now fixed.

The other issues for reducing size are:

  • Removing SOS.dll (tracked by https://github.com/dotnet/coreclr/issues/24092)
  • ILLinker improvements

So, I think we can close this issue.

@Cup: I've referenced the SOS issue above.
@Sven: If there are further issues about enabling Linker features more aggressively, please link them to this issue.

I've closed the issue now, since none of the work remaining is in CoreCLR repo.

@swaroop-sridhar Is fix for https://github.com/dotnet/sdk/issues/3257 available in daily .NET Core SDK builds? Because i'm still getting those extra 15MB on latest .NET Core SDK 3.0.100-preview7-012386. Or was this fix for some different bug?

@tomrus88 the fix for dotnet/sdk#3257 isn't available in that build.
I've verified that the fix works in the SDK branch, it'll propagate to the core-sdk today/tomorrow.

Single file size statistics for .NET Core SDK 3.0.100-preview7-012403

Project Type | Self Contained | Single File | Single File+Trim
------------ | ------------ | ------------- | -------------
Console | 67.4MB | 67.4MB | 27MB
WinForms | 151MB | 151MB | 86.9MB
WPF | 151MB | 151MB | 86.9MB

So double write bug appears to be fixed in latest daily preview build and file size is slightly lower now.

Another option is Roslyn:

~~~
C:\microsoft.net.compilers.3.3.1> type sun-mon.cs
using System;
class Program {
static void Main() {
Console.WriteLine("sunday monday");
}
}

C:\microsoft.net.compilers.3.3.1> tools\csc.exe sun-mon.cs
C:\microsoft.net.compilers.3.3.1> sun-mon.exe
sunday monday

C:\microsoft.net.compilers.3.3.1> dir sun-mon.exe
2019-11-16 11:34 PM 3,584 sun-mon.exe
~~~

https://nuget.org/packages/Microsoft.Net.Compilers

@cup The .exe produced by the C# compiler (Roslyn) is not the "self-contained" app and it's not a .NET Core executable. It needs a CLR runtime and framework to work. Which one that will be is determined on how you run it. If you just run it as is on Windows, it will run on .NET Framework (the desktop version which is part of Windows OS). You can try this by changing the code to be something like this:
``` C#
using System;
class Program {
static void Main() {
Console.WriteLine(typeof(object).Assembly.Location);
}
}

Then compile it with the C# compiler and run the exe. It will print out something like:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll

It's actually not possible to run the `.exe` as is with .NET Core as required configuration files are missing. You could add a file `sun-mon.runtimeconfig.json` next to it with content like this:
``` JSON
{
  "runtimeOptions": {
    "tfm": "netcoreapp3.0",
    "framework": {
      "name": "Microsoft.NETCore.App",
      "version": "3.0.0"
    }
  }
}

Then running it via the dotnet.exe will look like this:

dotnet sun-mon.exe
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0\System.Private.CoreLib.dll

So now it did run against .NET Core (3.0 in this case). Note that it used the runtime and framework installed in program files to do so.

In my case, if I create zip folder with executable + dll: 29MB
If I use PublishSingleFile and PublishTrimmed: 129MB

It still is too big. Using .net core 3.1 app on windows.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sahithreddyk picture sahithreddyk  Â·  3Comments

Timovzl picture Timovzl  Â·  3Comments

nalywa picture nalywa  Â·  3Comments

matty-hall picture matty-hall  Â·  3Comments

chunseoklee picture chunseoklee  Â·  3Comments