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"] } let(:unused_formula) { Formula["testball2"] }
before do before do
# Make testball1 poured from a bottle
install_test_formula "testball1" 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 = Tab.for_name("testball2")
tab.installed_on_request = false tab.installed_on_request = false
tab.installed_as_dependency = true tab.installed_as_dependency = true
tab.poured_from_bottle = true
tab.write tab.write
end end

View File

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

View File

@ -29,26 +29,31 @@ module Utils
.flat_map { |f| [f, *f.runtime_formula_dependencies].compact } .flat_map { |f| [f, *f.runtime_formula_dependencies].compact }
end 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 # dependents for bottles and without build {Formula} dependents
# for those built from source. # for those built from source.
# @private # @private
sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) } sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def formulae_with_no_formula_dependents(formulae) def bottled_formulae_with_no_formula_dependents(formulae)
dependents = T.let([], T::Array[Formula]) formulae_to_keep = T.let([], T::Array[Formula])
formulae.each do |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 if (tab = formula.any_installed_keg&.tab)
next if formula.any_installed_keg&.tab&.poured_from_bottle # 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| formula.deps.select(&:build?).each do |dep|
dependents << dep.to_formula formulae_to_keep << dep.to_formula
rescue FormulaUnavailableError rescue FormulaUnavailableError
# do nothing # do nothing
end end
end end
formulae - dependents formulae - formulae_to_keep
end end
# Recursive function that returns an array of {Formula} without # Recursive function that returns an array of {Formula} without
@ -56,7 +61,7 @@ module Utils
# @private # @private
sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) } sig { params(formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def unused_formulae_with_no_formula_dependents(formulae) 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 f.any_installed_keg&.tab&.installed_on_request
end end