keg: skip linking some symlinks that point to other kegs - #23558
Merged
Conversation
Specifically symlinks that point to another keg via the opt path using the same relative path. These would have previously been a conflict linking the same file. This can be used by split formulae to help resolve the path of relative files, e.g. Flang expecting LLVM's LTO library at `../lib/<libname>`. We've used other workarounds before like reversing the symlink so we resolve the path via Homebrew prefix; however, this is less ideal as the wrong file can be found if a user runs brew unlink/link. Also has lower security as an unrelated formula could inject something into the same location unlike our sandboxed kegs. This change does not allow symlink to Cellar path as it is fragile and should never be used in formulae.
7 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates Keg#link to skip linking certain symlinks that would otherwise conflict when they point (via HOMEBREW_PREFIX/opt) to a file that’s already linked from another keg at the same relative path, enabling split-formula layouts (e.g. LLVM/Flang) without fragile Cellar-path symlinks.
Changes:
- Teach
Keg#link_dirto prune entries when the source resolves to anopt-based path at the same relative location. - Extend
keg_specwith coverage for “same relative path” vs “different relative path” and a Cellar-path conflict case. - Adjust the test keg helper to support suffixing filenames to avoid cross-keg link conflicts in specs.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| Library/Homebrew/keg.rb | Adds pruning logic in link_dir to skip linking certain opt-based symlinks that would otherwise conflict. |
| Library/Homebrew/test/keg_spec.rb | Adds regression tests for cross-keg opt symlink handling and updates the test helper to support unique filenames. |
Suppressed comments (2)
Library/Homebrew/keg.rb:822
- This prune rule is intended to apply only to symlinks pointing into another keg via
HOMEBREW_PREFIX/opt, but the current check will also match a symlink pointing to this keg’s ownopt_record. That would skip linking a file that may not otherwise exist at the destination. Restrict this to symlinks and exclude the current keg’sopt_record.
# Skip symlinks where the source is located in another keg at the same
# relative path. Split formulae (e.g. llvm + flang) can use these when
# their binaries need to find files at a specific relative path.
Find.prune if resolved_src.fnmatch?("#{HOMEBREW_PREFIX}/opt/*/#{relative_src}", File::FNM_PATHNAME)
Library/Homebrew/test/keg_spec.rb:225
- Same as above: normalise
resolved_pathbefore comparing it to an absolute path, otherwise..segments in the joined path can make the equality check fail.
keg.link
expect((HOMEBREW_PREFIX/"lib"/filename2).resolved_path).to eq keg/"lib"/filename2
end
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Specifically symlinks that point to another keg via the opt path using the same relative path. These would have previously been a conflict linking the same file.
This can be used by split formulae to help resolve the path of relative files, e.g. Flang expecting LLVM's LTO library at
../lib/<libname>.We've used other workarounds before like reversing the symlink so we resolve the path via Homebrew prefix; however, this is less ideal as the wrong file can be found if a user runs brew unlink/link. Also has lower security as an unrelated formula could inject something into the same location unlike our sandboxed kegs.
This change does not allow symlink to Cellar path as it is fragile and should never be used in formulae.
Mainly want this for the LLVM case as I've been working on LLVM 23 update and am trying to fix up some LLVM <-> Flang interaction.
-lto_library. This works, but can't remove the default arg so there is always going to be a warning on invalid pathFlang keg --> Homebrew prefix --> LLVM keg. The problem here isbrew unlink llvmwill break it and even worse isbrew unlink llvm && brew link llvm@20would cause wrong LTO library to be found as path is unversioned.There are other scenarios like improving
qtformula layout to work even when userbrew unlinks Qt6 formulae.brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?