Page MenuHomeFreeBSD

lang/dotnet: Configure install location
Needs ReviewPublic

Authored by zirias on Mar 30 2024, 9:24 AM.
Tags
None
Referenced Files
Unknown Object (File)
Tue, Apr 30, 3:03 PM
Unknown Object (File)
Mon, Apr 29, 11:58 PM
Unknown Object (File)
Fri, Apr 26, 4:34 AM
Unknown Object (File)
Wed, Apr 24, 4:20 PM
Unknown Object (File)
Fri, Apr 19, 8:16 PM
Unknown Object (File)
Fri, Apr 5, 12:57 AM
Unknown Object (File)
Thu, Apr 4, 10:59 PM
Unknown Object (File)
Apr 2 2024, 8:01 PM
Subscribers
None

Details

Reviewers
arrowd
Summary

Add a post-install script to create /etc/dotnet/install_location if it
doesn't exist already. The location of this file is baked into .NET
applications compiled to IL code without any options like "ReadyToRun".
It's necessary for them to find the dotnet runtime without relying on an
environment variable.

Upstream PR: https://github.com/dotnet/runtime/pull/100731

Diff Detail

Repository
R11 FreeBSD ports repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 56835
Build 53723: arc lint + arc unit

Event Timeline

zirias created this revision.

Can you please elaborate a bit on what problem does this change solve?

Can you please elaborate a bit on what problem does this change solve?

Not exactly sure what you mean, it solves what's in the commit description: Being able to run "plain IL" dotnet executables without any further fiddling.

You might also refer to https://learn.microsoft.com/en-us/dotnet/core/distribution-packaging (remark (16))

Do you have concrete steps that would allow to observe the problem when that file is missing?

You'll just need some dotnet software built without using options like PublishReadyToRun, PublishAOT, PublishSingleFile ...

I can easily reproduce it with PowerShell (which I intend to port based on your work) ... a plain dotnet build creates plain IL without the above mentioned options. dotnet run works fine of course, but directly invoking the compiled pwsh binary fails with an error message that it can't find the .NET runtime. Note this probably won't be relevant for a port I might create ... I'll experiment with PublishReadyToRun, so powershell won't have a hard dependency on dotnet. But anyways, people installing this port probably expect being able to run compiled dotnet software.

Adding that file is all that's needed for dotnet projects compiled to plain IL to find the runtime. According to MS it is necessary as soon as dotnet is installed anywhere else than /usr/share/dotnet.

Just tried, can be reproduced with the simplest possible helloworld project:

$ mkdir test
$ cd test
$ dotnet new console
[...]
$ dotnet build
[...]
$ dotnet run
Hello, World!
$ bin/Debug/net8.0/test
You must install .NET to run this application.
[...]

After adding the config file:

$ bin/Debug/net8.0/test
Hello, World!

In FreeBSD the $LOCALBASE directory plays role of /usr, so from the FreeBSD perspective .NET is installed into a standard location and no pointer files are needed.

I guess, something should be patched inside the .NET runtime, I'll take a look.

can be reproduced with the simplest possible helloworld project:

Great, this is exactly what I asked for, many thanks! I'll play with runtime to make this testcase to work out of the box.

can be reproduced with the simplest possible helloworld project:

Great, this is exactly what I asked for, many thanks! I'll play with runtime to make this testcase to work out of the box.

This won't help when users download some binary (IL) software and try to run it, it will have /usr/share/dotnet and this config file "baked in", so I really think just adding this file is the portable solution.

Btw, the "quasi standard" location would still be ${LOCALBASE}/share/dotnet, but this won't help either, see above.

This won't help when users download some binary (IL) software and try to run it, it will have /usr/share/dotnet and this config file "baked in", so I really think just adding this file is the portable solution.

Just realized that isn't an option anyways as these always have some native code stub that probably won't work on FreeBSD anyways, so sorry for this brainfart. Still patching is extra maintenance work, but of course your decision ;)

Ooh, you're right.

Maybe we can at least persuade upstream to look for $LOCALBASE/etc/dotnet/install_location ... I'm reluctant to put this file into /etc

Maybe we can at least persuade upstream to look for $LOCALBASE/etc/dotnet/install_location

The thing is that this won't solve anything for precompiled stuff, but see my comment above: Probably pointless anyways because anything precompiled will always have some native stub code not built for FreeBSD...

... I'm reluctant to put this file into /etc

Yes, I don't really like this either. We could just use the @sample mechanism in ${LOCALBASE}/etc, also for my next review if dotnet is looking there for machine-wide nuget configs as well ... would be really nice!

Just realized that isn't an option anyways as these always have some native code stub that probably won't work on FreeBSD anyways, so sorry for this brainfart.

Hum, aren't compilation products of .NET intended to be cross-platform?

Hum, aren't compilation products of .NET intended to be cross-platform?

Yes. But there's always some "startup binary" added. If you compile this super simple "helloworld" project, you'll see output files test.dll (pure IL) and the test stub binary looking for the dotnet runtime to launch test.dll. If I understand it correctly, it's this binary that has the paths to look for the runtime baked in, but this one is platform-specific anyways.

So, your plan to solve this issue by patching is probably indeed the best. I didn't think about that earlier. Maybe I should have another look and re-create this whole review stack :-o

With our Linuxulator technology we might be actually able to run both Linux and FreeBSD binaries. I'll look into that.

With our Linuxulator technology we might be actually able to run both Linux and FreeBSD binaries. I'll look into that.

Thanks! I just looked at what this startup binary is doing by default .. in a nutshell looking for the dotnet runtime first in its own directory, then trying to read the config file, finally in the default location:

__sysctl("kern.proc.pathname.-1",4,0xf063429faf0,0xf063429fae8,0x0,0) = 0 (0x0)
__realpathat(AT_FDCWD,"/tmp/test/bin/Release/net8.0/publish/test","/tmp/test/bin/Release/net8.0/publish/test",1024,0) = 0 (0x0)
__realpathat(AT_FDCWD,"/tmp/test/bin/Release/net8.0/publish/test.dll","/tmp/test/bin/Release/net8.0/publish/test.dll",1024,0) = 0 (0x0)
access("/tmp/test/bin/Release/net8.0/publish/libhostfxr.so",F_OK) ERR#2 'No such file or directory'
open("/etc/dotnet/install_location_x64",O_RDONLY,0666) ERR#2 'No such file or directory'
open("/etc/dotnet/install_location",O_RDONLY,0666) ERR#2 'No such file or directory'
access("/usr/share/dotnet/host/fxr",F_OK)        ERR#2 'No such file or directory'

Yes, it might be possible that prebuilt dotnet projects with a Linux startup binary doing exactly that would run on FreeBSD, given that config file is present. I also agree it's fishy to add something to the base /etc from a port.

The only way to prevent this "search the framework" is a "self-contained" build, bloating this hello world to a total of 65MiB: dotnet publish -r freebsd-x64 -c Release --self-contained -p:PublishReadyToRun=true

Please let me know if I can help any further. My ultimate goal is to create a somewhat sane port of PowerShell (so please see the big picture also including at least the next review ... some features like self-contained ReadyToRun require FreeBSD-specific NuGet packages, and I think for the PowerShell port, this "bloat" would make more sense than a hard dependency on the full dotnet ...)

My PowerShell port seems to finally work: https://people.freebsd.org/~zirias/patches/0001-shells-powershell-Add-new-port.patch
Maybe it's a nice testcase here.

BTW, upstream lists SDK version 8.0.101, which I had to patch for building it.

Regarding the upstream PR, that certainly is the path to a "correct" solution! Two remarks though:

  • It will expect dotnet installed in ${LOCALBASE}/share/dotnet while the port currently installs to ${LOCALBASE}/dotnet, is this intentional?
  • There's no way it can fix this for dotnet projects already built without that change (nothing can be done about that I guess, just mentioning ...)
  • It will expect dotnet installed in ${LOCALBASE}/share/dotnet while the port currently installs to ${LOCALBASE}/dotnet, is this intentional?

Hmm, I was just following other cases around that code. Maybe it is our port that should be changed to install into share?

  • It will expect dotnet installed in ${LOCALBASE}/share/dotnet while the port currently installs to ${LOCALBASE}/dotnet, is this intentional?

Hmm, I was just following other cases around that code. Maybe it is our port that should be changed to install into share?

I'd agree with that, it's more in line with what upstream does so far! Just wanted to make sure it was intentional ;)

It'd be great if you do that (and maybe also update the port to 8.0.3)