Runtime: Specify runtime files location for self-contained apps

Created on 9 Feb 2019  路  13Comments  路  Source: dotnet/runtime

Publishing a self-contained app currently copies over 250 files into the output directory, which is a bit messy. Is there a way to specify the location for the runtime files so for example I can put them all into a "bin" or "runtime" folder to get a clean output directory with just my app files?

When you give your self contained app to others they first have to scroll through all over these over 250 runtime files just to find the actual executable. It really doesn't look nice and most of the time it's the first thing I'm getting to hear.

area-Host

Most helpful comment

@hmartinez82 currently this is not possible (or would be pretty tricky) since the host expect the self-contained "runtime" to live next to the app.

What you could do instead is build the apps as framework dependent. Then when installing:

  • Get the "zip" download for the specific runtime version/target
  • Unzip it to some location - can be in "runtime" subfolder to your main folder
  • Install the apps as shown above (but really anywhere, it doesn't matter)
  • Run the apps via a "script" - all you need to do is set DOTNET_ROOT=path env. variable to point to the runtime folder and then run the app from that environment. On Windows you may also want to set DOTNET_MULTILEVEL_LOOKUP=0 to make sure the apps will not use the globally installed .NET Core (if there's any).

I know this is not ideal as it requires you to run the app via a script, but it's the only solution we have today.

It's not a self-contained app anymore, but you control the version of the runtime fully - so it's very similar in that sense.

All 13 comments

Currently there's no option to do this. In theory it's possible, but our build/SDK doesn't do that.
Possibly a better solution would the proposed "single-exe" capability dotnet/designs#52

In theory it could solve the issue. But the single-exe solution according to the notes slows down the start time of the application by extracting all the file to disk. There is a huge difference between 200ms more start time or 2 seconds more start time. Without seeing it in action I cannot say whether it's better or not.

Another question, is there a way to get rid of unused runtime DLLs? As if now it seems it just copies the whole .NET Core Runtime into the folder. If I can reduce the files from >250 to maybe just 50-70 I'm actually using it would be a great help.

@sbomer would know if there's a way to use ILLinker to do that.

@Symbai I'm not questioning your scenario, I do agree that it would be great to have the option to put all the runtime files into a subdirectory..

Yup, the linker should be able to do that (instructions at https://github.com/mono/linker/blob/master/corebuild/README.md). The linker is a bit broken at the moment and is likely to change, so consider waiting until the next release if you'd like a better experience. Happy to help if needed.

@Symbai can you try the single-file in the latest preview? See this for how to enable it: https://github.com/dotnet/sdk/pull/3074

It does extract onto disk the first time you run the app, but subsequent runs should simply reuse the files which are already on the disk...

It does work but it's not exactly what I had in mind. Rather something like this https://github.com/0xd4d/dnSpy/blob/master/Build/AppHostPatcher/Program.cs . It allows to tell the native .NET Core apphost executable to launch the application.dll from a sub folder. This way the folder structure is a bit more clean and user friendly. So it becomes:

Program.exe
bin\Program.dll
bin\....

An improved version of this would only move all the .NET Core files into a sub folder so it becomes:

Program.exe
bin\Program.dll
bin\core\System.dll
bin\core\...

While single file is neat it comes with some other issues. For example launching files from detachable drives and avoid leaving "traces" on the host computer. Or if you store your settings in the program folder to make it portable. When using the single file option uses a temp folder instead which makes it difficult to programmatically define the setting file storage then.

But if this cannot be done or won't be done then you're welcome to close the issue and link to the single file option.

The approach implemented by @0xd4d is possible today without external tools, it's just not very easy to use:

  <UsingTask TaskName="CreateAppHost" AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" />
  <Target Name="PostBuild" AfterTargets="PostBuildEvent"
          DependsOnTargets="_GetAppHostPaths">
    <CreateAppHost AppHostSourcePath="$(AppHostSourcePath)"
                   AppHostDestinationPath="$(OutputPath)\app.exe"
                   AppBinaryName="bin\app.dll"
                   IntermediateAssembly="$(OutputPath)\app.dll"/>
  </Target>

This is essentially what the SDK does to produce the original app.exe. Currently there's no way to customize that process (I filed https://github.com/dotnet/sdk/issues/3405 for that). The above basically recreates the app.exe from scratch (so duplicating the work which SDK already did), but with the ability to customize the imprinted path (in the above sample it "moves" the code into a bin folder).

The problem with the above snippet is that it relies on internal SDK stuff (_GetAppHostPaths) which we want to keep "private" and thus have the ability to change them without breaking people. So if you do decide to use this approach, it comes with "can be broken at any time" tag on it.

The larger feature ask here is to be able to customize the layout of the application output folder (moving files into subfolders). This would require larger changes to the system, on the host side it would require a fix for https://github.com/dotnet/core-setup/issues/5645. On the SDK side it would require a way to specify the desired structure and then the actual implementation which would know how to generate the right .deps.json for it. So far we don't have that on the plans anywhere (that I'm aware of).

@vitek-karas Thanks for explaining this to me. Do you think I should leave this issue open or rather close it as there are no plans for it anywhere like you said?

We are "making" the plans for .NET 5 now, so I would say now is actually the best time to come up with "ideas". The larger SDK feature is something which would probably be better tracked in the SDK repo (the right people will see that and so on). Also because I think most of the work would be on the SDK side.

If you can spare some time, please create an issue there describing the scenario - the technical details are not that interesting at this point, but do try to answer the "why do I want this", "what's the problem I'm facing now", "why single-file doesn't work for me" and so on. These are the questions we generally have the hardest time answering - it's you, the users who know best.

In any case, thanks a lot for providing the feedback and answering our questions - these kinds of discussions help so much to understand what are the issues with the product.

you can try this tool NetCoreBeauty, and it should works, but it still have a bug that cannot be fixed it a short time, see https://github.com/dotnet/core-setup/issues/5645#issuecomment-516764531.

@sbomer Do you know if it's possible to have a single copy of the runtime, that came from a self-contained deployment for instance, and have multiple exes that are in subfolders use the runtime I in the topmost folder:

  • MainProg.exe
  • runtime dlls
  • SubFolders

    • SubFolder1

  • - - App1.exe
  • - - App1 specific deps

    • SubFolder2

  • - - App2.exe
  • - - App2 specific deps

I'm finding it impossible to tell App1.exe and App2.exe to use the runtime files from the top-most folder.

I've looked for docs on the matter and I can't find anything, not even on the deps.json and runtime.json docs

@hmartinez82 currently this is not possible (or would be pretty tricky) since the host expect the self-contained "runtime" to live next to the app.

What you could do instead is build the apps as framework dependent. Then when installing:

  • Get the "zip" download for the specific runtime version/target
  • Unzip it to some location - can be in "runtime" subfolder to your main folder
  • Install the apps as shown above (but really anywhere, it doesn't matter)
  • Run the apps via a "script" - all you need to do is set DOTNET_ROOT=path env. variable to point to the runtime folder and then run the app from that environment. On Windows you may also want to set DOTNET_MULTILEVEL_LOOKUP=0 to make sure the apps will not use the globally installed .NET Core (if there's any).

I know this is not ideal as it requires you to run the app via a script, but it's the only solution we have today.

It's not a self-contained app anymore, but you control the version of the runtime fully - so it's very similar in that sense.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matty-hall picture matty-hall  路  3Comments

omajid picture omajid  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

iCodeWebApps picture iCodeWebApps  路  3Comments

EgorBo picture EgorBo  路  3Comments