From a883f14b72adfee42a57871e84e4cbad75f37186 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Fri, 14 Jun 2024 17:26:28 +0100 Subject: [PATCH] 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 --- Library/Homebrew/test/cmd/autoremove_spec.rb | 9 ++++++-- .../Homebrew/test/utils/autoremove_spec.rb | 10 ++++---- Library/Homebrew/utils/autoremove.rb | 23 +++++++++++-------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Library/Homebrew/test/cmd/autoremove_spec.rb b/Library/Homebrew/test/cmd/autoremove_spec.rb index db0a80e621..07b2981210 100644 --- a/Library/Homebrew/test/cmd/autoremove_spec.rb +++ b/Library/Homebrew/test/cmd/autoremove_spec.rb @@ -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 diff --git a/Library/Homebrew/test/utils/autoremove_spec.rb b/Library/Homebrew/test/utils/autoremove_spec.rb index da67a09c0d..d1be63a7dd 100644 --- a/Library/Homebrew/test/utils/autoremove_spec.rb +++ b/Library/Homebrew/test/utils/autoremove_spec.rb @@ -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 diff --git a/Library/Homebrew/utils/autoremove.rb b/Library/Homebrew/utils/autoremove.rb index 5b14c8d505..b84d99bb16 100644 --- a/Library/Homebrew/utils/autoremove.rb +++ b/Library/Homebrew/utils/autoremove.rb @@ -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