Merge pull request #20296 from Homebrew/upgrade_reinstall_download_queue

Optionally use DownloadQueue for reinstall, upgrade.
This commit is contained in:
Mike McQuaid 2025-07-24 15:03:36 +00:00 committed by GitHub
commit 097e2a351c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 775 additions and 667 deletions

View File

@ -585,13 +585,15 @@ module Cask
}.compact }.compact
Homebrew::Install.perform_preinstall_checks_once Homebrew::Install.perform_preinstall_checks_once
valid_formula_installers = Homebrew::Install.fetch_formulae(primary_container.dependencies)
primary_container.dependencies.each do |dep| primary_container.dependencies.each do |dep|
next unless valid_formula_installers.include?(dep)
fi = FormulaInstaller.new( fi = FormulaInstaller.new(
dep, dep,
**install_options, **install_options,
) )
fi.prelude
fi.fetch
fi.install fi.install
fi.finish fi.finish
end end

View File

@ -426,6 +426,9 @@ on_request: true)
end end
ohai "Installing dependencies: #{missing_formulae_and_casks.map(&:to_s).join(", ")}" ohai "Installing dependencies: #{missing_formulae_and_casks.map(&:to_s).join(", ")}"
cask_installers = T.let([], T::Array[Installer])
formula_installers = T.let([], T::Array[FormulaInstaller])
missing_formulae_and_casks.each do |cask_or_formula| missing_formulae_and_casks.each do |cask_or_formula|
if cask_or_formula.is_a?(Cask) if cask_or_formula.is_a?(Cask)
if skip_cask_deps? if skip_cask_deps?
@ -433,7 +436,7 @@ on_request: true)
next next
end end
Installer.new( cask_installers << Installer.new(
cask_or_formula, cask_or_formula,
adopt: adopt?, adopt: adopt?,
binaries: binaries?, binaries: binaries?,
@ -444,10 +447,9 @@ on_request: true)
quiet: quiet?, quiet: quiet?,
require_sha: require_sha?, require_sha: require_sha?,
verbose: verbose?, verbose: verbose?,
).install )
else else
Homebrew::Install.perform_preinstall_checks_once formula_installers << FormulaInstaller.new(
fi = FormulaInstaller.new(
cask_or_formula, cask_or_formula,
**{ **{
show_header: true, show_header: true,
@ -456,12 +458,18 @@ on_request: true)
verbose: verbose?, verbose: verbose?,
}.compact, }.compact,
) )
fi.prelude
fi.fetch
fi.install
fi.finish
end end
end end
cask_installers.each(&:install)
return if formula_installers.blank?
Homebrew::Install.perform_preinstall_checks_once
valid_formula_installers = Homebrew::Install.fetch_formulae(formula_installers)
valid_formula_installers.each do |formula_installer|
formula_installer.install
formula_installer.finish
end
end end
def caveats def caveats

View File

@ -130,7 +130,7 @@ module Homebrew
unless formulae.empty? unless formulae.empty?
Install.perform_preinstall_checks_once Install.perform_preinstall_checks_once
install_context = formulae.map do |formula| reinstall_contexts = formulae.filter_map do |formula|
if formula.pinned? if formula.pinned?
onoe "#{formula.full_name} is pinned. You must unpin it to reinstall." onoe "#{formula.full_name} is pinned. You must unpin it to reinstall."
next next
@ -167,27 +167,18 @@ module Homebrew
verbose: args.verbose?, verbose: args.verbose?,
) )
formulae_installer = install_context.map(&:formula_installer) formulae_installers = reinstall_contexts.map(&:formula_installer)
# Main block: if asking the user is enabled, show dependency and size information. # Main block: if asking the user is enabled, show dependency and size information.
Install.ask_formulae(formulae_installer, dependants, args: args) if args.ask? Install.ask_formulae(formulae_installers, dependants, args: args) if args.ask?
install_context.each do |f| valid_formula_installers = Install.fetch_formulae(formulae_installers)
Homebrew::Reinstall.reinstall_formula(
f, reinstall_contexts.each do |reinstall_context|
flags: args.flags_only, next unless valid_formula_installers.include?(reinstall_context.formula_installer)
force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae, Homebrew::Reinstall.reinstall_formula(reinstall_context)
interactive: args.interactive?, Cleanup.install_formula_clean!(reinstall_context.formula)
keep_tmp: args.keep_tmp?,
debug_symbols: args.debug_symbols?,
force: args.force?,
debug: args.debug?,
quiet: args.quiet?,
verbose: args.verbose?,
git: args.git?,
)
Cleanup.install_formula_clean!(f.formula)
end end
Upgrade.upgrade_dependents( Upgrade.upgrade_dependents(

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
module OS module OS
@ -30,18 +30,21 @@ module OS
libstdc++.so.6 libstdc++.so.6
].freeze ].freeze
sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks(all_fatal: false) def perform_preinstall_checks(all_fatal: false)
super super
symlink_ld_so symlink_ld_so
setup_preferred_gcc_libs setup_preferred_gcc_libs
end end
sig { void }
def global_post_install def global_post_install
super super
symlink_ld_so symlink_ld_so
setup_preferred_gcc_libs setup_preferred_gcc_libs
end end
sig { void }
def check_cpu def check_cpu
return if ::Hardware::CPU.intel? && ::Hardware::CPU.is_64_bit? return if ::Hardware::CPU.intel? && ::Hardware::CPU.is_64_bit?
return if ::Hardware::CPU.arm? return if ::Hardware::CPU.arm?
@ -56,6 +59,7 @@ module OS
::Kernel.abort message ::Kernel.abort message
end end
sig { void }
def symlink_ld_so def symlink_ld_so
brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so" brew_ld_so = HOMEBREW_PREFIX/"lib/ld.so"
@ -75,6 +79,7 @@ module OS
FileUtils.ln_sf ld_so, brew_ld_so FileUtils.ln_sf ld_so, brew_ld_so
end end
sig { void }
def setup_preferred_gcc_libs def setup_preferred_gcc_libs
gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}" gcc_opt_prefix = HOMEBREW_PREFIX/"opt/#{OS::LINUX_PREFERRED_GCC_RUNTIME_FORMULA}"
glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable? glibc_installed = (HOMEBREW_PREFIX/"opt/glibc/bin/ld.so").readable?

View File

@ -13,7 +13,6 @@ require "sandbox"
require "development_tools" require "development_tools"
require "cache_store" require "cache_store"
require "linkage_checker" require "linkage_checker"
require "install"
require "messages" require "messages"
require "cask/cask_loader" require "cask/cask_loader"
require "cmd/install" require "cmd/install"
@ -61,8 +60,8 @@ class FormulaInstaller
bottle_arch: T.nilable(String), bottle_arch: T.nilable(String),
ignore_deps: T::Boolean, ignore_deps: T::Boolean,
only_deps: T::Boolean, only_deps: T::Boolean,
include_test_formulae: T::Array[Formula], include_test_formulae: T::Array[String],
build_from_source_formulae: T::Array[Formula], build_from_source_formulae: T::Array[String],
env: T.nilable(String), env: T.nilable(String),
git: T::Boolean, git: T::Boolean,
interactive: T::Boolean, interactive: T::Boolean,
@ -507,7 +506,10 @@ class FormulaInstaller
lock lock
start_time = Time.now start_time = Time.now
Homebrew::Install.perform_build_from_source_checks if !pour_bottle? && DevelopmentTools.installed? if !pour_bottle? && DevelopmentTools.installed?
require "install"
Homebrew::Install.perform_build_from_source_checks
end
# Warn if a more recent version of this formula is available in the tap. # Warn if a more recent version of this formula is available in the tap.
begin begin
@ -896,6 +898,7 @@ on_request: installed_on_request?, options:)
verbose: verbose?, verbose: verbose?,
) )
oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}" oh1 "Installing #{formula.full_name} dependency: #{Formatter.identifier(dep.name)}"
# prelude only needed to populate bottle_tab_runtime_dependencies, fetching has already been done.
fi.prelude fi.prelude
fi.install fi.install
fi.finish fi.finish
@ -955,6 +958,7 @@ on_request: installed_on_request?, options:)
fix_dynamic_linkage(keg) if !@poured_bottle || !formula.bottle_specification.skip_relocation? fix_dynamic_linkage(keg) if !@poured_bottle || !formula.bottle_specification.skip_relocation?
require "install"
Homebrew::Install.global_post_install Homebrew::Install.global_post_install
if build_bottle? || skip_post_install? if build_bottle? || skip_post_install?

View File

@ -21,6 +21,7 @@ module Homebrew
end end
end end
sig { params(cc: T.nilable(String)).void }
def check_cc_argv(cc) def check_cc_argv(cc)
return unless cc return unless cc
@ -32,13 +33,16 @@ module Homebrew
EOS EOS
end end
sig { params(all_fatal: T::Boolean).void }
def perform_build_from_source_checks(all_fatal: false) def perform_build_from_source_checks(all_fatal: false)
Diagnostic.checks(:fatal_build_from_source_checks) Diagnostic.checks(:fatal_build_from_source_checks)
Diagnostic.checks(:build_from_source_checks, fatal: all_fatal) Diagnostic.checks(:build_from_source_checks, fatal: all_fatal)
end end
sig { void }
def global_post_install; end def global_post_install; end
sig { void }
def check_prefix def check_prefix
if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) && if (Hardware::CPU.intel? || Hardware::CPU.in_rosetta2?) &&
HOMEBREW_PREFIX.to_s == HOMEBREW_MACOS_ARM_DEFAULT_PREFIX HOMEBREW_PREFIX.to_s == HOMEBREW_MACOS_ARM_DEFAULT_PREFIX
@ -64,6 +68,11 @@ module Homebrew
end end
end end
sig {
params(formula: Formula, head: T::Boolean, fetch_head: T::Boolean,
only_dependencies: T::Boolean, force: T::Boolean, quiet: T::Boolean,
skip_link: T::Boolean, overwrite: T::Boolean).returns(T::Boolean)
}
def install_formula?( def install_formula?(
formula, formula,
head: false, head: false,
@ -232,6 +241,16 @@ module Homebrew
false false
end end
sig {
params(formulae_to_install: T::Array[Formula], installed_on_request: T::Boolean,
installed_as_dependency: T::Boolean, build_bottle: T::Boolean, force_bottle: T::Boolean,
bottle_arch: T.nilable(String), ignore_deps: T::Boolean, only_deps: T::Boolean,
include_test_formulae: T::Array[String], build_from_source_formulae: T::Array[String],
cc: T.nilable(String), git: T::Boolean, interactive: T::Boolean, keep_tmp: T::Boolean,
debug_symbols: T::Boolean, force: T::Boolean, overwrite: T::Boolean, debug: T::Boolean,
quiet: T::Boolean, verbose: T::Boolean, dry_run: T::Boolean, skip_post_install: T::Boolean,
skip_link: T::Boolean).returns(T::Array[FormulaInstaller])
}
def formula_installers( def formula_installers(
formulae_to_install, formulae_to_install,
installed_on_request: true, installed_on_request: true,
@ -289,6 +308,53 @@ module Homebrew
end end
end end
sig { params(formula_installers: T::Array[FormulaInstaller]).returns(T::Array[FormulaInstaller]) }
def fetch_formulae(formula_installers)
formulae_names_to_install = formula_installers.map { |fi| fi.formula.name }
return formula_installers if formulae_names_to_install.empty?
formula_sentence = formulae_names_to_install.map { |name| Formatter.identifier(name) }.to_sentence
oh1 "Fetching downloads for: #{formula_sentence}", truncate: false
if EnvConfig.download_concurrency > 1
download_queue = Homebrew::DownloadQueue.new(pour: true)
formula_installers.each do |fi|
fi.download_queue = download_queue
end
end
valid_formula_installers = formula_installers.dup
begin
[:prelude_fetch, :prelude, :fetch].each do |step|
valid_formula_installers.select! do |fi|
fi.public_send(step)
true
rescue CannotInstallFormulaError => e
ofail e.message
false
rescue UnsatisfiedRequirements, DownloadError, ChecksumMismatchError => e
ofail "#{fi.formula}: #{e}"
false
end
download_queue&.fetch
end
ensure
download_queue&.shutdown
end
valid_formula_installers
end
sig {
params(formula_installers: T::Array[FormulaInstaller], installed_on_request: T::Boolean,
installed_as_dependency: T::Boolean, build_bottle: T::Boolean, force_bottle: T::Boolean,
bottle_arch: T.nilable(String), ignore_deps: T::Boolean, only_deps: T::Boolean,
include_test_formulae: T::Array[String], build_from_source_formulae: T::Array[String],
cc: T.nilable(String), git: T::Boolean, interactive: T::Boolean, keep_tmp: T::Boolean,
debug_symbols: T::Boolean, force: T::Boolean, overwrite: T::Boolean, debug: T::Boolean,
quiet: T::Boolean, verbose: T::Boolean, dry_run: T::Boolean,
skip_post_install: T::Boolean, skip_link: T::Boolean).void
}
def install_formulae( def install_formulae(
formula_installers, formula_installers,
installed_on_request: true, installed_on_request: true,
@ -328,41 +394,17 @@ module Homebrew
return return
end end
formula_sentence = formulae_names_to_install.map { |name| Formatter.identifier(name) }.to_sentence valid_formula_installers = fetch_formulae(formula_installers)
oh1 "Fetching downloads for: #{formula_sentence}", truncate: false
if EnvConfig.download_concurrency > 1
download_queue = Homebrew::DownloadQueue.new(pour: true)
formula_installers.each do |fi|
fi.download_queue = download_queue
end
end
valid_formula_installers = formula_installers.dup
begin
[:prelude_fetch, :prelude, :fetch].each do |step|
valid_formula_installers.select! do |fi|
fi.public_send(step)
true
rescue CannotInstallFormulaError => e
ofail e.message
false
rescue UnsatisfiedRequirements, DownloadError, ChecksumMismatchError => e
ofail "#{fi.formula}: #{e}"
false
end
download_queue&.fetch
end
ensure
download_queue&.shutdown
end
valid_formula_installers.each do |fi| valid_formula_installers.each do |fi|
install_formula(fi) formula = fi.formula
Cleanup.install_formula_clean!(fi.formula) upgrade = formula.linked? && formula.outdated? && !formula.head? && !Homebrew::EnvConfig.no_install_upgrade?
install_formula(fi, upgrade:)
Cleanup.install_formula_clean!(formula)
end end
end end
sig { params(formula: Formula, dependencies: T::Array[[Dependency, Options]]).void }
def print_dry_run_dependencies(formula, dependencies) def print_dry_run_dependencies(formula, dependencies)
return if dependencies.empty? return if dependencies.empty?
@ -373,6 +415,7 @@ module Homebrew
end end
# If asking the user is enabled, show dependency and size information. # If asking the user is enabled, show dependency and size information.
sig { params(formulae_installer: T::Array[FormulaInstaller], dependants: Homebrew::Upgrade::Dependents, args: Homebrew::CLI::Args).void }
def ask_formulae(formulae_installer, dependants, args:) def ask_formulae(formulae_installer, dependants, args:)
return if formulae_installer.empty? return if formulae_installer.empty?
@ -391,6 +434,7 @@ module Homebrew
ask_input ask_input
end end
sig { params(casks: T::Array[Cask::Cask]).void }
def ask_casks(casks) def ask_casks(casks)
return if casks.empty? return if casks.empty?
@ -400,8 +444,51 @@ module Homebrew
ask_input ask_input
end end
sig { params(formula_installer: FormulaInstaller, upgrade: T::Boolean).void }
def install_formula(formula_installer, upgrade:)
formula = formula_installer.formula
formula_installer.check_installation_already_attempted
if upgrade
Upgrade.print_upgrade_message(formula, formula_installer.options)
kegs = Upgrade.outdated_kegs(formula)
linked_kegs = kegs.select(&:linked?)
else
formula.print_tap_action
end
# first we unlink the currently active keg for this formula otherwise it is
# possible for the existing build to interfere with the build we are about to
# do! Seriously, it happens!
kegs.each(&:unlink) if kegs.present?
formula_installer.install
formula_installer.finish
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to upgrade f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
nil
ensure
# restore previous installation state if build failed
begin
linked_kegs&.each(&:link) unless formula&.latest_version_installed?
rescue
nil
end
end
private private
sig { params(formula: Formula).returns(T::Array[Keg]) }
def outdated_kegs(formula)
[formula, *formula.old_installed_formulae].map(&:linked_keg)
.select(&:directory?)
.map { |k| Keg.new(k.resolved_path) }
end
sig { params(all_fatal: T::Boolean).void }
def perform_preinstall_checks(all_fatal: false) def perform_preinstall_checks(all_fatal: false)
check_prefix check_prefix
check_cpu check_cpu
@ -410,6 +497,7 @@ module Homebrew
Diagnostic.checks(:fatal_preinstall_checks) Diagnostic.checks(:fatal_preinstall_checks)
end end
sig { void }
def attempt_directory_creation def attempt_directory_creation
Keg.must_exist_directories.each do |dir| Keg.must_exist_directories.each do |dir|
FileUtils.mkdir_p(dir) unless dir.exist? FileUtils.mkdir_p(dir) unless dir.exist?
@ -418,6 +506,7 @@ module Homebrew
end end
end end
sig { void }
def check_cpu def check_cpu
return unless Hardware::CPU.ppc? return unless Hardware::CPU.ppc?
@ -428,14 +517,7 @@ module Homebrew
EOS EOS
end end
def install_formula(formula_installer) sig { void }
formula = formula_installer.formula
upgrade = formula.linked? && formula.outdated? && !formula.head? && !Homebrew::EnvConfig.no_install_upgrade?
Upgrade.install_formula(formula_installer, upgrade:)
end
def ask_input def ask_input
ohai "Do you want to proceed with the installation? [Y/y/yes/N/n/no]" ohai "Do you want to proceed with the installation? [Y/y/yes/N/n/no]"
accepted_inputs = %w[y yes] accepted_inputs = %w[y yes]
@ -456,13 +538,16 @@ module Homebrew
end end
# Compute the total sizes (download, installed, and net) for the given formulae. # Compute the total sizes (download, installed, and net) for the given formulae.
sig { params(sized_formulae: T::Array[Formula], debug: T::Boolean).returns(T::Hash[Symbol, Integer]) }
def compute_total_sizes(sized_formulae, debug: false) def compute_total_sizes(sized_formulae, debug: false)
total_download_size = 0 total_download_size = 0
total_installed_size = 0 total_installed_size = 0
total_net_size = 0 total_net_size = 0
sized_formulae.select(&:bottle).each do |formula| sized_formulae.each do |formula|
bottle = formula.bottle bottle = formula.bottle
next unless bottle
# Fetch additional bottle metadata (if necessary). # Fetch additional bottle metadata (if necessary).
bottle.fetch_tab(quiet: !debug) bottle.fetch_tab(quiet: !debug)
@ -481,11 +566,15 @@ module Homebrew
net: total_net_size } net: total_net_size }
end end
sig {
params(formulae_installer: T::Array[FormulaInstaller],
dependants: Homebrew::Upgrade::Dependents).returns(T::Array[Formula])
}
def collect_dependencies(formulae_installer, dependants) def collect_dependencies(formulae_installer, dependants)
formulae_dependencies = formulae_installer.flat_map do |f| formulae_dependencies = formulae_installer.flat_map do |f|
[f.formula, f.compute_dependencies.flatten.grep(Dependency).flat_map(&:to_formula)] [f.formula, f.compute_dependencies.flatten.grep(Dependency).flat_map(&:to_formula)]
end.flatten.uniq end.flatten.uniq
formulae_dependencies.concat(dependants.upgradeable) if dependants&.upgradeable formulae_dependencies.concat(dependants.upgradeable) if dependants.upgradeable
formulae_dependencies.uniq formulae_dependencies.uniq
end end
end end

View File

@ -1,15 +1,33 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "formula_installer"
require "development_tools" require "development_tools"
require "messages" require "messages"
# Needed to handle circular require dependency.
# rubocop:disable Lint/EmptyClass
class FormulaInstaller; end
# rubocop:enable Lint/EmptyClass
module Homebrew module Homebrew
module Reinstall module Reinstall
# struct to keep context of the current installer, keg, formula and option class InstallationContext < T::Struct
InstallationContext = Struct.new(:formula_installer, :keg, :formula, :options) const :formula_installer, ::FormulaInstaller
def self.build_install_context( const :keg, T.nilable(Keg)
const :formula, Formula
const :options, Options
end
class << self
sig {
params(
formula: Formula, flags: T::Array[String], force_bottle: T::Boolean,
build_from_source_formulae: T::Array[String], interactive: T::Boolean, keep_tmp: T::Boolean,
debug_symbols: T::Boolean, force: T::Boolean, debug: T::Boolean, quiet: T::Boolean,
verbose: T::Boolean, git: T::Boolean
).returns(InstallationContext)
}
def build_install_context(
formula, formula,
flags:, flags:,
force_bottle: false, force_bottle: false,
@ -43,7 +61,7 @@ module Homebrew
options |= formula.build.used_options options |= formula.build.used_options
options &= formula.options options &= formula.options
fi = FormulaInstaller.new( formula_installer = FormulaInstaller.new(
formula, formula,
**{ **{
options:, options:,
@ -63,30 +81,17 @@ module Homebrew
verbose:, verbose:,
}.compact, }.compact,
) )
InstallationContext.new(fi, keg, formula, options) InstallationContext.new(formula_installer:, keg:, formula:, options:)
end end
def self.reinstall_formula( sig { params(install_context: InstallationContext).void }
install_context, def reinstall_formula(install_context)
flags:,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
debug_symbols: false,
force: false,
debug: false,
quiet: false,
verbose: false,
git: false
)
formula_installer = install_context.formula_installer formula_installer = install_context.formula_installer
keg = install_context.keg keg = install_context.keg
formula = install_context.formula formula = install_context.formula
options = install_context.options options = install_context.options
link_keg = keg&.linked? link_keg = keg&.linked?
formula_installer.prelude verbose = formula_installer.verbose?
formula_installer.fetch
oh1 "Reinstalling #{Formatter.identifier(formula.full_name)} #{options.to_a.join " "}" oh1 "Reinstalling #{Formatter.identifier(formula.full_name)} #{options.to_a.join " "}"
@ -96,20 +101,26 @@ module Homebrew
nil nil
# Any other exceptions we want to restore the previous keg and report the error. # Any other exceptions we want to restore the previous keg and report the error.
rescue Exception # rubocop:disable Lint/RescueException rescue Exception # rubocop:disable Lint/RescueException
ignore_interrupts { restore_backup(keg, link_keg, verbose:) } ignore_interrupts { restore_backup(keg, link_keg, verbose:) if keg }
raise raise
else else
if keg
backup_keg = backup_path(keg)
begin begin
FileUtils.rm_r(backup_path(keg)) if backup_path(keg).exist? FileUtils.rm_r(backup_keg) if backup_keg.exist?
rescue Errno::EACCES, Errno::ENOTEMPTY rescue Errno::EACCES, Errno::ENOTEMPTY
odie <<~EOS odie <<~EOS
Could not remove #{backup_path(keg).parent.basename} backup keg! Do so manually: Could not remove #{backup_keg.parent.basename} backup keg! Do so manually:
sudo rm -rf #{backup_path(keg)} sudo rm -rf #{backup_keg}
EOS EOS
end end
end end
end
def self.backup(keg) private
sig { params(keg: Keg).void }
def backup(keg)
keg.unlink keg.unlink
begin begin
keg.rename backup_path(keg) keg.rename backup_path(keg)
@ -120,23 +131,23 @@ module Homebrew
EOS EOS
end end
end end
private_class_method :backup
def self.restore_backup(keg, keg_was_linked, verbose:) sig { params(keg: Keg, keg_was_linked: T::Boolean, verbose: T::Boolean).void }
def restore_backup(keg, keg_was_linked, verbose:)
path = backup_path(keg) path = backup_path(keg)
return unless path.directory? return unless path.directory?
FileUtils.rm_r(Pathname.new(keg)) if keg.exist? FileUtils.rm_r(Pathname.new(keg)) if keg.exist?
path.rename keg path.rename keg.to_s
keg.link(verbose:) if keg_was_linked keg.link(verbose:) if keg_was_linked
end end
private_class_method :restore_backup
def self.backup_path(path) sig { params(keg: Keg).returns(Pathname) }
Pathname.new "#{path}.reinstall" def backup_path(keg)
Pathname.new "#{keg}.reinstall"
end
end end
private_class_method :backup_path
end end
end end

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "utils" require "utils"
require "cask/info"
RSpec.describe Cask::Info, :cask do RSpec.describe Cask::Info, :cask do
let(:args) { instance_double(Homebrew::Cmd::Info::Args) } let(:args) { instance_double(Homebrew::Cmd::Info::Args) }

View File

@ -208,6 +208,7 @@ RSpec.shared_context "integration test" do # rubocop:disable RSpec/ContextWordin
def install_test_formula(name, content = nil, build_bottle: false) def install_test_formula(name, content = nil, build_bottle: false)
setup_test_formula(name, content) setup_test_formula(name, content)
fi = FormulaInstaller.new(Formula[name], build_bottle:, installed_on_request: true) fi = FormulaInstaller.new(Formula[name], build_bottle:, installed_on_request: true)
fi.prelude_fetch
fi.prelude fi.prelude
fi.fetch fi.fetch
fi.install fi.install

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "reinstall" require "reinstall"
@ -11,9 +11,23 @@ require "utils/topological_hash"
module Homebrew module Homebrew
# Helper functions for upgrading formulae. # Helper functions for upgrading formulae.
module Upgrade module Upgrade
Dependents = Struct.new(:upgradeable, :pinned, :skipped) class Dependents < T::Struct
const :upgradeable, T::Array[Formula]
const :pinned, T::Array[Formula]
const :skipped, T::Array[Formula]
end
def self.formula_installers( class << self
sig {
params(
formulae_to_install: T::Array[Formula], flags: T::Array[String], dry_run: T::Boolean,
force_bottle: T::Boolean, build_from_source_formulae: T::Array[String],
dependents: T::Boolean, interactive: T::Boolean, keep_tmp: T::Boolean,
debug_symbols: T::Boolean, force: T::Boolean, overwrite: T::Boolean,
debug: T::Boolean, quiet: T::Boolean, verbose: T::Boolean
).returns(T::Array[FormulaInstaller])
}
def formula_installers(
formulae_to_install, formulae_to_install,
flags:, flags:,
dry_run: false, dry_run: false,
@ -29,7 +43,7 @@ module Homebrew
quiet: false, quiet: false,
verbose: false verbose: false
) )
return if formulae_to_install.empty? return [] if formulae_to_install.empty?
# Sort keg-only before non-keg-only formulae to avoid any needless conflicts # Sort keg-only before non-keg-only formulae to avoid any needless conflicts
# with outdated, non-keg-only versions of formulae being upgraded. # with outdated, non-keg-only versions of formulae being upgraded.
@ -47,7 +61,9 @@ module Homebrew
begin begin
formulae_to_install = dependency_graph.tsort & formulae_to_install formulae_to_install = dependency_graph.tsort & formulae_to_install
rescue TSort::Cyclic rescue TSort::Cyclic
raise CyclicDependencyError, dependency_graph.strongly_connected_components if Homebrew::EnvConfig.developer? if Homebrew::EnvConfig.developer?
raise CyclicDependencyError, dependency_graph.strongly_connected_components
end
end end
formulae_to_install.filter_map do |formula| formulae_to_install.filter_map do |formula|
@ -70,8 +86,10 @@ module Homebrew
fi.fetch_bottle_tab(quiet: !debug) fi.fetch_bottle_tab(quiet: !debug)
all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash| all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash|
minimum_version = Version.new(hash["version"]) if hash["version"].present? minimum_version = if (version = hash["version"])
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"]) Version.new(version)
end
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"].to_i)
end end
if !dry_run && dependents && all_runtime_deps_installed if !dry_run && dependents && all_runtime_deps_installed
@ -91,21 +109,12 @@ module Homebrew
end end
end end
def self.upgrade_formulae(formula_installers, dry_run: false, verbose: false) sig { params(formula_installers: T::Array[FormulaInstaller], dry_run: T::Boolean, verbose: T::Boolean).void }
valid_formula_installers = formula_installers.dup def upgrade_formulae(formula_installers, dry_run: false, verbose: false)
valid_formula_installers = if dry_run
unless dry_run formula_installers
valid_formula_installers.select! do |fi| else
fi.prelude Install.fetch_formulae(formula_installers)
fi.fetch
true
rescue CannotInstallFormulaError => e
ofail e
false
rescue UnsatisfiedRequirements, DownloadError => e
ofail "#{fi.formula.full_name}: #{e}"
false
end
end end
valid_formula_installers.each do |fi| valid_formula_installers.each do |fi|
@ -114,13 +123,15 @@ module Homebrew
end end
end end
private_class_method def self.outdated_kegs(formula) sig { params(formula: Formula).returns(T::Array[Keg]) }
def outdated_kegs(formula)
[formula, *formula.old_installed_formulae].map(&:linked_keg) [formula, *formula.old_installed_formulae].map(&:linked_keg)
.select(&:directory?) .select(&:directory?)
.map { |k| Keg.new(k.resolved_path) } .map { |k| Keg.new(k.resolved_path) }
end end
private_class_method def self.print_upgrade_message(formula, fi_options) sig { params(formula: Formula, fi_options: Options).void }
def print_upgrade_message(formula, fi_options)
version_upgrade = if formula.optlinked? version_upgrade = if formula.optlinked?
"#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}" "#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}"
else else
@ -130,7 +141,295 @@ module Homebrew
puts " #{version_upgrade} #{fi_options.to_a.join(" ")}" puts " #{version_upgrade} #{fi_options.to_a.join(" ")}"
end end
private_class_method def self.create_formula_installer( sig {
params(
formulae: T::Array[Formula], flags: T::Array[String], dry_run: T::Boolean,
ask: T::Boolean, installed_on_request: T::Boolean, force_bottle: T::Boolean,
build_from_source_formulae: T::Array[String], interactive: T::Boolean,
keep_tmp: T::Boolean, debug_symbols: T::Boolean, force: T::Boolean,
debug: T::Boolean, quiet: T::Boolean, verbose: T::Boolean
).returns(Dependents)
}
def dependants(
formulae,
flags:,
dry_run: false,
ask: false,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
debug_symbols: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
no_dependents = Dependents.new(upgradeable: [], pinned: [], skipped: [])
if Homebrew::EnvConfig.no_installed_dependents_check?
unless Homebrew::EnvConfig.no_env_hints?
opoo <<~EOS
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK is set: not checking for outdated
dependents or dependents with broken linkage!
EOS
end
return no_dependents
end
formulae_to_install = formulae.reject { |f| f.core_formula? && f.versioned_formula? }
return no_dependents if formulae_to_install.empty?
already_broken = check_broken_dependents(formulae_to_install)
# TODO: this should be refactored to use FormulaInstaller new logic
outdated = formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
.uniq
.select(&:outdated?)
# Ensure we never attempt a source build for outdated dependents of upgraded formulae.
outdated, skipped = outdated.partition do |dependent|
dependent.bottled? && dependent.deps.map(&:to_formula).all?(&:bottled?)
end
return no_dependents if outdated.blank? && already_broken.blank?
outdated -= formulae_to_install if dry_run
upgradeable = outdated.reject(&:pinned?)
.sort { |a, b| depends_on(a, b) }
pinned = outdated.select(&:pinned?)
.sort { |a, b| depends_on(a, b) }
Dependents.new(upgradeable:, pinned:, skipped:)
end
sig {
params(deps: Dependents, formulae: T::Array[Formula], flags: T::Array[String],
dry_run: T::Boolean, installed_on_request: T::Boolean, force_bottle: T::Boolean,
build_from_source_formulae: T::Array[String], interactive: T::Boolean, keep_tmp: T::Boolean,
debug_symbols: T::Boolean, force: T::Boolean, debug: T::Boolean, quiet: T::Boolean,
verbose: T::Boolean).void
}
def upgrade_dependents(deps, formulae,
flags:,
dry_run: false,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
debug_symbols: false,
force: false,
debug: false,
quiet: false,
verbose: false)
return if deps.blank?
upgradeable = deps.upgradeable
pinned = deps.pinned
skipped = deps.skipped
if pinned.present?
plural = Utils.pluralize("dependent", pinned.count)
opoo "Not upgrading #{pinned.count} pinned #{plural}:"
puts(pinned.map do |f|
"#{f.full_specified_name} #{f.pkg_version}"
end.join(", "))
end
if skipped.present?
opoo <<~EOS
The following dependents of upgraded formulae are outdated but will not
be upgraded because they are not bottled:
#{skipped * "\n "}
EOS
end
upgradeable.reject! { |f| FormulaInstaller.installed.include?(f) }
# Print the upgradable dependents.
if upgradeable.blank?
ohai "No outdated dependents to upgrade!" unless dry_run
else
installed_formulae = (dry_run ? formulae : FormulaInstaller.installed.to_a).dup
formula_plural = Utils.pluralize("formula", installed_formulae.count, plural: "e")
upgrade_verb = dry_run ? "Would upgrade" : "Upgrading"
ohai "#{upgrade_verb} #{Utils.pluralize("dependent", upgradeable.count,
include_count: true)} of upgraded #{formula_plural}:"
puts_no_installed_dependents_check_disable_message_if_not_already!
formulae_upgrades = upgradeable.map do |f|
name = f.full_specified_name
if f.optlinked?
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
else
"#{name} #{f.pkg_version}"
end
end
puts formulae_upgrades.join(", ")
end
return if upgradeable.blank?
unless dry_run
dependent_installers = formula_installers(
upgradeable,
flags:,
force_bottle:,
build_from_source_formulae:,
dependents: true,
interactive:,
keep_tmp:,
debug_symbols:,
force:,
debug:,
quiet:,
verbose:,
)
upgrade_formulae(dependent_installers, dry_run:, verbose:)
end
# Update installed formulae after upgrading
installed_formulae = FormulaInstaller.installed.to_a
# Assess the dependents tree again now we've upgraded.
unless dry_run
oh1 "Checking for dependents of upgraded formulae..."
puts_no_installed_dependents_check_disable_message_if_not_already!
end
broken_dependents = check_broken_dependents(installed_formulae)
if broken_dependents.blank?
if dry_run
ohai "No currently broken dependents found!"
opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
else
ohai "No broken dependents found!"
end
return
end
reinstallable_broken_dependents =
broken_dependents.reject(&:outdated?)
.reject(&:pinned?)
.sort { |a, b| depends_on(a, b) }
outdated_pinned_broken_dependents =
broken_dependents.select(&:outdated?)
.select(&:pinned?)
.sort { |a, b| depends_on(a, b) }
# Print the pinned dependents.
if outdated_pinned_broken_dependents.present?
count = outdated_pinned_broken_dependents.count
plural = Utils.pluralize("dependent", outdated_pinned_broken_dependents.count)
onoe "Not reinstalling #{count} broken and outdated, but pinned #{plural}:"
$stderr.puts(outdated_pinned_broken_dependents.map do |f|
"#{f.full_specified_name} #{f.pkg_version}"
end.join(", "))
end
# Print the broken dependents.
if reinstallable_broken_dependents.blank?
ohai "No broken dependents to reinstall!"
else
ohai "Reinstalling #{Utils.pluralize("dependent", reinstallable_broken_dependents.count,
include_count: true)} with broken linkage from source:"
puts_no_installed_dependents_check_disable_message_if_not_already!
puts reinstallable_broken_dependents.map(&:full_specified_name)
.join(", ")
end
return if dry_run
reinstall_contexts = reinstallable_broken_dependents.map do |formula|
Reinstall.build_install_context(
formula,
flags:,
force_bottle:,
build_from_source_formulae: build_from_source_formulae + [formula.full_name],
interactive:,
keep_tmp:,
debug_symbols:,
force:,
debug:,
quiet:,
verbose:,
)
end
valid_formula_installers = Install.fetch_formulae(reinstall_contexts.map(&:formula_installer))
reinstall_contexts.each do |reinstall_context|
next unless valid_formula_installers.include?(reinstall_context.formula_installer)
Reinstall.reinstall_formula(reinstall_context)
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to reinstall f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
nil
rescue CannotInstallFormulaError, DownloadError => e
ofail e
rescue BuildError => e
e.dump(verbose:)
puts
Homebrew.failed = true
end
end
private
sig { params(formula_installer: FormulaInstaller, dry_run: T::Boolean, verbose: T::Boolean).void }
def upgrade_formula(formula_installer, dry_run: false, verbose: false)
formula = formula_installer.formula
if dry_run
Install.print_dry_run_dependencies(formula, formula_installer.compute_dependencies) do |f|
name = f.full_specified_name
if f.optlinked?
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
else
"#{name} #{f.pkg_version}"
end
end
return
end
Install.install_formula(formula_installer, upgrade: true)
rescue BuildError => e
e.dump(verbose:)
puts
Homebrew.failed = true
end
sig { params(installed_formulae: T::Array[Formula]).returns(T::Array[Formula]) }
def check_broken_dependents(installed_formulae)
CacheStoreDatabase.use(:linkage) do |db|
installed_formulae.flat_map(&:runtime_installed_formula_dependents)
.uniq
.select do |f|
keg = f.any_installed_keg
next unless keg
next unless keg.directory?
LinkageChecker.new(keg, cache_db: db)
.broken_library_linkage?
end.compact
end
end
sig { void }
def puts_no_installed_dependents_check_disable_message_if_not_already!
return if Homebrew::EnvConfig.no_env_hints?
return if Homebrew::EnvConfig.no_installed_dependents_check?
return if @puts_no_installed_dependents_check_disable_message_if_not_already
puts "Disable this behaviour by setting HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK."
puts "Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`)."
@puts_no_installed_dependents_check_disable_message_if_not_already = T.let(true, T.nilable(T::Boolean))
end
sig {
params(formula: Formula, flags: T::Array[String], force_bottle: T::Boolean,
build_from_source_formulae: T::Array[String], interactive: T::Boolean,
keep_tmp: T::Boolean, debug_symbols: T::Boolean, force: T::Boolean,
overwrite: T::Boolean, debug: T::Boolean, quiet: T::Boolean, verbose: T::Boolean).returns(FormulaInstaller)
}
def create_formula_installer(
formula, formula,
flags:, flags:,
force_bottle: false, force_bottle: false,
@ -190,318 +489,15 @@ module Homebrew
) )
end end
def self.upgrade_formula(formula_installer, dry_run: false, verbose: false) sig { params(one: Formula, two: Formula).returns(Integer) }
formula = formula_installer.formula def depends_on(one, two)
if dry_run
Install.print_dry_run_dependencies(formula, formula_installer.compute_dependencies) do |f|
name = f.full_specified_name
if f.optlinked?
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
else
"#{name} #{f.pkg_version}"
end
end
return
end
install_formula(formula_installer, upgrade: true)
rescue BuildError => e
e.dump(verbose:)
puts
Homebrew.failed = true
end
def self.install_formula(formula_installer, upgrade:)
formula = formula_installer.formula
formula_installer.check_installation_already_attempted
if upgrade
print_upgrade_message(formula, formula_installer.options)
kegs = outdated_kegs(formula)
linked_kegs = kegs.select(&:linked?)
else
formula.print_tap_action
end
# first we unlink the currently active keg for this formula otherwise it is
# possible for the existing build to interfere with the build we are about to
# do! Seriously, it happens!
kegs.each(&:unlink) if kegs.present?
formula_installer.install
formula_installer.finish
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to upgrade f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
nil
ensure
# restore previous installation state if build failed
begin
linked_kegs&.each(&:link) unless formula.latest_version_installed?
rescue
nil
end
end
private_class_method def self.check_broken_dependents(installed_formulae)
CacheStoreDatabase.use(:linkage) do |db|
installed_formulae.flat_map(&:runtime_installed_formula_dependents)
.uniq
.select do |f|
keg = f.any_installed_keg
next unless keg
next unless keg.directory?
LinkageChecker.new(keg, cache_db: db)
.broken_library_linkage?
end.compact
end
end
def self.puts_no_installed_dependents_check_disable_message_if_not_already!
return if Homebrew::EnvConfig.no_env_hints?
return if Homebrew::EnvConfig.no_installed_dependents_check?
return if @puts_no_installed_dependents_check_disable_message_if_not_already
puts "Disable this behaviour by setting HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK."
puts "Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`)."
@puts_no_installed_dependents_check_disable_message_if_not_already = true
end
def self.dependants(
formulae,
flags:,
dry_run: false,
ask: false,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
debug_symbols: false,
force: false,
debug: false,
quiet: false,
verbose: false
)
if Homebrew::EnvConfig.no_installed_dependents_check?
unless Homebrew::EnvConfig.no_env_hints?
opoo <<~EOS
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK is set: not checking for outdated
dependents or dependents with broken linkage!
EOS
end
return
end
formulae_to_install = formulae.dup
formulae_to_install.reject! { |f| f.core_formula? && f.versioned_formula? }
return if formulae_to_install.empty?
already_broken_dependents = check_broken_dependents(formulae_to_install)
# TODO: this should be refactored to use FormulaInstaller new logic
outdated_dependents =
formulae_to_install.flat_map(&:runtime_installed_formula_dependents)
.uniq
.select(&:outdated?)
# Ensure we never attempt a source build for outdated dependents of upgraded formulae.
outdated_dependents, skipped_dependents = outdated_dependents.partition do |dependent|
dependent.bottled? && dependent.deps.map(&:to_formula).all?(&:bottled?)
end
return if outdated_dependents.blank? && already_broken_dependents.blank?
outdated_dependents -= formulae_to_install if dry_run
upgradeable_dependents =
outdated_dependents.reject(&:pinned?)
.sort { |a, b| depends_on(a, b) }
pinned_dependents =
outdated_dependents.select(&:pinned?)
.sort { |a, b| depends_on(a, b) }
Dependents.new(upgradeable_dependents, pinned_dependents, skipped_dependents)
end
def self.upgrade_dependents(deps, formulae,
flags:,
dry_run: false,
installed_on_request: false,
force_bottle: false,
build_from_source_formulae: [],
interactive: false,
keep_tmp: false,
debug_symbols: false,
force: false,
debug: false,
quiet: false,
verbose: false)
return if deps.blank?
upgradeable = deps.upgradeable
pinned = deps.pinned
skipped = deps.skipped
if pinned.present?
plural = Utils.pluralize("dependent", pinned.count)
opoo "Not upgrading #{pinned.count} pinned #{plural}:"
puts(pinned.map do |f|
"#{f.full_specified_name} #{f.pkg_version}"
end.join(", "))
end
if skipped.present?
opoo <<~EOS
The following dependents of upgraded formulae are outdated but will not
be upgraded because they are not bottled:
#{skipped * "\n "}
EOS
end
upgradeable.reject! { |f| FormulaInstaller.installed.include?(f) }
# Print the upgradable dependents.
if upgradeable.blank?
ohai "No outdated dependents to upgrade!" unless dry_run
else
installed_formulae = (dry_run ? formulae : FormulaInstaller.installed.to_a).dup
formula_plural = Utils.pluralize("formula", installed_formulae.count, plural: "e")
upgrade_verb = dry_run ? "Would upgrade" : "Upgrading"
ohai "#{upgrade_verb} #{Utils.pluralize("dependent", upgradeable.count,
include_count: true)} of upgraded #{formula_plural}:"
Upgrade.puts_no_installed_dependents_check_disable_message_if_not_already!
formulae_upgrades = upgradeable.map do |f|
name = f.full_specified_name
if f.optlinked?
"#{name} #{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
else
"#{name} #{f.pkg_version}"
end
end
puts formulae_upgrades.join(", ")
end
return if upgradeable.blank?
unless dry_run
dependent_installers = formula_installers(
upgradeable,
flags:,
force_bottle:,
build_from_source_formulae:,
dependents: true,
interactive:,
keep_tmp:,
debug_symbols:,
force:,
debug:,
quiet:,
verbose:,
)
upgrade_formulae(dependent_installers, dry_run: dry_run, verbose: verbose)
end
# Update installed formulae after upgrading
installed_formulae = FormulaInstaller.installed.to_a
# Assess the dependents tree again now we've upgraded.
unless dry_run
oh1 "Checking for dependents of upgraded formulae..."
Upgrade.puts_no_installed_dependents_check_disable_message_if_not_already!
end
broken_dependents = check_broken_dependents(installed_formulae)
if broken_dependents.blank?
if dry_run
ohai "No currently broken dependents found!"
opoo "If they are broken by the upgrade they will also be upgraded or reinstalled."
else
ohai "No broken dependents found!"
end
return
end
reinstallable_broken_dependents =
broken_dependents.reject(&:outdated?)
.reject(&:pinned?)
.sort { |a, b| depends_on(a, b) }
outdated_pinned_broken_dependents =
broken_dependents.select(&:outdated?)
.select(&:pinned?)
.sort { |a, b| depends_on(a, b) }
# Print the pinned dependents.
if outdated_pinned_broken_dependents.present?
count = outdated_pinned_broken_dependents.count
plural = Utils.pluralize("dependent", outdated_pinned_broken_dependents.count)
onoe "Not reinstalling #{count} broken and outdated, but pinned #{plural}:"
$stderr.puts(outdated_pinned_broken_dependents.map do |f|
"#{f.full_specified_name} #{f.pkg_version}"
end.join(", "))
end
# Print the broken dependents.
if reinstallable_broken_dependents.blank?
ohai "No broken dependents to reinstall!"
else
ohai "Reinstalling #{Utils.pluralize("dependent", reinstallable_broken_dependents.count,
include_count: true)} with broken linkage from source:"
Upgrade.puts_no_installed_dependents_check_disable_message_if_not_already!
puts reinstallable_broken_dependents.map(&:full_specified_name)
.join(", ")
end
return if dry_run
reinstallable_broken_dependents.each do |formula|
formula_installer = Reinstall.build_install_context(
formula,
flags:,
force_bottle:,
build_from_source_formulae: build_from_source_formulae + [formula.full_name],
interactive:,
keep_tmp:,
debug_symbols:,
force:,
debug:,
quiet:,
verbose:,
)
Reinstall.reinstall_formula(
formula_installer,
flags:,
force_bottle:,
build_from_source_formulae:,
interactive:,
keep_tmp:,
debug_symbols:,
force:,
debug:,
quiet:,
verbose:,
)
rescue FormulaInstallationAlreadyAttemptedError
# We already attempted to reinstall f as part of the dependency tree of
# another formula. In that case, don't generate an error, just move on.
nil
rescue CannotInstallFormulaError, DownloadError => e
ofail e
rescue BuildError => e
e.dump(verbose:)
puts
Homebrew.failed = true
end
end
private_class_method def self.depends_on(one, two)
if one.any_installed_keg if one.any_installed_keg
&.runtime_dependencies &.runtime_dependencies
&.any? { |dependency| dependency["full_name"] == two.full_name } &.any? { |dependency| dependency["full_name"] == two.full_name }
1 1
else else
one <=> two T.must(one <=> two)
end
end end
end end
end end