2024-11-18 23:17:18 +00:00
|
|
|
# typed: strict
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2019-02-02 17:11:37 +01:00
|
|
|
require "json"
|
2017-12-03 09:06:23 +01:00
|
|
|
|
2020-07-21 21:28:58 +02:00
|
|
|
require "lazy_object"
|
|
|
|
require "locale"
|
2024-01-12 09:38:49 -08:00
|
|
|
require "extend/hash/keys"
|
2020-07-21 21:28:58 +02:00
|
|
|
|
2019-02-02 17:11:37 +01:00
|
|
|
module Cask
|
2020-08-24 22:51:23 +02:00
|
|
|
# Configuration for installing casks.
|
|
|
|
#
|
2024-04-22 21:05:48 +02:00
|
|
|
# @api internal
|
2019-02-03 02:40:27 +01:00
|
|
|
class Config
|
2024-11-18 23:17:18 +00:00
|
|
|
DEFAULT_DIRS = T.let(
|
|
|
|
{
|
|
|
|
appdir: "/Applications",
|
|
|
|
keyboard_layoutdir: "/Library/Keyboard Layouts",
|
2025-01-27 15:12:50 +00:00
|
|
|
colorpickerdir: "~/Library/ColorPickers",
|
|
|
|
prefpanedir: "~/Library/PreferencePanes",
|
|
|
|
qlplugindir: "~/Library/QuickLook",
|
|
|
|
mdimporterdir: "~/Library/Spotlight",
|
|
|
|
dictionarydir: "~/Library/Dictionaries",
|
|
|
|
fontdir: "~/Library/Fonts",
|
|
|
|
servicedir: "~/Library/Services",
|
|
|
|
input_methoddir: "~/Library/Input Methods",
|
|
|
|
internet_plugindir: "~/Library/Internet Plug-Ins",
|
|
|
|
audio_unit_plugindir: "~/Library/Audio/Plug-Ins/Components",
|
|
|
|
vst_plugindir: "~/Library/Audio/Plug-Ins/VST",
|
|
|
|
vst3_plugindir: "~/Library/Audio/Plug-Ins/VST3",
|
|
|
|
screen_saverdir: "~/Library/Screen Savers",
|
2024-11-18 23:17:18 +00:00
|
|
|
}.freeze,
|
|
|
|
T::Hash[Symbol, String],
|
|
|
|
)
|
|
|
|
|
2024-12-04 22:49:14 +01:00
|
|
|
sig { returns(T::Hash[Symbol, String]) }
|
2020-07-21 21:28:58 +02:00
|
|
|
def self.defaults
|
|
|
|
{
|
2024-12-04 22:49:14 +01:00
|
|
|
languages: LazyObject.new { ::OS::Mac.languages },
|
2020-07-21 21:28:58 +02:00
|
|
|
}.merge(DEFAULT_DIRS).freeze
|
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { params(args: Homebrew::CLI::Args).returns(T.attached_class) }
|
2020-09-29 23:46:30 +02:00
|
|
|
def self.from_args(args)
|
2024-11-30 13:52:46 -08:00
|
|
|
# FIXME: T.unsafe is a workaround for methods that are only defined when `cask_options`
|
|
|
|
# is invoked on the parser. (These could be captured by a DSL compiler instead.)
|
2024-04-21 16:15:57 -07:00
|
|
|
args = T.unsafe(args)
|
2020-09-29 23:46:30 +02:00
|
|
|
new(explicit: {
|
|
|
|
appdir: args.appdir,
|
2023-03-26 08:10:40 +02:00
|
|
|
keyboard_layoutdir: args.keyboard_layoutdir,
|
2020-09-29 23:46:30 +02:00
|
|
|
colorpickerdir: args.colorpickerdir,
|
|
|
|
prefpanedir: args.prefpanedir,
|
|
|
|
qlplugindir: args.qlplugindir,
|
|
|
|
mdimporterdir: args.mdimporterdir,
|
|
|
|
dictionarydir: args.dictionarydir,
|
|
|
|
fontdir: args.fontdir,
|
|
|
|
servicedir: args.servicedir,
|
|
|
|
input_methoddir: args.input_methoddir,
|
|
|
|
internet_plugindir: args.internet_plugindir,
|
|
|
|
audio_unit_plugindir: args.audio_unit_plugindir,
|
|
|
|
vst_plugindir: args.vst_plugindir,
|
|
|
|
vst3_plugindir: args.vst3_plugindir,
|
|
|
|
screen_saverdir: args.screen_saverdir,
|
|
|
|
languages: args.language,
|
|
|
|
}.compact)
|
2017-12-03 09:06:23 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { params(json: String, ignore_invalid_keys: T::Boolean).returns(T.attached_class) }
|
|
|
|
def self.from_json(json, ignore_invalid_keys: false)
|
|
|
|
config = JSON.parse(json)
|
2019-02-02 17:11:37 +01:00
|
|
|
|
2019-02-03 02:40:27 +01:00
|
|
|
new(
|
2021-01-12 16:05:06 +01:00
|
|
|
default: config.fetch("default", {}),
|
|
|
|
env: config.fetch("env", {}),
|
|
|
|
explicit: config.fetch("explicit", {}),
|
2024-03-07 16:20:20 +00:00
|
|
|
ignore_invalid_keys:,
|
2019-02-03 02:40:27 +01:00
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2021-01-17 22:45:55 -08:00
|
|
|
sig {
|
2024-11-18 23:17:18 +00:00
|
|
|
params(
|
|
|
|
config: T::Enumerable[
|
|
|
|
[T.any(String, Symbol), T.any(String, Pathname, T::Array[String])],
|
|
|
|
],
|
|
|
|
).returns(
|
|
|
|
T::Hash[Symbol, T.any(String, Pathname, T::Array[String])],
|
|
|
|
)
|
2021-01-17 22:45:55 -08:00
|
|
|
}
|
2019-02-05 16:08:29 +01:00
|
|
|
def self.canonicalize(config)
|
2021-12-23 14:49:05 -05:00
|
|
|
config.to_h do |k, v|
|
2019-07-01 13:34:41 +02:00
|
|
|
key = k.to_sym
|
|
|
|
|
|
|
|
if DEFAULT_DIRS.key?(key)
|
2023-11-05 08:55:58 -08:00
|
|
|
raise TypeError, "Invalid path for default dir #{k}: #{v.inspect}" if v.is_a?(Array)
|
|
|
|
|
2019-07-01 13:34:41 +02:00
|
|
|
[key, Pathname(v).expand_path]
|
|
|
|
else
|
|
|
|
[key, v]
|
|
|
|
end
|
2021-12-23 14:49:05 -05:00
|
|
|
end
|
2019-02-05 16:08:29 +01:00
|
|
|
end
|
|
|
|
|
2024-04-22 21:05:48 +02:00
|
|
|
# Get the explicit configuration.
|
|
|
|
#
|
|
|
|
# @api internal
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]) }
|
2019-02-03 02:40:27 +01:00
|
|
|
attr_accessor :explicit
|
|
|
|
|
2021-01-17 22:45:55 -08:00
|
|
|
sig {
|
2021-01-12 16:05:06 +01:00
|
|
|
params(
|
|
|
|
default: T.nilable(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]),
|
|
|
|
env: T.nilable(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]),
|
|
|
|
explicit: T::Hash[Symbol, T.any(String, Pathname, T::Array[String])],
|
|
|
|
ignore_invalid_keys: T::Boolean,
|
|
|
|
).void
|
2021-01-17 22:45:55 -08:00
|
|
|
}
|
2021-01-12 16:05:06 +01:00
|
|
|
def initialize(default: nil, env: nil, explicit: {}, ignore_invalid_keys: false)
|
2024-11-18 23:17:18 +00:00
|
|
|
if default
|
|
|
|
@default = T.let(
|
|
|
|
self.class.canonicalize(self.class.defaults.merge(default)),
|
|
|
|
T.nilable(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
if env
|
|
|
|
@env = T.let(
|
|
|
|
self.class.canonicalize(env),
|
|
|
|
T.nilable(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]),
|
|
|
|
)
|
|
|
|
end
|
|
|
|
@explicit = T.let(
|
|
|
|
self.class.canonicalize(explicit),
|
|
|
|
T::Hash[Symbol, T.any(String, Pathname, T::Array[String])],
|
|
|
|
)
|
2019-02-03 02:40:27 +01:00
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
if ignore_invalid_keys
|
|
|
|
@env&.delete_if { |key, _| self.class.defaults.keys.exclude?(key) }
|
|
|
|
@explicit.delete_if { |key, _| self.class.defaults.keys.exclude?(key) }
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2023-03-19 17:31:51 -07:00
|
|
|
@env&.assert_valid_keys(*self.class.defaults.keys)
|
|
|
|
@explicit.assert_valid_keys(*self.class.defaults.keys)
|
2019-02-02 17:11:37 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]) }
|
2019-02-03 02:40:27 +01:00
|
|
|
def default
|
2020-07-21 21:28:58 +02:00
|
|
|
@default ||= self.class.canonicalize(self.class.defaults)
|
2019-02-03 02:40:27 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(T::Hash[Symbol, T.any(String, Pathname, T::Array[String])]) }
|
2019-02-03 02:40:27 +01:00
|
|
|
def env
|
2019-02-05 16:08:29 +01:00
|
|
|
@env ||= self.class.canonicalize(
|
2020-09-29 23:46:30 +02:00
|
|
|
Homebrew::EnvConfig.cask_opts
|
|
|
|
.select { |arg| arg.include?("=") }
|
2021-01-12 16:05:06 +01:00
|
|
|
.map { |arg| T.cast(arg.split("=", 2), [String, String]) }
|
2020-09-29 23:46:30 +02:00
|
|
|
.map do |(flag, value)|
|
|
|
|
key = flag.sub(/^--/, "")
|
2021-01-29 17:11:14 -05:00
|
|
|
# converts --language flag to :languages config key
|
2020-09-29 23:46:30 +02:00
|
|
|
if key == "language"
|
|
|
|
key = "languages"
|
|
|
|
value = value.split(",")
|
|
|
|
end
|
|
|
|
|
|
|
|
[key, value]
|
|
|
|
end,
|
2019-02-05 16:08:29 +01:00
|
|
|
)
|
2019-02-02 17:11:37 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(Pathname) }
|
2019-02-02 17:11:37 +01:00
|
|
|
def binarydir
|
2024-11-18 23:17:18 +00:00
|
|
|
@binarydir ||= T.let(HOMEBREW_PREFIX/"bin", T.nilable(Pathname))
|
2019-02-02 17:11:37 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(Pathname) }
|
2019-10-22 15:19:40 +03:00
|
|
|
def manpagedir
|
2024-11-18 23:17:18 +00:00
|
|
|
@manpagedir ||= T.let(HOMEBREW_PREFIX/"share/man", T.nilable(Pathname))
|
2019-10-22 15:19:40 +03:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { returns(T::Array[String]) }
|
2020-07-21 21:28:58 +02:00
|
|
|
def languages
|
|
|
|
[
|
2024-05-31 15:49:12 -07:00
|
|
|
*explicit.fetch(:languages, []),
|
|
|
|
*env.fetch(:languages, []),
|
|
|
|
*default.fetch(:languages, []),
|
2020-07-21 21:28:58 +02:00
|
|
|
].uniq.select do |lang|
|
|
|
|
# Ensure all languages are valid.
|
|
|
|
Locale.parse(lang)
|
|
|
|
true
|
|
|
|
rescue Locale::ParserError
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-11-18 23:17:18 +00:00
|
|
|
sig { params(languages: T::Array[String]).void }
|
2020-07-21 21:28:58 +02:00
|
|
|
def languages=(languages)
|
|
|
|
explicit[:languages] = languages
|
|
|
|
end
|
|
|
|
|
2020-02-19 11:18:40 +00:00
|
|
|
DEFAULT_DIRS.each_key do |dir|
|
2019-02-02 17:11:37 +01:00
|
|
|
define_method(dir) do
|
2023-03-19 19:37:28 -07:00
|
|
|
T.bind(self, Config)
|
2019-02-03 02:40:27 +01:00
|
|
|
explicit.fetch(dir, env.fetch(dir, default.fetch(dir)))
|
2019-02-02 17:11:37 +01:00
|
|
|
end
|
2017-12-03 09:06:23 +01:00
|
|
|
|
|
|
|
define_method(:"#{dir}=") do |path|
|
2023-03-19 19:37:28 -07:00
|
|
|
T.bind(self, Config)
|
2019-02-03 02:40:27 +01:00
|
|
|
explicit[dir] = Pathname(path).expand_path
|
2017-12-03 09:06:23 +01:00
|
|
|
end
|
|
|
|
end
|
2019-02-02 17:11:37 +01:00
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { params(other: Config).returns(T.self_type) }
|
2019-02-03 02:40:27 +01:00
|
|
|
def merge(other)
|
2019-02-03 13:03:16 +01:00
|
|
|
self.class.new(explicit: other.explicit.merge(explicit))
|
2019-02-03 02:40:27 +01:00
|
|
|
end
|
|
|
|
|
2021-01-12 16:05:06 +01:00
|
|
|
sig { params(options: T.untyped).returns(String) }
|
2023-07-18 11:30:46 -07:00
|
|
|
def to_json(*options)
|
2019-02-03 02:40:27 +01:00
|
|
|
{
|
2024-03-07 16:20:20 +00:00
|
|
|
default:,
|
|
|
|
env:,
|
|
|
|
explicit:,
|
2023-07-18 11:30:46 -07:00
|
|
|
}.to_json(*options)
|
2019-02-03 02:40:27 +01:00
|
|
|
end
|
2017-12-03 09:06:23 +01:00
|
|
|
end
|
|
|
|
end
|
2024-12-04 22:49:14 +01:00
|
|
|
|
|
|
|
require "extend/os/cask/config"
|