You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Upgrade fewer dependencies when building from source - #24503
On my MacBook Air (macOS 12.7.6, so few bottles), brew upgrade --dry-run ruby wanted to rebuild rust and llvm from source because of a new ca-certificates release:
rust 1.96.1 has a matching compatibility_version, so #24273 should leave it at its installed version. With ca-certificates current it does, and none of those dependencies are upgraded. Two separate bugs cause this, so there's one commit for each.
A harmless upgrade forces an outdated dependency to upgrade. brew upgrades an outdated dependency if anything in its receipt has an incompatible upgrade, and rust's receipt lists ca-certificates. Poured kegs never record a compatibility_version, so every ca-certificates release counts as incompatible. An upgrade no longer counts when the formula has an :all bottle, its keg has no include directory and its receipt records no compatibility_version. An :all bottle has no compiled code to link against, and none of the 1,119 in homebrew/core has a compatibility_version above 1. The include check rules out header-only libraries like eigen, whose code is compiled into their dependents. ca-certificates itself still upgrades, so certificates stay current.
Compatible dependencies behind an upgraded one are upgraded anyway. That's why cmake, sqlite and expat are in the plan. When rust has to upgrade, brew plans again, and later passes could only leave a dependency alone if an earlier pass had. An outdated dependency left alone hides everything behind it, so these were first reached on a later pass. This now tracks the dependencies that must be upgraded instead. The number of passes is still bounded, now by every dependency reachable with the selected options, because more can be left alone on later passes.
rust and openssl@4, with a compatibility_version bump on openssl@4
openssl@4, cmake, lz4, sqlite, expat, llvm, rust
openssl@4, lz4, llvm, rust
Across my 107 outdated formulae, the only other plan that changes is ripgrep, which stops upgrading ruby when ca-certificates is outdated.
This is the part of #24496 that doesn't need any new DSL. brew upgrade rust itself still upgrades llvm, because rust links it and their compatibility_versions differ. --build-bottle and pouring bottles are unchanged.
Have you checked for other open Pull Requests for the same change?
Have you explained what your changes do? Performance claims (e.g. "this is faster") must include brew benchmark results.
Have you explained why you'd like these changes included, not just what they do?
For bug fixes, have you given step-by-step brew commands to reproduce the bug?
Have you written new tests (excluding integration tests)? Here's an example.
Have you successfully run brew lgtm (style, typechecking and tests) locally?
I did not use AI/LLM to create this PR, or I disclosed the tool/model below and reviewed its output; I did not attribute commits to AI and will answer maintainer questions and review comments myself without AI/LLM.
Claude Code (Claude Opus 5.5) traced why ca-certificates pulled in rust and llvm, wrote the changes and their specs and verified them. Each new spec fails when the code it covers is broken. It compared plans between main and this branch against my real kegs and ran brew lgtm, which passes.
No actionable comments were generated in the recent review.
Summary
Summary by CodeRabbit
Bug Fixes
Improved dependency upgrades when updating Ruby, avoiding unnecessary upgrades of bottled dependencies that remain compatible.
Dependencies are upgraded when recorded compatibility information, available bottles, or build requirements indicate they need updating.
Improved handling of nested and optional build dependencies, so they are upgraded when they rely on a dependency that is also being upgraded.
The formula installer now repeats dependency expansion when kept dependencies have recorded runtime dependencies that intersect incompatible upgrades. It excludes an :all bottle from that check when no installed headers or recorded compatibility version exist. Tests cover bottle, receipt, header, and optional build-dependency cases.
The installer tracks dependencies that cannot be kept across expansion passes. It checks reachable dependencies for compatibility-version mismatches, with an exception for an :all bottle that has no installed headers or recorded compatibility version. Tests cover upgrade outcomes for receipts, headers, bottles, nested runtime dependencies, and selected optional build dependencies.
Merge Risk: Low · up to 252a9
The change reduces unnecessary dependency upgrades when building from source. In rare cases with several installed versions, a stale receipt could still trigger an extra upgrade. This is low risk and a bounded follow-up.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title summarises the reduction in dependency upgrades for source builds.
Description check
The description explains both dependency upgrade changes and their expected effects.
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Recent review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: 34ab50b0-e2b3-4651-8830-49b12856bdfd
Commits
Reviewing files that changed from the base of the PR and between fbd2e79 and 252a90c.
Files selected for processing: 2
Library/Homebrew/formula_installer.rb
Library/Homebrew/test/formula_installer_spec.rb
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Organization UI
Review profile: CHILL
Plan: Advanced
Run ID: 5d8d1fb8-64c9-4ede-8cc7-6f1e086c2d9f
Commits
Reviewing files that changed from the base of the PR and between 970da0c and fbd2e79.
Files selected for processing: 2
Library/Homebrew/formula_installer.rb
Library/Homebrew/test/formula_installer_spec.rb
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
- An outdated dependency was upgraded if anything in its receipt had
an incompatible upgrade, however indirectly
- Poured kegs never record a `compatibility_version`, so every
`ca-certificates` release counted as incompatible
- That forced an unnecessary `rust` and `llvm` upgrade when upgrading
`ruby` from source
- A formula with an `:all` bottle has no compiled code to link against
and a keg without headers has nothing to compile against, so an
empty receipt isn't a reason to treat that upgrade as incompatible
- A receipt recording a different `compatibility_version` still counts
- An outdated dependency left as it is hides the dependencies behind
it, so if it later has to be upgraded they're only seen on a new pass
- Later passes only left dependencies alone if an earlier pass had, so
these were upgraded despite a matching `compatibility_version`
- Track the dependencies that must be upgraded instead, so ones first
reached on a later pass can still be left alone
- Bound the passes by every dependency reachable with the selected
options, as more can be left alone on later passes
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
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.
On my MacBook Air (macOS 12.7.6, so few bottles),
brew upgrade --dry-run rubywanted to rebuildrustandllvmfrom source because of a newca-certificatesrelease:rust1.96.1 has a matchingcompatibility_version, so #24273 should leave it at its installed version. Withca-certificatescurrent it does, and none of those dependencies are upgraded. Two separate bugs cause this, so there's one commit for each.A harmless upgrade forces an outdated dependency to upgrade. brew upgrades an outdated dependency if anything in its receipt has an incompatible upgrade, and
rust's receipt listsca-certificates. Poured kegs never record acompatibility_version, so everyca-certificatesrelease counts as incompatible. An upgrade no longer counts when the formula has an:allbottle, its keg has noincludedirectory and its receipt records nocompatibility_version. An:allbottle has no compiled code to link against, and none of the 1,119 inhomebrew/corehas acompatibility_versionabove 1. Theincludecheck rules out header-only libraries likeeigen, whose code is compiled into their dependents.ca-certificatesitself still upgrades, so certificates stay current.Compatible dependencies behind an upgraded one are upgraded anyway. That's why
cmake,sqliteandexpatare in the plan. Whenrusthas to upgrade, brew plans again, and later passes could only leave a dependency alone if an earlier pass had. An outdated dependency left alone hides everything behind it, so these were first reached on a later pass. This now tracks the dependencies that must be upgraded instead. The number of passes is still bounded, now by every dependency reachable with the selected options, because more can be left alone on later passes.Plans for
rubyon main and this branch:rustandca-certificatesca-certificates,cmake,lz4,sqlite,expat,llvm,rustca-certificatesrustandopenssl@4, with acompatibility_versionbump onopenssl@4openssl@4,cmake,lz4,sqlite,expat,llvm,rustopenssl@4,lz4,llvm,rustAcross my 107 outdated formulae, the only other plan that changes is
ripgrep, which stops upgradingrubywhenca-certificatesis outdated.This is the part of #24496 that doesn't need any new DSL.
brew upgrade rustitself still upgradesllvm, becauserustlinks it and theircompatibility_versions differ.--build-bottleand pouring bottles are unchanged.brew benchmarkresults.brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?Claude Code (Claude Opus 5.5) traced why
ca-certificatespulled inrustandllvm, wrote the changes and their specs and verified them. Each new spec fails when the code it covers is broken. It compared plans between main and this branch against my real kegs and ranbrew lgtm, which passes.