autoremove: don't remove formulae that were built from source

When a formula was built from source, it should not be removed by
`brew autoremove` as it will take a while to be installed again.

Fixes https://github.com/Homebrew/brew/issues/17433
This commit is contained in:
Mike McQuaid 2024-06-14 17:26:28 +01:00
parent 754c026dd4
commit a883f14b72
No known key found for this signature in database
3 changed files with 26 additions and 16 deletions

View File

@ -11,13 +11,18 @@ RSpec.describe Homebrew::Cmd::Autoremove do
let(:unused_formula) { Formula["testball2"] }
before do
# Make testball1 poured from a bottle
install_test_formula "testball1"
install_test_formula "testball2"
tab = Tab.for_name("testball1")
tab.poured_from_bottle = true
tab.write
# Make testball2 an unused dependency
# Make testball2 poured from a bottle and an unused dependency
install_test_formula "testball2"
tab = Tab.for_name("testball2")
tab.installed_on_request = false
tab.installed_as_dependency = true
tab.poured_from_bottle = true
tab.write
end

View File

@ -61,7 +61,7 @@ RSpec.describe Utils::Autoremove do
end
end
describe "::formulae_with_no_formula_dependents" do
describe "::bottled_formulae_with_no_formula_dependents" do
include_context "with formulae for dependency testing"
before do
@ -72,17 +72,17 @@ RSpec.describe Utils::Autoremove do
it "filters out runtime dependencies" do
allow(tab_from_keg).to receive(:poured_from_bottle).and_return(true)
expect(described_class.send(:formulae_with_no_formula_dependents, formulae))
expect(described_class.send(:bottled_formulae_with_no_formula_dependents, formulae))
.to eq([formula_with_deps, formula_is_build_dep])
end
end
context "when formulae are built from source" do
it "filters out runtime and build dependencies" do
it "filters out formulae" do
allow(tab_from_keg).to receive(:poured_from_bottle).and_return(false)
expect(described_class.send(:formulae_with_no_formula_dependents, formulae))
.to eq([formula_with_deps])
expect(described_class.send(:bottled_formulae_with_no_formula_dependents, formulae))
.to eq([])
end
end
end

View File

@ -29,26 +29,31 @@ module Utils
.flat_map { |f| [f, *f.runtime_formula_dependencies].compact }
end
# An array of all installed {Formula} without runtime {Formula}
# An array of all installed bottled {Formula} without runtime {Formula}
# dependents for bottles and without build {Formula} dependents
# for those built from source.
# @private
sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def formulae_with_no_formula_dependents(formulae)
dependents = T.let([], T::Array[Formula])
def bottled_formulae_with_no_formula_dependents(formulae)
formulae_to_keep = T.let([], T::Array[Formula])
formulae.each do |formula|
dependents += formula.runtime_formula_dependencies
formulae_to_keep += formula.runtime_formula_dependencies
# Ignore build dependencies when the formula is a bottle
next if formula.any_installed_keg&.tab&.poured_from_bottle
if (tab = formula.any_installed_keg&.tab)
# Ignore build dependencies when the formula is a bottle
next if tab.poured_from_bottle
# Keep the formula if it was built from source
formulae_to_keep << formula
end
formula.deps.select(&:build?).each do |dep|
dependents << dep.to_formula
formulae_to_keep << dep.to_formula
rescue FormulaUnavailableError
# do nothing
end
end
formulae - dependents
formulae - formulae_to_keep
end
# Recursive function that returns an array of {Formula} without
@ -56,7 +61,7 @@ module Utils
# @private
sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def unused_formulae_with_no_formula_dependents(formulae)
unused_formulae = formulae_with_no_formula_dependents(formulae).reject do |f|
unused_formulae = bottled_formulae_with_no_formula_dependents(formulae).reject do |f|
f.any_installed_keg&.tab&.installed_on_request
end