diff --git a/Library/Homebrew/cask/auditor.rb b/Library/Homebrew/cask/auditor.rb index dcee4c4a9b9544204d816cb90e6cd1d9260e90f2..7abc4bb2c671af9aacdf809b9965a06bf556be5f 100644 --- a/Library/Homebrew/cask/auditor.rb +++ b/Library/Homebrew/cask/auditor.rb @@ -49,18 +49,16 @@ module Cask private def audit_all_languages - saved_languages = MacOS.instance_variable_get(:@languages) - begin - language_blocks.keys.all?(&method(:audit_languages)) - ensure - MacOS.instance_variable_set(:@languages, saved_languages) - end + language_blocks.keys.all?(&method(:audit_languages)) end def audit_languages(languages) ohai "Auditing language: #{languages.map { |lang| "'#{lang}'" }.to_sentence}" - MacOS.instance_variable_set(:@languages, languages) - audit_cask_instance(CaskLoader.load(cask.sourcefile_path)) + localized_cask = CaskLoader.load(cask.sourcefile_path) + config = localized_cask.config + config.languages = languages + localized_cask.config = config + audit_cask_instance(localized_cask) end def audit_cask_instance(cask) diff --git a/Library/Homebrew/cask/cmd.rb b/Library/Homebrew/cask/cmd.rb index 139e0638877c2ad15d0b19f026687083779e75f0..de9215dc649c526b85ecaf5df5bc6758101475c4 100644 --- a/Library/Homebrew/cask/cmd.rb +++ b/Library/Homebrew/cask/cmd.rb @@ -65,8 +65,7 @@ module Cask option "--help", :help, false - # handled in OS::Mac - option "--language a,b,c", ->(*) {} + option "--language=a,b,c", ->(value) { Config.global.languages = value } # override default handling of --version option "--version", ->(*) { raise OptionParser::InvalidOption } @@ -180,8 +179,6 @@ module Cask end def process_options(*args) - exclude_regex = /^--#{Regexp.union(*Config::DEFAULT_DIRS.keys.map(&Regexp.public_method(:escape)))}=/ - non_options = [] if idx = args.index("--") @@ -189,15 +186,32 @@ module Cask args = args.first(idx) end + exclude_regex = /^--#{Regexp.union(*Config::DEFAULT_DIRS.keys.map(&Regexp.public_method(:escape)))}=/ cask_opts = Shellwords.shellsplit(ENV.fetch("HOMEBREW_CASK_OPTS", "")) .reject { |arg| arg.match?(exclude_regex) } all_args = cask_opts + args - remaining = all_args.select do |arg| - !process_arguments([arg]).empty? - rescue OptionParser::InvalidOption, OptionParser::MissingArgument, OptionParser::AmbiguousOption - true + i = 0 + remaining = [] + + while i < all_args.count + begin + arg = all_args[i] + next if process_arguments([arg]).empty? + + remaining << arg + rescue OptionParser::MissingArgument + raise if i + 1 >= all_args.count + + args = all_args[i..(i + 1)] + process_arguments(args) + i += 1 + rescue OptionParser::InvalidOption + remaining << arg + end + + i += 1 end remaining + non_options diff --git a/Library/Homebrew/cask/config.rb b/Library/Homebrew/cask/config.rb index bd9bd711556a19e73a721985c1b8cebe59c69505..74c68bc24321c07c928dd42ea2f6b7d29a8a175a 100644 --- a/Library/Homebrew/cask/config.rb +++ b/Library/Homebrew/cask/config.rb @@ -2,6 +2,9 @@ require "json" +require "lazy_object" +require "locale" + require "extend/hash_validator" using HashValidator @@ -24,6 +27,12 @@ module Cask screen_saverdir: "~/Library/Screen Savers", }.freeze + def self.defaults + { + languages: LazyObject.new { MacOS.languages }, + }.merge(DEFAULT_DIRS).freeze + end + def self.global @global ||= new end @@ -69,16 +78,16 @@ module Cask attr_accessor :explicit def initialize(default: nil, env: nil, explicit: {}) - @default = self.class.canonicalize(DEFAULT_DIRS.merge(default)) if default + @default = self.class.canonicalize(self.class.defaults.merge(default)) if default @env = self.class.canonicalize(env) if env @explicit = self.class.canonicalize(explicit) - @env&.assert_valid_keys!(*DEFAULT_DIRS.keys) - @explicit.assert_valid_keys!(*DEFAULT_DIRS.keys) + @env&.assert_valid_keys!(*self.class.defaults.keys) + @explicit.assert_valid_keys!(*self.class.defaults.keys) end def default - @default ||= self.class.canonicalize(DEFAULT_DIRS) + @default ||= self.class.canonicalize(self.class.defaults) end def env @@ -86,7 +95,16 @@ module Cask Shellwords.shellsplit(ENV.fetch("HOMEBREW_CASK_OPTS", "")) .select { |arg| arg.include?("=") } .map { |arg| arg.split("=", 2) } - .map { |(flag, value)| [flag.sub(/^--/, ""), value] }, + .map do |(flag, value)| + key = flag.sub(/^--/, "") + + if key == "language" + key = "languages" + value = value.split(",") + end + + [key, value] + end, ) end @@ -98,6 +116,24 @@ module Cask @manpagedir ||= HOMEBREW_PREFIX/"share/man" end + def languages + [ + *explicit[:languages], + *env[:languages], + *default[:languages], + ].uniq.select do |lang| + # Ensure all languages are valid. + Locale.parse(lang) + true + rescue Locale::ParserError + false + end + end + + def languages=(languages) + explicit[:languages] = languages + end + DEFAULT_DIRS.each_key do |dir| define_method(dir) do explicit.fetch(dir, env.fetch(dir, default.fetch(dir))) diff --git a/Library/Homebrew/cask/dsl.rb b/Library/Homebrew/cask/dsl.rb index 525f927a2f5a229937a516a892a6fd8c16e6359a..ba5fc3cdf234b623c618b55c490ccacbe48cc113 100644 --- a/Library/Homebrew/cask/dsl.rb +++ b/Library/Homebrew/cask/dsl.rb @@ -137,13 +137,13 @@ module Cask raise CaskInvalidError.new(cask, "No default language specified.") if @language_blocks.default.nil? - locales = MacOS.languages - .map do |language| - Locale.parse(language) - rescue Locale::ParserError - nil - end - .compact + locales = cask.config.languages + .map do |language| + Locale.parse(language) + rescue Locale::ParserError + nil + end + .compact locales.each do |locale| key = locale.detect(@language_blocks.keys) @@ -225,7 +225,7 @@ module Cask end def caskroom_path - @cask.caskroom_path + cask.caskroom_path end def staged_path diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 8a854ab01f1f46b1fb3a4d8a44c989da7aaeb5f8..b44a47bcc441e1482cd44262819ceef29ff45e95 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -68,21 +68,7 @@ module OS end os_langs = os_langs.scan(/[^ \n"(),]+/) - @languages = [ - *Homebrew.args.value("language")&.split(","), - *ENV["HOMEBREW_LANGUAGES"]&.split(","), - *os_langs, - ].uniq - - # Ensure all languages are valid - @languages.select! do |lang| - Locale.parse(lang) - true - rescue Locale::ParserError - false - end - - @languages + @languages = os_langs end def language diff --git a/Library/Homebrew/test/cask/dsl_spec.rb b/Library/Homebrew/test/cask/dsl_spec.rb index 24b4a06dc2ec9492cbddae39aca395214541bc8f..949a1c804f728add4f5c528682e8993b5db9a943 100644 --- a/Library/Homebrew/test/cask/dsl_spec.rb +++ b/Library/Homebrew/test/cask/dsl_spec.rb @@ -118,8 +118,8 @@ describe Cask::DSL, :cask do end describe "language stanza" do - it "allows multilingual casks" do - cask = lambda do + context "when language is set explicitly" do + subject(:cask) { Cask::Cask.new("cask-with-apps") do language "zh" do sha256 "abc123" @@ -133,37 +133,65 @@ describe Cask::DSL, :cask do url "https://example.org/#{language}.zip" end + } + + matcher :be_the_chinese_version do + match do |cask| + expect(cask.language).to eq("zh-CN") + expect(cask.sha256).to eq("abc123") + expect(cask.url.to_s).to eq("https://example.org/zh-CN.zip") + end + end + + matcher :be_the_english_version do + match do |cask| + expect(cask.language).to eq("en-US") + expect(cask.sha256).to eq("xyz789") + expect(cask.url.to_s).to eq("https://example.org/en-US.zip") + end + end + + before do + config = cask.config + config.languages = languages + cask.config = config + end + + context "to 'zh'" do + let(:languages) { ["zh"] } + + it { is_expected.to be_the_chinese_version } + end + + context "to 'zh-XX'" do + let(:languages) { ["zh-XX"] } + + it { is_expected.to be_the_chinese_version } + end + + context "to 'en'" do + let(:languages) { ["en"] } + + it { is_expected.to be_the_english_version } end - allow(MacOS).to receive(:languages).and_return(["zh"]) - expect(cask.call.language).to eq("zh-CN") - expect(cask.call.sha256).to eq("abc123") - expect(cask.call.url.to_s).to eq("https://example.org/zh-CN.zip") + context "to 'xx-XX'" do + let(:languages) { ["xx-XX"] } - allow(MacOS).to receive(:languages).and_return(["zh-XX"]) - expect(cask.call.language).to eq("zh-CN") - expect(cask.call.sha256).to eq("abc123") - expect(cask.call.url.to_s).to eq("https://example.org/zh-CN.zip") + it { is_expected.to be_the_english_version } + end - allow(MacOS).to receive(:languages).and_return(["en"]) - expect(cask.call.language).to eq("en-US") - expect(cask.call.sha256).to eq("xyz789") - expect(cask.call.url.to_s).to eq("https://example.org/en-US.zip") + context "to 'xx-XX,zh,en'" do + let(:languages) { ["xx-XX", "zh", "en"] } - allow(MacOS).to receive(:languages).and_return(["xx-XX"]) - expect(cask.call.language).to eq("en-US") - expect(cask.call.sha256).to eq("xyz789") - expect(cask.call.url.to_s).to eq("https://example.org/en-US.zip") + it { is_expected.to be_the_chinese_version } + end - allow(MacOS).to receive(:languages).and_return(["xx-XX", "zh", "en"]) - expect(cask.call.language).to eq("zh-CN") - expect(cask.call.sha256).to eq("abc123") - expect(cask.call.url.to_s).to eq("https://example.org/zh-CN.zip") + context "to 'xx-XX,en-US,zh'" do + let(:languages) { ["xx-XX", "en-US", "zh"] } - allow(MacOS).to receive(:languages).and_return(["xx-XX", "en-US", "zh"]) - expect(cask.call.language).to eq("en-US") - expect(cask.call.sha256).to eq("xyz789") - expect(cask.call.url.to_s).to eq("https://example.org/en-US.zip") + it { is_expected.to be_the_english_version } + end end it "returns an empty array if no languages are specified" do