diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb index ac5fd1875b00d..cc7a571764c4e 100644 --- a/Library/Homebrew/cli/args.rb +++ b/Library/Homebrew/cli/args.rb @@ -36,9 +36,11 @@ def freeze_named_args!(named_args) # Reset cache values reliant on named_args @formulae = nil @resolved_formulae = nil + @resolved_formulae_casks = nil @formulae_paths = nil @casks = nil @kegs = nil + @kegs_casks = nil self[:named_args] = named_args self[:named_args].freeze @@ -96,6 +98,25 @@ def resolved_formulae end.uniq(&:name).freeze end + def resolved_formulae_casks + @resolved_formulae_casks ||= begin + resolved_formulae = [] + casks = [] + + downcased_unique_named.each do |name| + resolved_formulae << Formulary.resolve(name, spec: spec(nil)) + rescue FormulaUnavailableError + begin + casks << Cask::CaskLoader.load(name) + rescue Cask::CaskUnavailableError + raise "No available formula or cask with the name \"#{name}\"" + end + end + + [resolved_formulae.freeze, casks.freeze].freeze + end + end + def formulae_paths @formulae_paths ||= (downcased_unique_named - casks).map do |name| Formulary.path(name) @@ -108,55 +129,33 @@ def casks end def kegs - require "keg" - require "formula" - require "missing_formula" - @kegs ||= downcased_unique_named.map do |name| - raise UsageError if name.empty? - - rack = Formulary.to_rack(name.downcase) - - dirs = rack.directory? ? rack.subdirs : [] - - if dirs.empty? - if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall")) - $stderr.puts reason - end - raise NoSuchKegError, rack.basename + resolve_keg name + rescue NoSuchKegError => e + if (reason = Homebrew::MissingFormula.suggest_command(name, "uninstall")) + $stderr.puts reason end + raise e + end.freeze + end - linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename - opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}" - - begin - if opt_prefix.symlink? && opt_prefix.directory? - Keg.new(opt_prefix.resolved_path) - elsif linked_keg_ref.symlink? && linked_keg_ref.directory? - Keg.new(linked_keg_ref.resolved_path) - elsif dirs.length == 1 - Keg.new(dirs.first) - else - f = if name.include?("/") || File.exist?(name) - Formulary.factory(name) - else - Formulary.from_rack(rack) - end - - unless (prefix = f.installed_prefix).directory? - raise MultipleVersionsInstalledError, rack.basename - end - - Keg.new(prefix) + def kegs_casks + @kegs_casks ||= begin + kegs = [] + casks = [] + + downcased_unique_named.each do |name| + kegs << resolve_keg(name) + rescue NoSuchKegError + begin + casks << Cask::CaskLoader.load(name) + rescue Cask::CaskUnavailableError + raise "No installed keg or cask with the name \"#{name}\"" end - rescue FormulaUnavailableError - raise <<~EOS - Multiple kegs installed to #{rack} - However we don't know which one you refer to. - Please delete (with rm -rf!) all but one and then try again. - EOS end - end.freeze + + [kegs.freeze, casks.freeze].freeze + end end def build_stable? @@ -241,6 +240,50 @@ def spec(default = :stable) default end end + + def resolve_keg(name) + require "keg" + require "formula" + require "missing_formula" + + raise UsageError if name.blank? + + rack = Formulary.to_rack(name.downcase) + + dirs = rack.directory? ? rack.subdirs : [] + raise NoSuchKegError, rack.basename if dirs.empty? + + linked_keg_ref = HOMEBREW_LINKED_KEGS/rack.basename + opt_prefix = HOMEBREW_PREFIX/"opt/#{rack.basename}" + + begin + if opt_prefix.symlink? && opt_prefix.directory? + Keg.new(opt_prefix.resolved_path) + elsif linked_keg_ref.symlink? && linked_keg_ref.directory? + Keg.new(linked_keg_ref.resolved_path) + elsif dirs.length == 1 + Keg.new(dirs.first) + else + f = if name.include?("/") || File.exist?(name) + Formulary.factory(name) + else + Formulary.from_rack(rack) + end + + unless (prefix = f.installed_prefix).directory? + raise MultipleVersionsInstalledError, "#{rack.basename} has multiple installed versions" + end + + Keg.new(prefix) + end + rescue FormulaUnavailableError + raise MultipleVersionsInstalledError, <<~EOS + Multiple kegs installed to #{rack} + However we don't know which one you refer to. + Please delete (with rm -rf!) all but one and then try again. + EOS + end + end end end end diff --git a/Library/Homebrew/cmd/reinstall.rb b/Library/Homebrew/cmd/reinstall.rb index bc85d52f134ed..037260a2eaabb 100644 --- a/Library/Homebrew/cmd/reinstall.rb +++ b/Library/Homebrew/cmd/reinstall.rb @@ -6,6 +6,9 @@ require "reinstall" require "cli/parser" require "cleanup" +require "cask/cmd" +require "cask/utils" +require "cask/macos" require "upgrade" module Homebrew @@ -57,7 +60,8 @@ def reinstall Install.perform_preinstall_checks - args.resolved_formulae.each do |f| + resolved_formulae, casks = args.resolved_formulae_casks + resolved_formulae.each do |f| if f.pinned? onoe "#{f.full_name} is pinned. You must unpin it to reinstall." next @@ -70,5 +74,12 @@ def reinstall check_installed_dependents Homebrew.messages.display_messages + + return if casks.blank? + + reinstall_cmd = Cask::Cmd::Reinstall.new(casks) + reinstall_cmd.verbose = args.verbose? + reinstall_cmd.force = args.force? + reinstall_cmd.run end end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb index af66c9ac1e18f..2cb101b2961f6 100644 --- a/Library/Homebrew/cmd/uninstall.rb +++ b/Library/Homebrew/cmd/uninstall.rb @@ -5,6 +5,9 @@ require "diagnostic" require "migrator" require "cli/parser" +require "cask/all" +require "cask/cmd" +require "cask/cask_loader" module Homebrew module_function @@ -29,15 +32,26 @@ def uninstall_args def uninstall uninstall_args.parse - kegs_by_rack = if args.force? - Hash[args.named.map do |name| + if args.force? + casks = [] + kegs_by_rack = {} + + args.named.each do |name| rack = Formulary.to_rack(name) - next unless rack.directory? - [rack, rack.subdirs.map { |d| Keg.new(d) }] - end] + if rack.directory? + kegs_by_rack[rack] = rack.subdirs.map { |d| Keg.new(d) } + else + begin + casks << Cask::CaskLoader.load(name) + rescue Cask::CaskUnavailableError + # Since the uninstall was forced, ignore any unavailable casks + end + end + end else - args.kegs.group_by(&:rack) + all_kegs, casks = args.kegs_casks + kegs_by_rack = all_kegs.group_by(&:rack) end handle_unsatisfied_dependents(kegs_by_rack) @@ -108,6 +122,13 @@ def uninstall end end end + + return if casks.blank? + + cask_uninstall = Cask::Cmd::Uninstall.new(casks) + cask_uninstall.force = args.force? + cask_uninstall.verbose = args.verbose? + cask_uninstall.run rescue MultipleVersionsInstalledError => e ofail e puts "Run `brew uninstall --force #{e.name}` to remove all versions." diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index ba47d036b7887..bc32524e61065 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -29,14 +29,7 @@ def initialize end end -class MultipleVersionsInstalledError < RuntimeError - attr_reader :name - - def initialize(name) - @name = name - super "#{name} has multiple installed versions" - end -end +class MultipleVersionsInstalledError < RuntimeError; end class NotAKegError < RuntimeError; end diff --git a/Library/Homebrew/test/exceptions_spec.rb b/Library/Homebrew/test/exceptions_spec.rb index ee0aa4f3708fc..79dba60a4ef34 100644 --- a/Library/Homebrew/test/exceptions_spec.rb +++ b/Library/Homebrew/test/exceptions_spec.rb @@ -3,7 +3,7 @@ require "exceptions" describe MultipleVersionsInstalledError do - subject { described_class.new("foo") } + subject { described_class.new("foo has multiple installed versions") } its(:to_s) { is_expected.to eq("foo has multiple installed versions") } end