7

I have two C# .NET 5 projects, let's call them A and B. Both are executables (not DLLs). Project A references project B. I would like to publish project A as a self-contained executable (though not single-file, if that matters). I'm using this command to kick off the publishing process:

dotnet publish A.csproj -c Release --self-contained -r win-x64 /p:PublishTrimmed=true /p:PublishReadyToRun=true

However, this produces the following error when attempting to build project B (which is required since project A references it):

error NETSDK1031: It is not supported to build or publish a self-contained application without specifying a RuntimeIdentifier. You must either specify a RuntimeIdentifier or set SelfContained to false.

But as you can see, I am (I think?) specifying a runtime identifier with -r win-x64. I'm guessing that the runtime identifier that I've specified on the command-line isn't getting passed through when building project B. How can I fix this without disabling SelfContained?

My dotnet version is:

dotnet --version
5.0.400

EDIT: I do not want to put <RuntimeIdentifier>win-x64</RuntimeIdentifier> in my project files because I need to build for multiple different runtimes and I want to be able to control that via the command line.

Walt D
  • 4,491
  • 6
  • 33
  • 43
  • Internally .NET .exe have the same format as DLL's, with some native bootstrap code and a entry point. A concept from Java they lifted and improoved upon. | Compiling it to need no DLL's - not even the Framework ones - is what .NEt Native does, I think: https://stackoverflow.com/a/45710/3346583 – Christopher Aug 16 '21 at 19:03
  • @Christopher Sorry, I don't think I understand your point. – Walt D Aug 16 '21 at 19:06
  • I think you cannot do this without hacking the msbuild somehow. But i think this is just an issue with architecture. Why not extract a shared library between two executable projects? It's supported, official way to do this. And i cannot seem to find any disadvantage of that. Library is library, application is application. Depending on another application just seems hacky – Jakoss Aug 16 '21 at 19:12
  • I understand you want your project to be "self contained", in the sense of "no external dependencies". BUT ... are you absolutely certain that you need `--self-contained`? SUGGESTION: Try it without. See if it resolves the problem; verify your app runs correctly. – paulsm4 Aug 16 '21 at 19:15
  • @Christopher Extracting a shared library would take days of work. EXEs referencing other EXEs has long been supported. – Walt D Aug 16 '21 at 19:15
  • @paulsm4 I'll try that, thanks! Looking at the docs it looks like `--self-contained` is the default when a runtime is specified. – Walt D Aug 16 '21 at 19:16
  • @paulsm4 Not working, now I get the error `The referenced project 'B.csproj' is a non self-contained executable. A non self-contained executable cannot be referenced by a self-contained executable.` – Walt D Aug 16 '21 at 19:17
  • Adding `/p:ValidateExecutableReferencesMatchSelfContained=false` seems to be working, though I'm a little concerned that I may be suppressing something important. – Walt D Aug 16 '21 at 19:22
  • For whatever it's worth: a) I use MSVS > publish (vs. dotnet publish). But they *should* be "equivalent". b) I've always used Deployment Mode: Framework-dependent (vs. "Self-Contained"). c) I'm suggesting trying "Deployment Mode: Framework-dependent" for both your projects: A and B. d) I DON'T know if my environment asp.Net Core app deployed to an IIS server, with .Net runtimes installed) is necessarily applicable to yours. But I think it's important to ask yourself if you REALLY need "Self-Contained". There's a good chance that maybe you don't... – paulsm4 Aug 16 '21 at 19:22
  • Framework-dependent works fine, but I specifically need self-contained. – Walt D Aug 16 '21 at 19:23
  • @WaltD "Extracting a shared library would take days of work. EXEs referencing other EXEs has long been supported." Then it is realy good I said nothing in that direction! In fact I said that .exe and .dll are the *same* and thus no extraction is needed. – Christopher Aug 16 '21 at 19:33
  • @Christopher Sorry, looks like I tagged the wrong person. My bad! – Walt D Aug 16 '21 at 19:34

2 Answers2

7

Thanks to a couple of comments in this Github issue, I was able to figure out how to get it to compile:

  1. Remove the --self-contained flag from the command-line. (According to the docs, "Default is true if a runtime identifier is specified and the project is an executable project (not a library project).")

  2. Add /p:ValidateExecutableReferencesMatchSelfContained=false to the command-line. This was required to suppress the error: "The referenced project 'B.csproj' is a non self-contained executable. A non self-contained executable cannot be referenced by a self-contained executable."

  3. If you want to be able to run the B.exe file that was generated while building project A, you'll need to add an appropriate B.deps.json file to the build output. The easiest way I've found to do this is to make a separate self-contained build of B and then simply copy over the B.deps.json file from it. (And if you're building with PublishTrimmed as I am, then you may need to add entries into your TrimmerRoots.xml file to prevent code needed by project B from getting trimmed from the output.)

After doing these two things, my project is now building and running successfully. I have verified that it still works even on a machine that does not have the .NET Runtime installed and thus the build really is self-contained as desired.

Walt D
  • 4,491
  • 6
  • 33
  • 43
  • "attempting to run this executable does not work" is expected, as you used `/p:PublishTrimmed=true` which trims MSIL for A, but can be harmful for B (trimming out what B requires). – Lex Li Aug 16 '21 at 21:01
  • @LexLi I think you're probably right that PublishTrimmed could potentially cause problems (hopefully adding appropriate entries to TrimmerRoot.xml would solve them?), though in my case it turned out to be simply that the build output of A doesn't include the `B.deps.json` file. If I include that file then it runs fine! – Walt D Aug 17 '21 at 04:26
0

I also have Library, Console and UI projects and having same problem. I referenced console project into UI to be copied over to the output folder of UI in development and publish time, but the best solution is just to unreference the console project and publish both for same folder

dotnet publish MyConsole -o "publish/myRelease" -c Release -r xxx-xxx -p:PublishReadyToRun=true --self-contained
dotnet publish MyUI-o "publish/myRelease" -c Release -r xxx-xxx -p:PublishReadyToRun=true --self-contained

My build scripts:

  1. https://github.com/sn4k3/UVtools/blob/master/build/createRelease.ps1
  2. https://github.com/sn4k3/UVtools/blob/master/build/createRelease.sh

I only call the ps1 script on windows and let WSL do the rest for others.Unfortunately compiling to windows on linux generate a "bad" .exe

Note: If you want the other project to be copied during development, eg: Visual Studio, you can create a simple copy over command on build conditions. That will fix both problems