homebrew-core icon indicating copy to clipboard operation
homebrew-core copied to clipboard

Building `dotnet` portable

Open miroljub1995 opened this issue 2 months ago • 15 comments

brew gist-logs <formula> link OR brew config AND brew doctor output

% brew gist-logs dotnet
Error: No logs.

% brew config
HOMEBREW_VERSION: 4.6.17
ORIGIN: https://github.com/Homebrew/brew
HEAD: aa278895809b4c079e560fb4fb9bf17e48cf65f1
Last commit: 4 days ago
Branch: stable
Core tap JSON: 14 Oct 08:54 UTC
Core cask tap JSON: 14 Oct 08:54 UTC
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_CASK_OPTS: []
HOMEBREW_FORBID_PACKAGES_FROM_PATHS: set
HOMEBREW_MAKE_JOBS: 12
Homebrew Ruby: 3.4.5 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/3.4.5/bin/ruby
CPU: dodeca-core 64-bit arm_lobos
Clang: 17.0.0 build 1700
Git: 2.39.5 => /Library/Developer/CommandLineTools/usr/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 15.6.1-arm64
CLT: 16.4.0.0.1.1747106510
Xcode: N/A
Rosetta 2: false

% brew doctor
Your system is ready to brew.

Verification

  • [x] My brew doctor output says Your system is ready to brew. and am still able to reproduce my issue.
  • [x] I ran brew update and am still able to reproduce my issue.
  • [x] I have resolved all warnings from brew doctor and that did not fix my problem.
  • [x] I searched for recent similar issues at https://github.com/Homebrew/homebrew-core/issues?q=is%3Aissue and found no duplicates.
  • [x] My issue is not about a failure to build a formula from source.

What were you trying to do (and why)?

I'm trying to build dotnet app in Native AOT and run into issue with linking of openssl and brotli. After comments from this issue, they concluded that it is an issue with how brew builds dotnet.

What happened (include all command output)?

Error:

ld: library not found for -lssl

What did you expect to happen?

Successful linking libs.

Step-by-step reproduction instructions (by running brew commands)

brew install dotnet
dotnet new console -o TestConsoleAOT --aot
dotnet publish TestConsoleAOT

miroljub1995 avatar Oct 17 '25 06:10 miroljub1995

I'm guessing "portable" means "statically link everyting"? Because we don't link anything statically: https://docs.brew.sh/Acceptable-Formulae#shared-vs-static-libraries

SMillerDev avatar Oct 17 '25 08:10 SMillerDev

@jkotas is it statically linked, or just has all dependent libraries with it? I guess the second, but I wanted to confirm.

miroljub1995 avatar Oct 17 '25 08:10 miroljub1995

"Portable" means that the binaries are not tied to specific OS/distro version. It originated on Linux. The main difference is in how dynamic libraries are discovered:

  • The portable Linux builds find the versions of ICU and OpenSSL libraries that are available and loads them dynamically. It allows the same .NET runtime binary to run against both OpenSSL 1.1 and OpenSSL 3.0. The same .NET runtime binary can work on all supported Linux distros.
  • The non-portable builds depend on a specific version of ICU and OpenSSL. The non-portable builds should be built using distro-specific RID to avoid collisions with portable packages that are published by Microsoft.

The non-portable builds were specifically designed to address concerns that the Linux distros had with portable builds. Current version auto-detection of OpenSSL and ICU done by the portable builds is against the Linux distro rules. The non-portable builds are not used or tested much outside the Linux distro builder scenarios.

The problem you have run into is that the defaults attached to -source-build switch are optimized for Linux distro builders, and they turns on the non-portable build. I think you should either drop -source-build when building for macOS; or force the portable build on macOS by /p:PortableBuild=true.

Static vs. dynamic linking is orthogonal to the portable build concept.

jkotas avatar Oct 17 '25 20:10 jkotas

The portable Linux builds find the versions of ICU and OpenSSL libraries that are available and loads them dynamically. It allows the same .NET runtime binary to run against both OpenSSL 1.1 and OpenSSL 3.0. The same .NET runtime binary can work on all supported Linux distros.

Homebrew keeps track of the dependencies though, so that doesn't seem that beneficial.

SMillerDev avatar Oct 18 '25 14:10 SMillerDev

I have a few thoughts here:

  • The software shipped by Homebrew is only intended to be used in the place where it's installed (in Homebrew's Cellar). This is the only scenario that we validate and try to support (ensure that dotnet can find other Homebrew-provided software dependencies, and that other dotnet-based Homebrew packages can find Homebrew-provided dotnet).
    • This is not to say that we actively attempt to make the packages non-portable, but it's not a goal. The binary packages distributed via Homebrew are not intended for redistribution, and the fact that any of them are redistributable is a byproduct.
  • Homebrew's core tap (this repository) does not have any notion of optional dependencies (although the formula spec does support them). In other words, our CI will still only test dotnet against the specific OpenSSL/ICU/etc. at build-time. Therefore, we could not even make any assurances that a portable build would work properly with other dependencies sourced from elsewhere.
    • If a user has multiple OpenSSL or ICU installed (e.g. maybe their system/distro-provided OpenSSL or ICU), sounds like those could get picked instead of the Homebrew-provided dependencies. This makes providing support/troubleshooting more difficult (maintainers saying "I can't reproduce this on my machine" and/or "we can't reproduce the issue in CI"). With library linkage, we rule out this scenario by explicitly stating which libraries to use (via rpath and other mechanisms). It seems that with this portable configuration, we would need to find a different way to enforce this... which also sounds like it defeats the purpose of using a portable build.

The non-portable builds should be built using distro-specific RID to avoid collisions with portable packages that are published by Microsoft.

If you have some guidance available about how to set this during the build process, it sounds like this would be a good thing to do for non-portable builds we have today.

alebcay avatar Oct 18 '25 17:10 alebcay

This is also about app binaries produced on user's machines. dotnet build and dotnet publish commands default to portable RID irrespective of whether .NET runtime was built as portable or not. What happened in https://github.com/dotnet/runtime/issues/120440:

  • dotnet build defaults to producing portable app binary.
  • It sees that there is a local portable runtime package suitable for building the app.
  • This local package is not actually built as portable that results into the build break.

The fixes that I have suggested above will make the local package built as portable to fix the mismatch.

Another option is to set /p:BundleNativeAotCompiler=false on the command line. It will avoid bundling the local copy of the mismatched package. The package will be downloaded from https://www.nuget.org/ instead.

There are certainly other options how to fix the mismatch, but they are going to require more changes and fixes.

If you have some guidance available about how to set this during the build process,

This is untested scenario. I do not think there is a option that you can just set. You would probably need to start with changing the logic in https://github.com/dotnet/dotnet/blob/8762bd921df96f28a567acfb83797c445c7bc06b/eng/common/native/init-distro-rid.sh#L75 . I am not sure whether you want to spend time on trying to fix this.

jkotas avatar Oct 18 '25 17:10 jkotas

I'm still looking into the options you mentioned (portable build or avoid bundling the AOT compiler). Just wanted to mention that in the process of digging, I did see a --rid/--target-rid option in build.sh.

alebcay avatar Oct 19 '25 01:10 alebcay

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

github-actions[bot] avatar Nov 09 '25 12:11 github-actions[bot]

This problem is not related directly to the issue but it might help us move it forward. For some reason rebuilding on x86 Linux fails on the same step:

2025-11-01T06:37:48.9310700Z       crossgen2_publish -> /var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/artifacts/bin/crossgen2_publish/x64/Release/crossgen2.dll
2025-11-01T06:37:48.9310791Z       Generating native code
2025-11-01T06:37:48.9311215Z       crossgen2_publish -> /var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/artifacts/bin/crossgen2_publish/x64/Release/linux-x64/publish/
2025-11-01T06:37:48.9311319Z       Microsoft.NETCore.App.Crossgen2 -> 
2025-11-01T06:37:48.9311414Z       Illegal instruction (core dumped)
2025-11-01T06:37:48.9313742Z     /var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/src/installer/pkg/sfx/Microsoft.NETCore.App/Microsoft.NETCore.App.Crossgen2.sfxproj(53,5): error MSB3073: The command "/var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/artifacts/bin/crossgen2_publish/x64/Release/linux-x64/publish/crossgen2 /var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/artifacts/bin/coreclr/linux.x64.Release/IL/System.Private.CoreLib.dll --out /var/tmp/dotnet-20251101-6867-oxszd/dotnet-9.0.109/src/runtime/artifacts/obj/Microsoft.NETCore.App.Crossgen2/Release/net9.0/linux-x64/S.P.C.tmp" exited with code 132.

See https://github.com/Homebrew/homebrew-core/pull/253272 (https://github.com/Homebrew/homebrew-core/actions/runs/19208400864/job/54911196790?pr=253272#step:4:7777) and https://github.com/Homebrew/homebrew-core/pull/250206 (https://github.com/Homebrew/homebrew-core/actions/runs/18992367534/job/54247469663?pr=250206#step:4:7123)

The error message doesn't show anything useful (even with diag verbosity level), and searching on the internet didn't give promising results. @jkotas, sorry for the ping, but may you (or someone you know) help fixing it?

botantony avatar Nov 10 '25 11:11 botantony

linux x86 port not actively maintained. It is likely that there are multiple things broken.

jkotas avatar Nov 10 '25 15:11 jkotas

linux x86 port not actively maintained. It is likely that there are multiple things broken.

We meant Linux x64, i.e. source-build project's main target platform.

The hardware we run on is GitHub actions' runner (Zen3 ?) or GCP e2-standard-8 (Zen2 ?).

cho-m avatar Nov 10 '25 16:11 cho-m

Can you open the core dump and see where the process crashed?

My guess is that it is likely native toolchain version issue, like the one we just hit with clang 21 (https://github.com/dotnet/runtime/issues/119706).

jkotas avatar Nov 10 '25 17:11 jkotas

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

github-actions[bot] avatar Dec 02 '25 00:12 github-actions[bot]

Unstale

miroljub1995 avatar Dec 02 '25 07:12 miroljub1995

The bot is right, without activity here the issue is stale. I'd recommend helping out on the open PRs instead.

  • https://github.com/Homebrew/homebrew-core/pull/250206
  • https://github.com/Homebrew/homebrew-core/pull/254079

SMillerDev avatar Dec 02 '25 12:12 SMillerDev