Search code examples
.net-core.net-8.0building

dotnet produces two copies of an executable


The dotnet compiler produces two copies of the same executable and my google skills are not good enough to figure out why.

E.g. in a simple Console project:

dotnet publish --configuration Release produces bin/Release/net8.0/FizzBuzz and bin/Release/net8.0/publish/FizzBuzz, which are identical.

I want to build one optimized executable and one cross-platform executable. How do I do that?


EDIT: Thanks to @winscripter, I figured out the following:

dotnet publish -c . will produce:

bin/net8.0/
├── FizzBuzz (executable)
├── FizzBuzz.deps.json (not sure)
├── FizzBuzz.dll (cross-platform IL code)
├── FizzBuzz.pdb (debug symbols)
├── FizzBuzz.runtimeconfig.json (required for non stand-alone)
└── publish/ (a copy of the above files)

FizzBuzz.dll and FizzBuzz.runtimeconfig.json are needed for the FizzBuzz executable, which can be executed directly with ./bin/net8.0/publish/FizzBuzz.

FizzBuzz.dll can be run cross-platform with dotnet bin/net8.0/publish/FizzBuzz.dll. Only FizzBuzz.runtimeconfig.json is required for that to work.

The rest of the generated files are not needed for running the program.

I'm pretty sure that, without specifying the runtime option for dotnet, the default optimized build is for my platform, e.i. linux.

Because ldd reports linux specific dependencies:
ldd bin/net8.0/publish/FizzBuzz
    linux-vdso.so.1 (0x00007ffd260e0000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe955acd000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe955ac8000)
    libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe955800000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe95571e000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe955a9b000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe955539000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fe955b05000)

I know that there are different build targets (OS specific, dotnet SDK specific etc) but I'm confused by the many options.

If you link to https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish, please provide a description of the cons of the options, since I don't get them.

My FizzBuzz.csproj file:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <AssemblyName>FizzBuzz</AssemblyName>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <!-- I added ErrorOnDuplicatePublishOutputFiles -->
    <ErrorOnDuplicatePublishOutputFiles>true</ErrorOnDuplicatePublishOutputFiles>
  </PropertyGroup>

</Project>

I added <ErrorOnDuplicatePublishOutputFiles> to see if that would change anything. It did not.

dotnet --version
8.0.301

uname -a
Linux kali 6.8.11-amd64 #1 SMP PREEMPT_DYNAMIC Kali 6.8.11-1kali2 (2024-05-30) x86_64 GNU/Linux

Solution

  • With regards why there are two copies of an executable, see this answer:

    dotnet publish builds the project before copying binaries to the output directory. The files you see in bin\Release\netcoreapp2.0\win-x64 directory are the result of dotnet build command. You could check it by running following command: dotnet build --configuration Release --runtime win-x64

    With regards how to publish an executable to run on multiple systems at once: I don't think this is possible. When you use the dotnet publish command, it generates an executable that is OS specific (so one will run on one operating system only.) So, you would need to publish multiple executables in order to make the application cross platform, for example, dotnet publish --runtime win-x64 for Windows 64-Bit or dotnet publish --runtime linux-x64 for Linux 64-Bit. Or, use dotnet build to generate a dll file that contains IL code which can run on multiple systems at once, but it may require to be executed with the dotnet command.