2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-08-18 22:11:42 +03:00
|
|
|
require "optparse"
|
|
|
|
require "shellwords"
|
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
require "cli/parser"
|
2016-10-04 15:24:58 +02:00
|
|
|
require "extend/optparse"
|
2018-06-09 12:20:58 +02:00
|
|
|
|
2018-09-03 19:39:07 +01:00
|
|
|
require "cask/config"
|
|
|
|
|
2018-09-03 20:17:29 +01:00
|
|
|
require "cask/cmd/abstract_command"
|
2019-05-23 15:20:24 +08:00
|
|
|
require "cask/cmd/--cache"
|
2018-09-03 20:17:29 +01:00
|
|
|
require "cask/cmd/audit"
|
|
|
|
require "cask/cmd/cat"
|
|
|
|
require "cask/cmd/create"
|
|
|
|
require "cask/cmd/doctor"
|
|
|
|
require "cask/cmd/edit"
|
|
|
|
require "cask/cmd/fetch"
|
2020-04-07 08:32:30 +02:00
|
|
|
require "cask/cmd/help"
|
2018-09-03 20:17:29 +01:00
|
|
|
require "cask/cmd/home"
|
|
|
|
require "cask/cmd/info"
|
|
|
|
require "cask/cmd/install"
|
|
|
|
require "cask/cmd/list"
|
|
|
|
require "cask/cmd/outdated"
|
|
|
|
require "cask/cmd/reinstall"
|
|
|
|
require "cask/cmd/style"
|
|
|
|
require "cask/cmd/uninstall"
|
|
|
|
require "cask/cmd/upgrade"
|
|
|
|
require "cask/cmd/zap"
|
|
|
|
|
|
|
|
require "cask/cmd/abstract_internal_command"
|
|
|
|
require "cask/cmd/internal_help"
|
|
|
|
require "cask/cmd/internal_stanza"
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2018-09-06 08:29:14 +02:00
|
|
|
module Cask
|
2020-08-19 10:34:07 +02:00
|
|
|
# Implementation of the `brew cask` command-line interface.
|
|
|
|
#
|
|
|
|
# @api private
|
2018-09-04 08:45:48 +01:00
|
|
|
class Cmd
|
2020-08-02 14:32:31 +02:00
|
|
|
include Context
|
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
ALIASES = {
|
2016-10-14 20:33:16 +02:00
|
|
|
"ls" => "list",
|
|
|
|
"homepage" => "home",
|
2020-04-07 08:32:30 +02:00
|
|
|
"instal" => "install", # gem does the same
|
2017-02-21 04:35:52 -08:00
|
|
|
"uninstal" => "uninstall",
|
2016-10-14 20:33:16 +02:00
|
|
|
"rm" => "uninstall",
|
|
|
|
"remove" => "uninstall",
|
|
|
|
"abv" => "info",
|
|
|
|
"dr" => "doctor",
|
|
|
|
}.freeze
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-18 12:26:19 -04:00
|
|
|
DEPRECATED_COMMANDS = {
|
|
|
|
Cmd::Cache => "brew --cache --cask",
|
|
|
|
Cmd::Doctor => "brew doctor --verbose",
|
|
|
|
Cmd::Home => "brew home",
|
|
|
|
Cmd::List => "brew list --cask",
|
|
|
|
Cmd::Outdated => "brew outdated --cask",
|
|
|
|
Cmd::Reinstall => "brew reinstall",
|
|
|
|
Cmd::Upgrade => "brew upgrade --cask",
|
|
|
|
}.freeze
|
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
def self.description
|
2020-08-19 17:12:32 +01:00
|
|
|
max_command_length = Cmd.commands.map(&:length).max
|
2020-08-01 02:30:46 +02:00
|
|
|
|
2020-08-19 17:12:32 +01:00
|
|
|
command_lines = Cmd.command_classes
|
|
|
|
.select(&:visible?)
|
|
|
|
.map do |klass|
|
|
|
|
" - #{"`#{klass.command_name}`".ljust(max_command_length + 2)} #{klass.short_description}\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
<<~EOS
|
2020-08-01 02:30:46 +02:00
|
|
|
Homebrew Cask provides a friendly CLI workflow for the administration of macOS applications distributed as binaries.
|
|
|
|
|
|
|
|
Commands:
|
2020-08-19 17:12:32 +01:00
|
|
|
#{command_lines.join}
|
|
|
|
|
|
|
|
See also: `man brew`
|
2020-08-01 02:30:46 +02:00
|
|
|
EOS
|
|
|
|
end
|
2017-05-21 00:15:56 +02:00
|
|
|
|
2020-09-27 22:53:01 +02:00
|
|
|
OPTIONS = [
|
|
|
|
[:flag, "--appdir=", {
|
|
|
|
description: "Target location for Applications. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:appdir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--colorpickerdir=", {
|
|
|
|
description: "Target location for Color Pickers. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:colorpickerdir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--prefpanedir=", {
|
|
|
|
description: "Target location for Preference Panes. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:prefpanedir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--qlplugindir=", {
|
|
|
|
description: "Target location for QuickLook Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:qlplugindir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--mdimporterdir=", {
|
|
|
|
description: "Target location for Spotlight Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:mdimporterdir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--dictionarydir=", {
|
|
|
|
description: "Target location for Dictionaries. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:dictionarydir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--fontdir=", {
|
|
|
|
description: "Target location for Fonts. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:fontdir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--servicedir=", {
|
|
|
|
description: "Target location for Services. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:servicedir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--input_methoddir=", {
|
|
|
|
description: "Target location for Input Methods. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:input_methoddir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--internet_plugindir=", {
|
|
|
|
description: "Target location for Internet Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:internet_plugindir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--audio_unit_plugindir=", {
|
|
|
|
description: "Target location for Audio Unit Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:audio_unit_plugindir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--vst_plugindir=", {
|
|
|
|
description: "Target location for VST Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:vst_plugindir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--vst3_plugindir=", {
|
|
|
|
description: "Target location for VST3 Plugins. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:vst3_plugindir]}`",
|
|
|
|
}],
|
|
|
|
[:flag, "--screen_saverdir=", {
|
|
|
|
description: "Target location for Screen Savers. " \
|
|
|
|
"Default: `#{Config::DEFAULT_DIRS[:screen_saverdir]}`",
|
|
|
|
}],
|
|
|
|
[:comma_array, "--language", {
|
|
|
|
description: "Set language of the Cask to install. The first matching " \
|
|
|
|
"language is used, otherwise the default language on the Cask. " \
|
|
|
|
"The default value is the `language of your system`",
|
|
|
|
}],
|
|
|
|
].freeze
|
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
def self.parser(&block)
|
|
|
|
Homebrew::CLI::Parser.new do
|
|
|
|
if block_given?
|
|
|
|
instance_eval(&block)
|
|
|
|
else
|
|
|
|
usage_banner <<~EOS
|
|
|
|
`cask` <command> [<options>] [<cask>]
|
2017-05-21 00:15:56 +02:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
#{Cmd.description}
|
|
|
|
EOS
|
|
|
|
end
|
2017-05-21 00:15:56 +02:00
|
|
|
|
2020-09-27 22:53:01 +02:00
|
|
|
OPTIONS.each do |option|
|
|
|
|
send(*option)
|
|
|
|
end
|
2020-08-01 02:30:46 +02:00
|
|
|
end
|
|
|
|
end
|
2017-03-06 20:37:13 +01:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def self.command_classes
|
2016-10-14 20:55:09 +02:00
|
|
|
@command_classes ||= constants.map(&method(:const_get))
|
2020-08-01 02:30:46 +02:00
|
|
|
.select { |klass| klass.is_a?(Class) && klass < AbstractCommand }
|
2017-05-20 19:08:03 +02:00
|
|
|
.reject(&:abstract?)
|
2016-12-30 16:13:09 +01:00
|
|
|
.sort_by(&:command_name)
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2016-09-24 13:52:43 +02:00
|
|
|
def self.commands
|
|
|
|
@commands ||= command_classes.map(&:command_name)
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def self.lookup_command(command_name)
|
2016-09-24 13:52:43 +02:00
|
|
|
@lookup ||= Hash[commands.zip(command_classes)]
|
2017-05-21 00:15:56 +02:00
|
|
|
command_name = ALIASES.fetch(command_name, command_name)
|
2020-04-07 08:32:30 +02:00
|
|
|
@lookup.fetch(command_name, nil)
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-07-20 13:18:09 -04:00
|
|
|
def self.aliases
|
|
|
|
ALIASES
|
|
|
|
end
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def self.run(*args)
|
|
|
|
new(*args).run
|
|
|
|
end
|
2018-06-20 22:16:08 +02:00
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def initialize(*args)
|
2020-08-01 02:30:46 +02:00
|
|
|
@argv = args
|
2020-04-07 08:32:30 +02:00
|
|
|
end
|
2018-06-20 22:16:08 +02:00
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def find_external_command(command)
|
|
|
|
@tap_cmd_directories ||= Tap.cmd_directories
|
|
|
|
@path ||= PATH.new(@tap_cmd_directories, ENV["HOMEBREW_PATH"])
|
2018-06-20 22:16:08 +02:00
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
external_ruby_cmd = @tap_cmd_directories.map { |d| d/"brewcask-#{command}.rb" }
|
|
|
|
.find(&:file?)
|
|
|
|
external_ruby_cmd ||= which("brewcask-#{command}.rb", @path)
|
2018-06-20 22:16:08 +02:00
|
|
|
|
|
|
|
if external_ruby_cmd
|
2020-04-07 08:32:30 +02:00
|
|
|
ExternalRubyCommand.new(command, external_ruby_cmd)
|
|
|
|
elsif external_command = which("brewcask-#{command}", @path)
|
|
|
|
ExternalCommand.new(external_command)
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def detect_internal_command(*args)
|
|
|
|
args.each_with_index do |arg, i|
|
|
|
|
if command = self.class.lookup_command(arg)
|
|
|
|
args.delete_at(i)
|
|
|
|
return [command, args]
|
|
|
|
elsif !arg.start_with?("-")
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2017-05-21 00:15:56 +02:00
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
nil
|
2017-05-21 00:15:56 +02:00
|
|
|
end
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def detect_external_command(*args)
|
|
|
|
args.each_with_index do |arg, i|
|
|
|
|
if command = find_external_command(arg)
|
|
|
|
args.delete_at(i)
|
|
|
|
return [command, args]
|
|
|
|
elsif !arg.start_with?("-")
|
|
|
|
break
|
2017-06-03 01:29:29 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
nil
|
2017-06-03 01:29:29 +02:00
|
|
|
end
|
|
|
|
|
2017-05-21 00:15:56 +02:00
|
|
|
def run
|
2020-08-01 02:30:46 +02:00
|
|
|
argv = @argv
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
args = self.class.parser.parse(argv, ignore_invalid_options: true)
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
Config::DEFAULT_DIRS.each_key do |name|
|
|
|
|
Config.global.public_send(:"#{name}=", args[name]) if args[name]
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
Config.global.languages = args.language if args.language
|
2020-04-07 08:32:30 +02:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
Tap.default_cask_tap.install unless Tap.default_cask_tap.installed?
|
2020-07-21 21:28:58 +02:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
command, argv = detect_internal_command(*argv) ||
|
|
|
|
detect_external_command(*argv) ||
|
|
|
|
[args.remaining.empty? ? NullCommand : UnknownSubcommand.new(args.remaining.first), argv]
|
2020-07-21 21:28:58 +02:00
|
|
|
|
2020-09-03 10:34:22 +01:00
|
|
|
if (replacement = DEPRECATED_COMMANDS[command])
|
|
|
|
odeprecated "brew cask #{command.command_name}", replacement
|
|
|
|
end
|
2020-08-18 12:26:19 -04:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
if args.help?
|
|
|
|
puts command.help
|
|
|
|
else
|
|
|
|
command.run(*argv)
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2020-08-01 02:30:46 +02:00
|
|
|
rescue CaskError, MethodDeprecatedError, ArgumentError => e
|
|
|
|
onoe e.message
|
|
|
|
$stderr.puts e.backtrace if args.debug?
|
|
|
|
exit 1
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
|
2020-08-19 10:34:07 +02:00
|
|
|
# Wrapper class for running an external Ruby command.
|
2020-04-07 08:32:30 +02:00
|
|
|
class ExternalRubyCommand
|
|
|
|
def initialize(command, path)
|
|
|
|
@command_name = command.to_s.capitalize.to_sym
|
|
|
|
@path = path
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
def run(*args)
|
2020-08-13 22:24:40 +02:00
|
|
|
command_class&.run(*args)
|
|
|
|
end
|
|
|
|
|
|
|
|
def help
|
|
|
|
command_class&.help
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def command_class
|
|
|
|
return @command_class if defined?(@command_class)
|
|
|
|
|
2020-04-07 08:32:30 +02:00
|
|
|
require @path
|
2017-03-08 15:49:37 +01:00
|
|
|
|
2020-08-13 22:24:40 +02:00
|
|
|
@command_class = begin
|
2020-04-07 08:32:30 +02:00
|
|
|
Cmd.const_get(@command_name)
|
|
|
|
rescue NameError
|
2020-08-13 22:24:40 +02:00
|
|
|
nil
|
2019-10-03 13:39:18 +05:30
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2020-04-07 08:32:30 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-19 10:34:07 +02:00
|
|
|
# Wrapper class for running an external command.
|
2020-04-07 08:32:30 +02:00
|
|
|
class ExternalCommand
|
|
|
|
def initialize(path)
|
|
|
|
@path = path
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
def run(*argv)
|
|
|
|
exec @path, *argv
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2020-08-13 22:24:40 +02:00
|
|
|
|
|
|
|
def help
|
|
|
|
exec @path, "--help"
|
|
|
|
end
|
2020-04-07 08:32:30 +02:00
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-19 10:34:07 +02:00
|
|
|
# Helper class for showing help for unknown subcommands.
|
2020-08-01 02:30:46 +02:00
|
|
|
class UnknownSubcommand
|
|
|
|
def initialize(command_name)
|
|
|
|
@command_name = command_name
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
|
2020-08-01 02:30:46 +02:00
|
|
|
def run(*)
|
|
|
|
raise UsageError, "Subcommand `#{@command_name}` does not exist."
|
2016-09-24 13:52:43 +02:00
|
|
|
end
|
2020-05-14 00:31:14 -04:00
|
|
|
|
|
|
|
def help
|
|
|
|
run
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
2020-08-01 02:30:46 +02:00
|
|
|
|
2020-08-19 10:34:07 +02:00
|
|
|
# Helper class for showing help when no subcommand is given.
|
2020-08-01 02:30:46 +02:00
|
|
|
class NullCommand
|
|
|
|
def self.run(*)
|
|
|
|
raise UsageError, "No subcommand given."
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.help
|
|
|
|
Cmd.parser.generate_help_text
|
|
|
|
end
|
|
|
|
end
|
2016-08-18 22:11:42 +03:00
|
|
|
end
|
|
|
|
end
|