Skip to content

Commit

Permalink
uninstall: only <=1 Diagnostic.missing_deps call
Browse files Browse the repository at this point in the history
  • Loading branch information
alyssais committed Oct 25, 2016
1 parent 4526915 commit ef13f8e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 8 deletions.
43 changes: 36 additions & 7 deletions Library/Homebrew/cmd/uninstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

require "keg"
require "formula"
require "diagnostic"
require "migrator"

module Homebrew
Expand Down Expand Up @@ -75,17 +76,45 @@ def uninstall
end

def check_for_dependents(kegs)
return false unless result = find_some_installed_dependents(kegs)

requireds, dependents = result

msg = "Refusing to uninstall #{requireds.join(", ")} because "
msg << (requireds.count == 1 ? "it is" : "they are")
msg << " required by #{dependents.join(", ")}, which "
msg << (dependents.count == 1 ? "is" : "are")
msg << " currently installed."
ofail msg
print "You can override this and force removal with "
puts "`brew uninstall --ignore-dependencies #{requireds.map(&:name).join(" ")}`."

true
end

# Will return some kegs, and some dependencies, if they're present.
# For efficiency, we don't bother trying to get complete data.
def find_some_installed_dependents(kegs)
kegs.each do |keg|
dependents = keg.installed_dependents - kegs
next if dependents.empty?
dependents.map! { |d| "#{d.name} #{d.version}" }
return [keg], dependents if dependents.any?
end

dependents_output = dependents.map { |k| "#{k.name} #{k.version}" }.join(", ")
conjugation = dependents.count == 1 ? "is" : "are"
ofail "Refusing to uninstall #{keg} because it is required by #{dependents_output}, which #{conjugation} currently installed."
puts "You can override this and force removal with `brew uninstall --ignore-dependencies #{keg.name}`."
return true
# 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? }
}
Diagnostic.missing_deps(remaining_formulae, kegs.map(&:name)) do |dependent, required|
kegs_by_name = kegs.group_by(&:to_formula)
required_kegs = required.map { |f| kegs_by_name[f].sort_by(&:version).last }
return required_kegs, [dependent]
end
false

nil
end

def rm_pin(rack)
Expand Down
4 changes: 3 additions & 1 deletion Library/Homebrew/keg.rb
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ def to_formula

def installed_dependents
Formula.installed.flat_map(&:installed_kegs).select do |keg|
Tab.for_keg(keg).runtime_dependencies.any? do |dep|
tab = Tab.for_keg(keg)
next if tab.runtime_dependencies.nil? # no dependency information saved.
tab.runtime_dependencies.any? do |dep|
# Resolve formula rather than directly comparing names
# in case of conflicts between formulae from different taps.
dep_formula = Formulary.factory(dep["full_name"])
Expand Down
18 changes: 18 additions & 0 deletions Library/Homebrew/test/test_uninstall.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ def test_uninstall_leaving_dependents
end
end

def test_uninstall_leaving_dependents_no_runtime_dependencies_in_tab
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

run_as_not_developer do
assert_match "Refusing to uninstall",
cmd_fail("uninstall", "testball_f1")
refute_empty f1.installed_kegs
assert_match "Uninstalling #{f2.rack}",
cmd("uninstall", "testball_f2")
assert_empty f2.installed_kegs
end
end

def test_uninstall_force_leaving_dependents
cmd("install", "testball_f2")
run_as_not_developer do
Expand Down

0 comments on commit ef13f8e

Please sign in to comment.