diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index 4b1c4bead7..18cebd71fb 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -54,6 +54,7 @@ module Homebrew Uninstall.uninstall_kegs( kegs_by_rack, + casks: casks, force: args.force?, ignore_dependencies: args.ignore_dependencies?, named_args: args.named, diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index 8b7089bb57..76de4524c9 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -190,6 +190,57 @@ class Keg [all_required_kegs.to_a, all_dependents.sort] end + # Given an array of kegs, this method finds casks dependent on them + # except those included in the `casks` array. If it does, it returns: + # + # - kegs in the passed array that have dependent casks + # - installed casks dependent on them. + # + # If it doesn't, it returns nil. + # + # If all dependent casks are included in the `casks` array, the value + # returned is nil. + def self.find_cask_dependents(kegs, casks) + return if kegs.blank? + + casks_to_check = Cask::Caskroom.casks - casks + return if casks_to_check.blank? + + kegs_by_source = kegs.group_by do |keg| + f = keg.to_formula + [f.name, f.tap] + rescue + [keg.name, keg.tab.tap] + end + + all_required_kegs = Set.new + all_dependents = [] + + casks_to_check.each do |dependent| + dependencies = CaskDependent.new(dependent).runtime_dependencies + next if dependencies.blank? + + required = dependencies.map(&:to_formula) + + required_kegs = required.map do |f| + f_kegs = kegs_by_source[[f.name, f.tap]] + next unless f_kegs + + f_kegs.max_by(&:version) + end.compact + + next if required_kegs.blank? + + all_required_kegs += required_kegs + all_dependents << dependent.to_s + end + + return if all_required_kegs.blank? + return if all_dependents.blank? + + [all_required_kegs.to_a, all_dependents.sort] + end + # @param path if this is a file in a keg, returns the containing {Keg} object. def self.for(path) original_path = path diff --git a/Library/Homebrew/test/uninstall_spec.rb b/Library/Homebrew/test/uninstall_spec.rb index d5f54077f2..c6b0a5723d 100644 --- a/Library/Homebrew/test/uninstall_spec.rb +++ b/Library/Homebrew/test/uninstall_spec.rb @@ -5,31 +5,42 @@ require "uninstall" describe Homebrew::Uninstall do let(:dependency) { formula("dependency") { url "f-1" } } - let(:dependent) do - formula("dependent") do + + let(:dependent_formula) do + formula("dependent_formula") do url "f-1" depends_on "dependency" end end + let(:dependent_cask) do + Cask::CaskLoader.load(+<<-RUBY) + cask "dependent_cask" do + url "c-1" + depends_on formula: "dependency" + end + RUBY + end + let(:kegs_by_rack) { { dependency.rack => [Keg.new(dependency.latest_installed_prefix)] } } before do - [dependency, dependent].each do |f| + [dependency, dependent_formula].each do |f| f.latest_installed_prefix.mkpath Keg.new(f.latest_installed_prefix).optlink end tab = Tab.empty tab.homebrew_version = "1.1.6" - tab.tabfile = dependent.latest_installed_prefix/Tab::FILENAME + tab.tabfile = dependent_formula.latest_installed_prefix/Tab::FILENAME tab.runtime_dependencies = [ { "full_name" => "dependency", "version" => "1" }, ] tab.write stub_formula_loader dependency - stub_formula_loader dependent + stub_formula_loader dependent_formula + stub_cask_loader dependent_cask end describe "::handle_unsatisfied_dependents" do diff --git a/Library/Homebrew/uninstall.rb b/Library/Homebrew/uninstall.rb index cabb18f627..b3da4f5082 100644 --- a/Library/Homebrew/uninstall.rb +++ b/Library/Homebrew/uninstall.rb @@ -10,8 +10,9 @@ module Homebrew module Uninstall module_function - def uninstall_kegs(kegs_by_rack, force: false, ignore_dependencies: false, named_args: []) + def uninstall_kegs(kegs_by_rack, casks: [], force: false, ignore_dependencies: false, named_args: []) handle_unsatisfied_dependents(kegs_by_rack, + casks: casks, ignore_dependencies: ignore_dependencies, named_args: named_args) return if Homebrew.failed? @@ -96,18 +97,27 @@ module Homebrew end end - def handle_unsatisfied_dependents(kegs_by_rack, ignore_dependencies: false, named_args: []) + def handle_unsatisfied_dependents(kegs_by_rack, casks: [], ignore_dependencies: false, named_args: []) return if ignore_dependencies all_kegs = kegs_by_rack.values.flatten(1) - check_for_dependents(all_kegs, named_args: named_args) + check_for_dependents(all_kegs, casks: casks, named_args: named_args) rescue MethodDeprecatedError # Silently ignore deprecations when uninstalling. nil end - def check_for_dependents(kegs, named_args: []) - return false unless result = Keg.find_some_installed_dependents(kegs) + def check_for_dependents(kegs, casks: [], named_args: []) + result_kegs = Keg.find_some_installed_dependents(kegs) + result_casks = Keg.find_cask_dependents(kegs, casks) + + return false if result_kegs.blank? && result_casks.blank? + + result = if result_kegs.present? && result_casks.present? + [(result_kegs[0] + result_casks[0]).uniq, result_kegs[1] + result_casks[1]] + else + result_kegs || result_casks + end if Homebrew::EnvConfig.developer? DeveloperDependentsMessage.new(*result, named_args: named_args).output