diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index f9cf56f4442d4..2c7be04be581e 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -76,7 +76,7 @@ def uninstall end def check_for_dependents(kegs) - return false unless result = find_some_installed_dependents(kegs) + return false unless result = Keg.find_some_installed_dependents(kegs) requireds, dependents = result @@ -108,8 +108,16 @@ def find_some_installed_dependents(kegs) remaining_formulae = Formula.installed.select { |f| f.installed_kegs.any? { |k| Tab.for_keg(k).runtime_dependencies.nil? } } - Diagnostic.missing_deps(remaining_formulae, kegs.map(&:name)) do |dependent, required| - kegs_by_name = kegs.group_by(&:to_formula) + + keg_names = kegs.map(&:name) + kegs_by_name = kegs.group_by(&:to_formula) + remaining_formulae.each do |dependent| + required = dependent.missing_dependencies(hide: keg_names) + required.select! do |f| + kegs_by_name.key?(f) + end + next unless required.any? + required_kegs = required.map { |f| kegs_by_name[f].sort_by(&:version).last } return required_kegs, [dependent] end diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 16717ea4eda63..d86236037bebe 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -87,6 +87,41 @@ def to_s; <<-EOS.undent mime-info pixmaps sounds postgresql ].freeze + # Will return some kegs, and some dependencies, if they're present. + # For efficiency, we don't bother trying to get complete data. + def self.find_some_installed_dependents(kegs) + # First, check in the tabs of installed Formulae. + kegs.each do |keg| + dependents = keg.installed_dependents - kegs + dependents.map! { |d| "#{d.name} #{d.version}" } + return [keg], dependents if dependents.any? + end + + # Some kegs won't have modern Tabs with the dependencies listed. + # In this case, fall back to Formula#missing_dependencies. + + # Find formulae that didn't have dependencies saved in all of their kegs, + # so need them to be calculated now. + # + # This happens after the initial dependency check because it's sloooow. + remaining_formulae = Formula.installed.select { |f| + f.installed_kegs.any? { |k| Tab.for_keg(k).runtime_dependencies.nil? } + } + + keg_names = kegs.map(&:name) + kegs_by_name = kegs.group_by(&:to_formula) + remaining_formulae.each do |dependent| + required = dependent.missing_dependencies(hide: keg_names) + required.select! { |f| kegs_by_name.key?(f) } + next unless required.any? + + required_kegs = required.map { |f| kegs_by_name[f].sort_by(&:version).last } + return required_kegs, [dependent] + end + + nil + end + # if path is a file in a keg then this will return the containing Keg object def self.for(path) path = path.realpath diff --git a/Library/Homebrew/test/test_uninstall.rb b/Library/Homebrew/test/test_uninstall.rb index c36a14477c5b0..a41e5e9d1c225 100644 --- a/Library/Homebrew/test/test_uninstall.rb +++ b/Library/Homebrew/test/test_uninstall.rb @@ -31,6 +31,32 @@ def test_uninstall assert_empty Formulary.factory(testball).installed_kegs end + def test_uninstall_with_unrelated_missing_deps_in_tab + setup_test_formula "testball" + run_as_not_developer do + cmd("install", testball) + cmd("install", "testball_f2") + cmd("uninstall", "--ignore-dependencies", "testball_f1") + cmd("uninstall", testball) + end + end + + def test_uninstall_with_unrelated_missing_deps_not_in_tab + setup_test_formula "testball" + run_as_not_developer do + cmd("install", testball) + cmd("install", "testball_f2") + + f2_keg = f2.installed_kegs.first + f2_tab = Tab.for_keg(f2_keg) + f2_tab.runtime_dependencies = nil + f2_tab.write + + cmd("uninstall", "--ignore-dependencies", "testball_f1") + cmd("uninstall", testball) + end + end + def test_uninstall_leaving_dependents cmd("install", "testball_f2") run_as_not_developer do