diff --git a/Library/Homebrew/cli/named_args.rb b/Library/Homebrew/cli/named_args.rb index 58153426e6..566279650f 100644 --- a/Library/Homebrew/cli/named_args.rb +++ b/Library/Homebrew/cli/named_args.rb @@ -80,17 +80,24 @@ module Homebrew @to_formulae_and_casks ||= T.let( {}, T.nilable(T::Hash[T.nilable(Symbol), T::Array[T.any(Formula, Keg, Cask::Cask)]]) ) - @to_formulae_and_casks[only] ||= downcased_unique_named.flat_map do |name| - options = { warn: }.compact - load_formula_or_cask(name, only:, method:, **options) - rescue FormulaUnreadableError, FormulaClassUnavailableError, - TapFormulaUnreadableError, TapFormulaClassUnavailableError, - Cask::CaskUnreadableError - # Need to rescue before `*UnavailableError` (superclass of this) - # The formula/cask was found, but there's a problem with its implementation - raise - rescue NoSuchKegError, FormulaUnavailableError, Cask::CaskUnavailableError, FormulaOrCaskUnavailableError - ignore_unavailable ? [] : raise + @to_formulae_and_casks[only] ||= begin + download_queue = Homebrew::DownloadQueue.new if Homebrew::EnvConfig.download_concurrency > 1 + + formulae_and_casks = downcased_unique_named.flat_map do |name| + load_and_fetch_full_formula_or_cask(name, only:, method:, warn:, download_queue:) + rescue FormulaUnreadableError, FormulaClassUnavailableError, + TapFormulaUnreadableError, TapFormulaClassUnavailableError, + Cask::CaskUnreadableError + # Need to rescue before `*UnavailableError` (superclass of this) + # The formula/cask was found, but there's a problem with its implementation + raise + rescue NoSuchKegError, FormulaUnavailableError, Cask::CaskUnavailableError, FormulaOrCaskUnavailableError + ignore_unavailable ? [] : raise + end + + download_queue&.fetch + + map_to_fully_loaded(formulae_and_casks) end.freeze if uniq @@ -152,11 +159,19 @@ module Homebrew T::Array[T.any(Formula, Keg, Cask::Cask, T::Array[Keg], FormulaOrCaskUnavailableError)], ]), ) - @to_formulae_casks_unknowns[method] = downcased_unique_named.map do |name| - load_formula_or_cask(name, only:, method:) - rescue FormulaOrCaskUnavailableError => e - e - end.uniq.freeze + @to_formulae_casks_unknowns[method] = begin + download_queue = Homebrew::DownloadQueue.new if Homebrew::EnvConfig.download_concurrency > 1 + + formulae_and_casks = downcased_unique_named.map do |name| + load_and_fetch_full_formula_or_cask(name, only:, method:, download_queue:) + rescue FormulaOrCaskUnavailableError => e + e + end.uniq + + download_queue&.fetch + + map_to_fully_loaded(formulae_and_casks) + end.freeze end sig { params(uniq: T::Boolean).returns(T::Array[Formula]) } @@ -315,10 +330,21 @@ module Homebrew end sig { - params(name: String, only: T.nilable(Symbol), method: T.nilable(Symbol), warn: T.nilable(T::Boolean)) + params(name: String, only: T.nilable(Symbol), method: T.nilable(Symbol), warn: T::Boolean, + download_queue: T.nilable(Homebrew::DownloadQueue)) .returns(T.any(Formula, Keg, Cask::Cask, T::Array[Keg])) } - def load_formula_or_cask(name, only: nil, method: nil, warn: nil) + def load_and_fetch_full_formula_or_cask(name, only: nil, method: nil, warn: false, download_queue: nil) + formula_or_cask = load_formula_or_cask(name, only:, method:, warn:) + formula_or_cask.fetch_fully_loaded_formula!(download_queue:) if formula_or_cask.is_a?(Formula) + formula_or_cask + end + + sig { + params(name: String, only: T.nilable(Symbol), method: T.nilable(Symbol), warn: T::Boolean) + .returns(T.any(Formula, Keg, Cask::Cask, T::Array[Keg])) + } + def load_formula_or_cask(name, only: nil, method: nil, warn: false) Homebrew.with_no_api_env_if_needed(@without_api) do unreadable_error = nil @@ -326,8 +352,8 @@ module Homebrew begin case method when nil, :factory - options = { warn:, force_bottle: @force_bottle, flags: @flags }.compact - Formulary.factory(name, *@override_spec, **options) + Formulary.factory(name, *@override_spec, + warn:, force_bottle: @force_bottle, flags: @flags, prefer_stub: true) when :resolve resolve_formula(name) when :latest_kegs @@ -447,7 +473,7 @@ module Homebrew sig { params(name: String).returns(Formula) } def resolve_formula(name) - Formulary.resolve(name, **{ spec: @override_spec, force_bottle: @force_bottle, flags: @flags }.compact) + Formulary.resolve(name, spec: @override_spec, force_bottle: @force_bottle, flags: @flags, prefer_stub: true) end sig { params(name: String).returns([Pathname, T::Array[Keg]]) } @@ -577,6 +603,19 @@ module Homebrew opoo package_conflicts_message(ref, loaded_type, cask) end + + sig { + type_parameters(:U) + .params(formulae_and_casks: T::Array[T.all(T.type_parameter(:U), Object)]) + .returns(T::Array[T.all(T.type_parameter(:U), Object)]) + } + def map_to_fully_loaded(formulae_and_casks) + formulae_and_casks.map do |formula_or_cask| + next formula_or_cask unless formula_or_cask.is_a?(Formula) + + T.cast(formula_or_cask.fully_loaded_formula, T.all(T.type_parameter(:U), Object)) + end + end end end end diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb index 54e791c080..327727e4fb 100644 --- a/Library/Homebrew/cli/parser.rb +++ b/Library/Homebrew/cli/parser.rb @@ -733,7 +733,7 @@ module Homebrew next if arg.match?(HOMEBREW_CASK_TAP_CASK_REGEX) begin - Formulary.factory(arg, spec, flags: argv.select { |a| a.start_with?("--") }) + Formulary.factory(arg, spec, flags: argv.select { |a| a.start_with?("--") }, prefer_stub: true) rescue FormulaUnavailableError, FormulaSpecificationError nil end diff --git a/Library/Homebrew/dependency.rb b/Library/Homebrew/dependency.rb index 55e864b7dd..c766e750a2 100644 --- a/Library/Homebrew/dependency.rb +++ b/Library/Homebrew/dependency.rb @@ -36,8 +36,8 @@ class Dependency [name, tags].hash end - def to_formula - formula = Formulary.factory(name, warn: false) + def to_formula(prefer_stub: false) + formula = Formulary.factory(name, warn: false, prefer_stub:) formula.build = BuildOptions.new(options, formula.options) formula end @@ -45,7 +45,7 @@ class Dependency sig { params(minimum_version: T.nilable(Version), minimum_revision: T.nilable(Integer)).returns(T::Boolean) } def installed?(minimum_version: nil, minimum_revision: nil) formula = begin - to_formula + to_formula(prefer_stub: true) rescue FormulaUnavailableError nil end @@ -86,7 +86,7 @@ class Dependency end def missing_options(inherited_options) - formula = to_formula + formula = to_formula(prefer_stub: true) required = options required |= inherited_options required &= formula.options.to_a diff --git a/Library/Homebrew/test/utils/autoremove_spec.rb b/Library/Homebrew/test/utils/autoremove_spec.rb index b078d3e7e9..59c14b85c1 100644 --- a/Library/Homebrew/test/utils/autoremove_spec.rb +++ b/Library/Homebrew/test/utils/autoremove_spec.rb @@ -65,7 +65,8 @@ RSpec.describe Utils::Autoremove do include_context "with formulae for dependency testing" before do - allow(Formulary).to receive(:factory).with("three", { warn: false }).and_return(formula_is_build_dep) + allow(Formulary).to receive(:factory).with("three", { prefer_stub: false, warn: false }) + .and_return(formula_is_build_dep) end context "when formulae are bottles" do