Can I shrink a dotnet publish package - .net-core

I have recently started developing using dotnet core (as opposed to old fashion plain .net) to create a number of small utility console applications.
The development is fine and it has come to the point I want to publish them.
I am using the CLI and as I am only interested in Win 10 deployments, tried
dotnet publish -c release -r win10-x64
It worked and built me a "publish" folder where everything seemed to work, though the "publish" folder is huge (~70mb) compared to the size of the app (~500 lines of code).
As I am only going to deploy to Win10 machines is there a way to package this so I don't need all the .NET files? I thought that was what the -r option was for but that does not seem to have achieved much.

It depends on how you want to deploy your app/who is going to use them.
The -r flag creates a self-contained app. This causes the publish command to include the necessary .NET Core DLL's for the specified platform (and platform specific nuget packages if they are avaiable), which means anyone can use the app without having to install .NET Core runtime.
If you remove the -r flag then publish will only include the DLL's for you app. But this means whoever wants to use your app must first install the .NET Core runtime.
You can see the difference by using the -o flag to write the publish output to different directories e.g.
dotnet publish -c release -r win10-x64 -o ./publish-win10
or
dotnet publish -c release -o ./publish-any
Now go and have a look at what has been written to ./publish-win10 and ./publish-any folders and you can the difference.
If you are installing them onto a system where the .NET Core runtime is already present then you can just distribute the DLL and save a lot of space. However if you want to be able to distribute the app without the end user having to worry about having the .NET Core runtime installed then the -r flag to create a self-contained distribution is the way to go, but results in the 'package' including the necessary .NET Core assemblies.
AFAIK the -r flag does not affect how you app is compiled, just what runtime DLL's are included as part of the publish command. So you always get the same DLL for your code if you publish it for win10-x64 or with, or without, the -r flag so your app DLL will run on any (.NET Core compatible) platform, but I am happy to be corrected on that point.

Unlike .NET 4.x which you are used to where building is the standard to create an output, .NET Core (And .NET 5) considers building and publishing to be very different.
The huge (70MB) size is because the publisher is assuming that your target does not have any form of the .NET Framework installed, so is bundling it in with your project.
You can change your publish line to
dotnet publish -r win-x64 --self-contained false
Just by itself, this will create the .NET Core standard - A .dll (Your application), a .exe (That runs the application), some .json files for settings, and a .pdb file for debugging symbols.
To alter the application to the .NET Framework standard output that you are used to, open the .csproj file in a text editor, and below the
<TargetFramework>netx.x</TargetFramework>
line, add
<PublishSingleFile>true</PublishSingleFile>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
And rerun the publish command. This will result in a single .exe and a single .pdb in the publish directory (The .pdb file which you can safely delete).
Note: Since you have included --self-contained false, the target will need the specified version of the .NET Framework (The one specified in your .csproj file) installed. Whilst many versions of the .NET Framework are currently installed on most Windows 10 Devices (Generally located at C:\Windows\Microsoft.NET\Framework or C:\Program Files\dotnet\), the .NET Core / .NET 5 Runtimes aren't yet as common (Although will likely be distributed through Windows Update in the near future), so may require a once-off download if the recipient of your .exe does not have it.
Whilst the .NET Core / .NET 5 resultant binaries ARE larger (Although by around 150kb - Not 50MB), they run significantly faster than their .NET Framework 4.x counterparts.

Related

Obfuscator for .NET Core Single Publish Files

Is there an obfuscation tool that can work well on the exe and pdb files that result from a dotnet core single file publish?
I am using dotnet core single file publish with the command: dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true. This works great in giving me just two neat files an exe and a pdb file, which I am able to give to a client to run my application.
However, I am still concerned about its ability to be decompiled.
I tried using ILSpy and JustCompile on both the files and they luckily could not be decompiled with these tools. Is it then that my files are safe, or it is that the tools have not yet caught up?
If the latter, what obfuscation tool can I use to protect these files? I attempted to use Obfuscar which did not work specifically on the single file publish outputs, the exe and pdb.
Any suggestions on the obfuscation tools to use for this?
Disclosure: I work for the Dotfuscator team at PreEmptive.
We have tested and verified that Dotfuscator Professional handles this scenario on both .NET Core 3 and .NET 5.
Specifically, you must use Dotfuscator Professional's MSBuild integration, which is now our recommended method of using Dotfuscator Professional for new projects. However, Dotfuscator will not update .pdb files on .NET Core or .NET 5, so you will not be able to debug builds which use Dotfuscator (e.g., Release builds). You should not ship .pdb files to untrusted users.
You can decompile .NET Core self-contained executables if you manually unpack them:
Can .Net Core 3 self-contained single executable be decompiled?
You would have to run the obfuscator as part of the build process, before the individual assemblies are compressed into the single file. That's probably possible if you add a custom MSBuild target that executes the obfuscator, and use the BeforeTargets attribute to integrate it at the correct point in the build process. But I haven't looked at the .NET core build system in detail.
You can use Obfuscar.
Use it in obj directory after target Compile and then copy obfuscated files to directory.(replace with original files)

How do I integrate exe publishing with the VS2019 build of the console .net core 2.2 application?

I saw a couple of similar questions but so far I found no single answer to the problem of integrating the publishing step with my build process. Unfortunately the dotnet publish command rebuilds the project again meaning that if I put the "dotnet publish" command in the project's Post-Build steps I get an infinite loop of building retries.
What I want to achieve is to get an exe built for my .NET Core 2.2 Console App for a few selected environments eg. osx and windows-10, possibly linux too, each in its own folder. Obvious condition is that it has to be integrated with the build, so no extra manuals steps (commands) are required. This has to work from within VS2019 Pro as well in CI (like AzureDevOps).
Is this basic step achievable or .Net core was a major step-back in the progress of software development?
I hope I just miss something and I am just grossly exaggerating. :)
Thanks, Radek
How do I integrate exe publishing with the VS2019 build of the console
.net core 2.2 application?
Actually, I think you do not need to worry about this.
dotnet publish already contains the build process. Publish process will first execute Build and then run publish. In a word, Build is a part of Publish process.
So when you input dotnet publish under Build, you will get an infinite loop of building retries.
Solution
----- Just delete post-build event in xxx.csproj file and just dotnet publish directly and it will run the build process first.
You can test in the local VS and when you right-click on your project-->Publish, it will show the step in the output windwow.
In addition, as far as I know, Azure DevOps has a task called dotnet publish which contains Build.
And if you want to do some msbuild custom target only for publish step, you can add a condition like Condition="'$(DeployOnBuild)'=='true'", it will execute for Publish process rather than the normal build step(right-click on your project-->Build).
<Target Name="testone" Condition="'$(DeployOnBuild)'=='true'" AfterTargets="Build">
xxxxxxxxxxxxxx
</Target>
---------------Update 1----------------
First question
Actually, the build of the publish is the pure process and then publish will copy the content of the build output into publish folder. So the execute program is just from the build output folder.
See the publish log:
So you should not specify a publish target under the build process. This is superfluous.
Second question
To generate this program for Window-10, linux or osx, you can try these command line to publish your project:(Release is the standard release build configuration)
For Win-10:
dotnet publish -r win10-x64 -c Release --self-contained
Linux:
dotnet publish -r linux-x64 -c Release --self-contained
For osx:
dotnet publish -r osx.10.12-x64 -c Release --self-contained
In this way, the project is first built according to the specified runtime and then published.
More info about .NET Core RID Catalog, you can refer to this document.
Update 2
I think you should change the configuration in this package UI:
Then click Save.
Also, when you publish this web project, please try to delete bin and obj folder and then publish it.
Debug: bin\Debug\netcoreapp2.1\publish
Release: bin\Release\netcoreapp2.1\publish
Or you should use dotnet command as I described to publish the project. The path is under Debug or Release folder.
What I want to achieve is to get an exe built for my .NET Core 2.2
Console App for a few selected environments eg. osx and windows-10,
possibly linux too, each in its own folder. Obvious condition is that
it has to be integrated with the build, so no extra manuals steps
(commands) are required. This has to work from within VS2019 Pro as
well in CI (like AzureDevOps).
Is this basic step achievable or .Net core was a major step-back in
the progress of software development?
It's a good idea but as I know what you want is not 100% supported for now.
It seems that your expected scenario is:
Click the Build(F5) button=>Project will be built in different platforms win-x64,win-x86,linux-x64...,also will be published in different platforms automatically with self-contained mode.
But the fact is:
1.Click the Build button(Build(F5) equals to the Build button in project context) will run the Build Target (A default built-in target for each project for build). => dotnet build in command-line.
2.Click the Publish button will run the Publish Target (A default built-in target for each project for publish). => dotnet publish in command-line.
3.If you have any build/publish command in post-build event, it will result in an expected loop. So it's hard to combine publish with build perfectly since they're two actions in VS with different button/behavior/corresponding command. (Only dotnet publish command can recognize --self-contained)
4.To build/publish the projects in parallel, the batch file or msbuild targets file is a good choice.
#1.Build different platforms using one build command see this. #2.Publish different platforms using one command see this. (They both use custom .targets to do the job)
Suggestions:
According to your scenario, I think you can consider using #2. It's not necessary for you to build with different platforms during your normal development.
1.In local VS when you're developing and debugging:
The default build button/option is enough for you to debug.
2.In local VS when you want to publish the project in different platforms:
Use #2 above so that you can publish them using cmd.exe or PS.exe. (Via command dotnet msbuild -restore -t:PublishAllRids)
3.When you're automating the CI pipeline in AzDeops(Also use #2):
A CMD/PS task with dotnet msbuild -restore -t:PublishAllRids command is enough. Dotnet publish will build automatically if we don't pass --no-build argument.
4.Azure Devops Service suggests separate the jobs in different tasks, it's not recommend to Integrate Publish with Build heavily, especially for .net core projects. (Here's official sample about CI for .net core projects.)

.Net Core 3.0 NuGet DLLs

Let's say that we include Nuget Package Microsoft.Extensions.Configuration in a Console .Net Core app, and include the same package in another Console .Net Core app.
When we publish these two apps, each app would publish:
Microsoft.Extensions.Configuration.Abstractions.dll
Microsoft.Extensions.Configuration.dll
in each folder.
If we had 10 console apps using the same package, we would have these dlls in 10 different folders in the application server. If we reference to multiple NuGet packages, the number of dependency dll files would multiply.
Is there a way to consolidate these dlls in one folder in the app server, so when we publish our executable, all we need to do is move the executable and configuration file to the server, and it will find these dlls in a common folder. Sort of setting a dll Path.
In consideration of Trisped's suggestion, I am posting an answer that neither me nor my boss are completely satisfied with. But for the time being to the best of my knowledge, the way to consolidate these DLLs is by writing a utility program to move those DLLs to a designated common DLL folder. And, at the same time update both .deps.json file and .runtimeconfig.json file with the new location path of the common DLL folder and additionalProbingPaths folder path structure, respectively.
We can't do this by hand manually because there would be too many DLLs to move and too tedious to edit .deps.json file, which got wiped out everytime we publish the Console App solution. I have written the utility program. Unfortunately this is company's IP so I can't share the code.
The idea is to enumerate the DLLs in the publish folder and store those filenames in a collection / dictionary, and later on use that dictionary to update the runtime dll paths in .deps.json. For CLI use, I use these options:
-c Release -f netcoreapp3.0 --self-contained false -r --runtime win-x64 -o <publisheFolder>
It would be very helpful if Visual Studio Publish Profile would include a folder that we can specify, where all Third Party and Nuget Package DLLs will reside, in addition to the Publish folder, where only the app executable, app dll, configuration files, deps.json and runtimeconfig.json will reside. Even better if the CLI would allow additional option to specify the DLL folder and, not include the runtime folder when --self-contained false is indicated.
After all, isn't one of the main purpose of DLL to allow applications to share code with each other?

.net core 2.0 console app : exe file location

I am working with .net core 2.0 console application. I need to run this console app using command prompt.
Like,
MyApp.exe arguments
I published the console app using below command to generate .exe :
dotnet publish -c Release -r win10-x64
It creates multiple .exe file,
1) \bin\Release\netcoreapp2.0\win10-x64
2) \bin\Release\netcoreapp2.0\win10-x64\publish
I believe both are same and I can use (2) as published version of the app. Correct me if wrong.
I am not sure why it generates .exe at (1) and does not contain bunch of dlls at there.
What is the difference?
anyone can give me more information about this?
The first one is still a framework-dependent deployment, it used when you call dotnet run -r win-x64. It resolves and configures the shared framework via the information in .runtimeconfig.json and your PATH environment variable and locates the DLLs via a values in the .runtimeconfig.dev.json and .deps.json based on your global packages cache (=> specific to your machine and user).
For deploying self-contained applications, the publish folder contains all the necessary assets. The host uses the local dlls instead of the shared framework and as well as the necessary DLLs.

ASP.NET v5 on a Build Server

I'm trying to build a VS2015 ASP.NET v5 web app on our build server (basically, outside of Visual Studio). Our existing scripts simply invoke msbuild with the csproj file, but with this project I get:
Project File is empty
What is the "story" for "building" these new webapps outside Visual Studio? I believe they can still target .NET 4.5 (I hope so, as we're not upgrading web servers any time soon) so assumed it were possible.
Well there is no .csproj in a dnx project everything that is needed for dnu to build a project is contained in the project.json. There is a xproj file but you can ignore that. Microsoft has finally decided to see the light and uses xproj just for VS specific "stuff" and IDE agnostic project details are put in the project.json.
So to build a dnx project all you need is the right version of dnx and the project source code. Now AFAIK there are no out of the box solutions but everything is done with command line commands so script something up should be easy. It all depends on how robust of a solution you want to build.
To build a dnx project from the command line (assuming you have the proper dnx installed and set to active) it is just two commands. dnu restore runs a dependency check and dnu (a part of dnx) has a built in nuget client so it will reach out and grab dependencies if needed. dnu build runs that actual compilation.
So cd to the project root (which contains project.json) and run dnu restore then dnu build.
It gets more complex if you need to dynamically support different dnx versions. Keep in mind dnx versions are identified by runtime (coreclr or clr), architecture (x86, x64, etc), and a version number. So if you are only targetting say x64 builds on clr (full .net runtime) that eliminates two variables but what happens if a project requires a newer version of the runtime than what is installed on the build server? As an example say you installed (manually using dnvm) dnx-clr-win-x64.1.0.0-beta4 on the build server but at some point in the future a developer requires dnx-clr-win-x64.1.0.0-beta6-1200 to resolve a bug.
The simplistic solution would be just to install new runtime versions as needed and build all projects against the newest one needed. This isn't as bad as it might first sound. Once dnx gets out of beta changes to the runtime should be infrequent. Remember the runtime is the very low level code and unmanaged dlls. It is the bootstrapping stub that the BCL sits on top of. Hopefully there should not be that many changes to the dnx for a given OS, architecture and runtime.
For a more robust solution you could use scripting to find the runtime version required for a project. It is found in a solution level global.json. The script could then use dnvm list to determine if it has that runtime installed. If it doesn't then use dnvm install or dnvm upgrade to install the required version. Before starting the build it would use the command dnvm use to make the correct runtime active and then proceed with dnu restore and dnu build.
Honestly I expect some pretty robust solutions to come along. Task runners (gulp, grunt, etc) are first class citizens in .NET 5. Most likely your workflow will involve bower for client side dependency resolution, npm, grunt/gulp and some task packages for things like minifying js files. The build server is going to need all that as well so having a build task as a grunt or gulp package seems a pretty good fit.

Resources