Skip to content

autoremove: allow removing source-built formulae - #24471

Open
HaraldNordgren wants to merge 2 commits into
mainfrom
autoremove-built-from-source
Open

HaraldNordgren wants to merge 2 commits into
mainfrom
autoremove-built-from-source

Conversation

@HaraldNordgren

@HaraldNordgren HaraldNordgren commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

On my macOS 12 machine, brew autoremove can't clean up anything. brew leaves says nothing needs makedepend, but autoremove keeps it:

$ brew leaves --installed-as-dependency | grep makedepend
makedepend
$ brew autoremove --dry-run
$

Since #17508, autoremove keeps every formula that was built from source because it may take a while to install again. 88 of my 194 formulae were built from source, so unused ones pile up. Three of them (googletest, jansson and libmagic) came from a clamav build I stopped partway through.

Setting HOMEBREW_AUTOREMOVE_BUILT_FROM_SOURCE lets autoremove remove them:

$ HOMEBREW_AUTOREMOVE_BUILT_FROM_SOURCE=1 brew autoremove --dry-run
==> Would autoremove 7 unneeded formulae:
googletest
jansson
libassuan
libmagic
makedepend
pinentry
swig

It's opt-in because of #17433, and once set it also applies to the automatic runs from brew cleanup and brew uninstall. It only overrides the rule that keeps the formula itself, so build dependencies of the remaining source-built formulae stay. Here that removes 7 formulae (22M) and keeps cmake and ghc, whereas dropping that protection too would remove 25 (2.5G).

Without the variable, an explicit brew autoremove now lists what it's keeping and names the variable, so the empty output no longer looks like a bug. A warning like this was agreed in #13831 but never landed. Automatic runs stay quiet.

#13831 argued against adding more options, but since #17508 there's no other way to have autoremove clean up after source builds.


  • Have you followed our Contributing guidelines?
  • 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 autoremove kept these formulae, read the #13831 and #17508 history, wrote the change and its specs and verified them. The new specs fail without the change and pass with it. It ran brew autoremove from this branch on my real install, first with --dry-run with and without the variable, then for real with the variable, which removed the 7 formulae above. It also ran brew lgtm, which passes. env_config.rbi was regenerated with tapioca dsl Homebrew::EnvConfig.


- `brew autoremove` keeps every unused formula built from source, so on
  machines where most formulae are built from source it finds nothing.
- Add `HOMEBREW_AUTOREMOVE_BUILT_FROM_SOURCE` to remove them anyway.
  Build dependencies of the remaining source builds are still kept.
- Warn about the kept formulae when `brew autoremove` is run explicitly,
  as agreed in #13831, so the empty output doesn't look like a bug.
@HaraldNordgren HaraldNordgren changed the title autoremove: warn about formulae built from source autoremove: allow removing source-built formulae Nov 4, 2026
@HaraldNordgren
HaraldNordgren force-pushed the autoremove-built-from-source branch from 1418d7c to 4666d39 Compare November 4, 2026 09:51
@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Summary

Summary by CodeRabbit

  • New Features
    • brew autoremove now warns when unused formulae built from source are kept and explains how to include them in removal.
    • Set HOMEBREW_AUTOREMOVE_BUILT_FROM_SOURCE=1 to remove unused formulae built from source with brew autoremove, brew cleanup and brew uninstall. Build dependencies needed by remaining source-built formulae are retained.
  • Documentation
    • Added details about this setting to the environment-variable documentation.

Autoremove candidate selection now supports a configurable policy for formulae built from source. Cleanup can report candidates excluded by that policy. The environment variable, command integration, tests, and documentation cover this behaviour.

Changes: Source-Built Formula Autoremove

Layer / File(s) Summary
Source-built candidate policy
Library/Homebrew/utils/autoremove.rb, Library/Homebrew/test/utils/autoremove_spec.rb
Autoremove utility methods accept keep_built_from_source: and pass it through recursive candidate searches. Tests cover formula filtering when the option is false.
Cleanup configuration and warning
Library/Homebrew/cleanup.rb, Library/Homebrew/cmd/autoremove.rb, Library/Homebrew/env_config.rb, Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi, Library/Homebrew/test/cleanup_spec.rb, docs/Manpage.md, manpages/brew.1
Cleanup calculates candidates using the configured source-build policy and can report candidates excluded by that policy. The autoremove command enables the warning. Tests and documentation describe the environment variable.
Merge Risk: Low · up to f64ff

The opt-in can remove build dependencies that users expect to keep. These bounded issues should be fixed or accepted before merging.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely summarises the main change: allowing autoremove to remove formulae built from source.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description directly explains the autoremove changes, the new environment variable, the source-built formula behaviour, and the related tests and commands.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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: 51336083-0b56-4ead-a1b6-1225b8ad123f
Commits

Reviewing files that changed from the base of the PR and between b090520 and 1418d7c.

Files selected for processing: 9
  • Library/Homebrew/cleanup.rb
  • Library/Homebrew/cmd/autoremove.rb
  • Library/Homebrew/env_config.rb
  • Library/Homebrew/sorbet/rbi/dsl/homebrew/env_config.rbi
  • Library/Homebrew/test/cleanup_spec.rb
  • Library/Homebrew/test/utils/autoremove_spec.rb
  • Library/Homebrew/utils/autoremove.rb
  • docs/Manpage.md
  • manpages/brew.1

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread Library/Homebrew/cleanup.rb Resolved
- The warning lists everything the variable would remove, including
  bottled formulae only needed by a formula built from source.
- Pin that so the list stays in step with what gets removed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (2)

Minor · Keep build dependencies of source-built formulae that casks require. · autoremove.rb:18-19

Library/Homebrew/utils/autoremove.rb:18-19
Minor · Functional Correctness · Quick win

Keep build dependencies of source-built formulae that casks require.

When HOMEBREW_AUTOREMOVE_BUILT_FROM_SOURCE is set, a source-built formula that a cask requires can still be selected as unused. The recursive pass then removes it from its input, so its build dependencies are no longer protected. The later cask filter keeps that formula. It does not restore protection for its build dependencies. The InstalledDependents check after selection reads runtime dependencies only, so it does not protect build-only edges either.

The installed formula keeps working. A later rebuild from source may fail or require reinstalling the removed build dependency.

Apply the cask check during candidate selection. The protected formula then stays in the input, and its build dependencies stay protected.

Suggested fix
       def removable_formulae(formulae, casks, keep_built_from_source: true)
-        unused_formulae = unused_formulae_with_no_formula_dependents(formulae, keep_built_from_source:)
         cask_dep_names = cask_dependent_formula_names(casks, formulae)
-        unused_formulae.reject { |f| cask_dep_names.intersect?(f.possible_names) }
+        unused_formulae_with_no_formula_dependents(formulae, keep_built_from_source:, protected_names: cask_dep_names)
       end
-      sig { params(formulae: T::Array[Formula], keep_built_from_source: T::Boolean).returns(T::Array[Formula]) }
-      def unused_formulae_with_no_formula_dependents(formulae, keep_built_from_source: true)
+      sig {
+        params(formulae: T::Array[Formula], keep_built_from_source: T::Boolean,
+               protected_names: T::Set[String]).returns(T::Array[Formula])
+      }
+      def unused_formulae_with_no_formula_dependents(formulae, keep_built_from_source: true, protected_names: Set.new)
         unused_formulae = bottled_formulae_with_no_formula_dependents(formulae, keep_built_from_source:).select do |f|
           tab = f.any_installed_keg&.tab
           next unless tab
           next unless tab.installed_on_request_present?
+          next if protected_names.intersect?(f.possible_names)
 
           tab.installed_on_request == false
         end
 
         unless unused_formulae.empty?
-          unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae,
-                                                                        keep_built_from_source:)
+          unused_formulae += unused_formulae_with_no_formula_dependents(formulae - unused_formulae,
+                                                                        keep_built_from_source:,
+                                                                        protected_names:)
         end
Minor · Qualify the build-dependency guarantee for… · env_config.rb:107-112

Library/Homebrew/env_config.rb:107-112
Minor · Functional Correctness · Quick win

Qualify the build-dependency guarantee for HOMEBREW_NO_CLEANUP_FORMULAE.

The description says that build dependencies of remaining source-built formulae are kept. That does not hold for formulae listed in HOMEBREW_NO_CLEANUP_FORMULAE, or for their installed runtime dependencies. Autoremove removes those formulae from its input before it selects candidates. Their build dependencies then lose that protection, and they can be removed unless another rule keeps them. Reinstalling a removed source-built build dependency can be slow, which the PR is meant to avoid.

Suggested fix
         description: "If set, `brew autoremove`, `brew cleanup` and `brew uninstall` will also remove unused " \
                      "formulae that were built from source. Build dependencies of the remaining formulae that " \
-                     "were built from source are still kept.",
+                     "were built from source are still kept. This rule does not keep build dependencies of " \
+                     "formulae listed in `HOMEBREW_NO_CLEANUP_FORMULAE`, or of their installed runtime " \
+                     "dependencies. Other rules can still keep them.",
         boolean:     true,

Regenerate manpages/brew.1 and docs/Manpage.md from the same description text. The docs/Manpage.md body was not inspected, so this change is not shown as a diff for that file.

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: 9f04e501-a62c-48f9-af7f-b42f41edd6ee
Commits

Reviewing files that changed from the base of the PR and between 4666d39 and f64ffd9.

Files selected for processing: 1
  • Library/Homebrew/test/cleanup_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.

Comment on lines +108 to +110
description: "If set, `brew autoremove`, `brew cleanup` and `brew uninstall` will also remove unused " \
"formulae that were built from source. Build dependencies of the remaining formulae that " \
"were built from source are still kept.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would rather we reconsider the default rather than adding a new environment variable. Is it "safer" than it was before now, yeh? What cases are now better covered than before?

Thanks!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants