Merge pull request #20296 from Homebrew/upgrade_reinstall_download_queue
Optionally use DownloadQueue for reinstall, upgrade.
This commit is contained in:
commit
097e2a351c
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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?
|
||||||
|
|||||||
@ -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?
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -1,142 +1,153 @@
|
|||||||
# 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)
|
||||||
formula,
|
const :formula, Formula
|
||||||
flags:,
|
const :options, Options
|
||||||
force_bottle: false,
|
end
|
||||||
build_from_source_formulae: [],
|
|
||||||
interactive: false,
|
|
||||||
keep_tmp: false,
|
|
||||||
debug_symbols: false,
|
|
||||||
force: false,
|
|
||||||
debug: false,
|
|
||||||
quiet: false,
|
|
||||||
verbose: false,
|
|
||||||
git: false
|
|
||||||
)
|
|
||||||
if formula.opt_prefix.directory?
|
|
||||||
keg = Keg.new(formula.opt_prefix.resolved_path)
|
|
||||||
tab = keg.tab
|
|
||||||
link_keg = keg.linked?
|
|
||||||
installed_as_dependency = tab.installed_as_dependency == true
|
|
||||||
installed_on_request = tab.installed_on_request == true
|
|
||||||
build_bottle = tab.built_bottle?
|
|
||||||
backup keg
|
|
||||||
else
|
|
||||||
link_keg = nil
|
|
||||||
installed_as_dependency = false
|
|
||||||
installed_on_request = true
|
|
||||||
build_bottle = false
|
|
||||||
end
|
|
||||||
|
|
||||||
build_options = BuildOptions.new(Options.create(flags), formula.options)
|
class << self
|
||||||
options = build_options.used_options
|
sig {
|
||||||
options |= formula.build.used_options
|
params(
|
||||||
options &= formula.options
|
formula: Formula, flags: T::Array[String], force_bottle: T::Boolean,
|
||||||
|
build_from_source_formulae: T::Array[String], interactive: T::Boolean, keep_tmp: T::Boolean,
|
||||||
fi = FormulaInstaller.new(
|
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:,
|
||||||
options:,
|
force_bottle: false,
|
||||||
link_keg:,
|
build_from_source_formulae: [],
|
||||||
installed_as_dependency:,
|
interactive: false,
|
||||||
installed_on_request:,
|
keep_tmp: false,
|
||||||
build_bottle:,
|
debug_symbols: false,
|
||||||
force_bottle:,
|
force: false,
|
||||||
build_from_source_formulae:,
|
debug: false,
|
||||||
git:,
|
quiet: false,
|
||||||
interactive:,
|
verbose: false,
|
||||||
keep_tmp:,
|
git: false
|
||||||
debug_symbols:,
|
|
||||||
force:,
|
|
||||||
debug:,
|
|
||||||
quiet:,
|
|
||||||
verbose:,
|
|
||||||
}.compact,
|
|
||||||
)
|
)
|
||||||
InstallationContext.new(fi, keg, formula, options)
|
if formula.opt_prefix.directory?
|
||||||
end
|
keg = Keg.new(formula.opt_prefix.resolved_path)
|
||||||
|
tab = keg.tab
|
||||||
|
link_keg = keg.linked?
|
||||||
|
installed_as_dependency = tab.installed_as_dependency == true
|
||||||
|
installed_on_request = tab.installed_on_request == true
|
||||||
|
build_bottle = tab.built_bottle?
|
||||||
|
backup keg
|
||||||
|
else
|
||||||
|
link_keg = nil
|
||||||
|
installed_as_dependency = false
|
||||||
|
installed_on_request = true
|
||||||
|
build_bottle = false
|
||||||
|
end
|
||||||
|
|
||||||
def self.reinstall_formula(
|
build_options = BuildOptions.new(Options.create(flags), formula.options)
|
||||||
install_context,
|
options = build_options.used_options
|
||||||
flags:,
|
options |= formula.build.used_options
|
||||||
force_bottle: false,
|
options &= formula.options
|
||||||
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
|
|
||||||
keg = install_context.keg
|
|
||||||
formula = install_context.formula
|
|
||||||
options = install_context.options
|
|
||||||
link_keg = keg&.linked?
|
|
||||||
formula_installer.prelude
|
|
||||||
formula_installer.fetch
|
|
||||||
|
|
||||||
oh1 "Reinstalling #{Formatter.identifier(formula.full_name)} #{options.to_a.join " "}"
|
formula_installer = FormulaInstaller.new(
|
||||||
|
formula,
|
||||||
|
**{
|
||||||
|
options:,
|
||||||
|
link_keg:,
|
||||||
|
installed_as_dependency:,
|
||||||
|
installed_on_request:,
|
||||||
|
build_bottle:,
|
||||||
|
force_bottle:,
|
||||||
|
build_from_source_formulae:,
|
||||||
|
git:,
|
||||||
|
interactive:,
|
||||||
|
keep_tmp:,
|
||||||
|
debug_symbols:,
|
||||||
|
force:,
|
||||||
|
debug:,
|
||||||
|
quiet:,
|
||||||
|
verbose:,
|
||||||
|
}.compact,
|
||||||
|
)
|
||||||
|
InstallationContext.new(formula_installer:, keg:, formula:, options:)
|
||||||
|
end
|
||||||
|
|
||||||
formula_installer.install
|
sig { params(install_context: InstallationContext).void }
|
||||||
formula_installer.finish
|
def reinstall_formula(install_context)
|
||||||
rescue FormulaInstallationAlreadyAttemptedError
|
formula_installer = install_context.formula_installer
|
||||||
nil
|
keg = install_context.keg
|
||||||
# Any other exceptions we want to restore the previous keg and report the error.
|
formula = install_context.formula
|
||||||
rescue Exception # rubocop:disable Lint/RescueException
|
options = install_context.options
|
||||||
ignore_interrupts { restore_backup(keg, link_keg, verbose:) }
|
link_keg = keg&.linked?
|
||||||
raise
|
verbose = formula_installer.verbose?
|
||||||
else
|
|
||||||
begin
|
oh1 "Reinstalling #{Formatter.identifier(formula.full_name)} #{options.to_a.join " "}"
|
||||||
FileUtils.rm_r(backup_path(keg)) if backup_path(keg).exist?
|
|
||||||
rescue Errno::EACCES, Errno::ENOTEMPTY
|
formula_installer.install
|
||||||
odie <<~EOS
|
formula_installer.finish
|
||||||
Could not remove #{backup_path(keg).parent.basename} backup keg! Do so manually:
|
rescue FormulaInstallationAlreadyAttemptedError
|
||||||
sudo rm -rf #{backup_path(keg)}
|
nil
|
||||||
EOS
|
# Any other exceptions we want to restore the previous keg and report the error.
|
||||||
|
rescue Exception # rubocop:disable Lint/RescueException
|
||||||
|
ignore_interrupts { restore_backup(keg, link_keg, verbose:) if keg }
|
||||||
|
raise
|
||||||
|
else
|
||||||
|
if keg
|
||||||
|
backup_keg = backup_path(keg)
|
||||||
|
begin
|
||||||
|
FileUtils.rm_r(backup_keg) if backup_keg.exist?
|
||||||
|
rescue Errno::EACCES, Errno::ENOTEMPTY
|
||||||
|
odie <<~EOS
|
||||||
|
Could not remove #{backup_keg.parent.basename} backup keg! Do so manually:
|
||||||
|
sudo rm -rf #{backup_keg}
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
sig { params(keg: Keg).void }
|
||||||
|
def backup(keg)
|
||||||
|
keg.unlink
|
||||||
|
begin
|
||||||
|
keg.rename backup_path(keg)
|
||||||
|
rescue Errno::EACCES, Errno::ENOTEMPTY
|
||||||
|
odie <<~EOS
|
||||||
|
Could not rename #{keg.name} keg! Check/fix its permissions:
|
||||||
|
sudo chown -R #{ENV.fetch("USER", "$(whoami)")} #{keg}
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
return unless path.directory?
|
||||||
|
|
||||||
|
FileUtils.rm_r(Pathname.new(keg)) if keg.exist?
|
||||||
|
|
||||||
|
path.rename keg.to_s
|
||||||
|
keg.link(verbose:) if keg_was_linked
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { params(keg: Keg).returns(Pathname) }
|
||||||
|
def backup_path(keg)
|
||||||
|
Pathname.new "#{keg}.reinstall"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.backup(keg)
|
|
||||||
keg.unlink
|
|
||||||
begin
|
|
||||||
keg.rename backup_path(keg)
|
|
||||||
rescue Errno::EACCES, Errno::ENOTEMPTY
|
|
||||||
odie <<~EOS
|
|
||||||
Could not rename #{keg.name} keg! Check/fix its permissions:
|
|
||||||
sudo chown -R #{ENV.fetch("USER", "$(whoami)")} #{keg}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
private_class_method :backup
|
|
||||||
|
|
||||||
def self.restore_backup(keg, keg_was_linked, verbose:)
|
|
||||||
path = backup_path(keg)
|
|
||||||
|
|
||||||
return unless path.directory?
|
|
||||||
|
|
||||||
FileUtils.rm_r(Pathname.new(keg)) if keg.exist?
|
|
||||||
|
|
||||||
path.rename keg
|
|
||||||
keg.link(verbose:) if keg_was_linked
|
|
||||||
end
|
|
||||||
private_class_method :restore_backup
|
|
||||||
|
|
||||||
def self.backup_path(path)
|
|
||||||
Pathname.new "#{path}.reinstall"
|
|
||||||
end
|
|
||||||
private_class_method :backup_path
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -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) }
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,52 +11,471 @@ 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
|
||||||
formulae_to_install,
|
sig {
|
||||||
flags:,
|
params(
|
||||||
dry_run: false,
|
formulae_to_install: T::Array[Formula], flags: T::Array[String], dry_run: T::Boolean,
|
||||||
force_bottle: false,
|
force_bottle: T::Boolean, build_from_source_formulae: T::Array[String],
|
||||||
build_from_source_formulae: [],
|
dependents: T::Boolean, interactive: T::Boolean, keep_tmp: T::Boolean,
|
||||||
dependents: false,
|
debug_symbols: T::Boolean, force: T::Boolean, overwrite: T::Boolean,
|
||||||
interactive: false,
|
debug: T::Boolean, quiet: T::Boolean, verbose: T::Boolean
|
||||||
keep_tmp: false,
|
).returns(T::Array[FormulaInstaller])
|
||||||
debug_symbols: false,
|
}
|
||||||
force: false,
|
def formula_installers(
|
||||||
overwrite: false,
|
formulae_to_install,
|
||||||
debug: false,
|
flags:,
|
||||||
quiet: false,
|
dry_run: false,
|
||||||
verbose: false
|
force_bottle: false,
|
||||||
)
|
build_from_source_formulae: [],
|
||||||
return if formulae_to_install.empty?
|
dependents: false,
|
||||||
|
interactive: false,
|
||||||
|
keep_tmp: false,
|
||||||
|
debug_symbols: false,
|
||||||
|
force: false,
|
||||||
|
overwrite: false,
|
||||||
|
debug: false,
|
||||||
|
quiet: false,
|
||||||
|
verbose: false
|
||||||
|
)
|
||||||
|
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.
|
||||||
formulae_to_install.sort! do |a, b|
|
formulae_to_install.sort! do |a, b|
|
||||||
if !a.keg_only? && b.keg_only?
|
if !a.keg_only? && b.keg_only?
|
||||||
1
|
1
|
||||||
elsif a.keg_only? && !b.keg_only?
|
elsif a.keg_only? && !b.keg_only?
|
||||||
-1
|
-1
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
dependency_graph = Utils::TopologicalHash.graph_package_dependencies(formulae_to_install)
|
||||||
|
begin
|
||||||
|
formulae_to_install = dependency_graph.tsort & formulae_to_install
|
||||||
|
rescue TSort::Cyclic
|
||||||
|
if Homebrew::EnvConfig.developer?
|
||||||
|
raise CyclicDependencyError, dependency_graph.strongly_connected_components
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
formulae_to_install.filter_map do |formula|
|
||||||
|
Migrator.migrate_if_needed(formula, force:, dry_run:)
|
||||||
|
begin
|
||||||
|
fi = create_formula_installer(
|
||||||
|
formula,
|
||||||
|
flags:,
|
||||||
|
force_bottle:,
|
||||||
|
build_from_source_formulae:,
|
||||||
|
interactive:,
|
||||||
|
keep_tmp:,
|
||||||
|
debug_symbols:,
|
||||||
|
force:,
|
||||||
|
overwrite:,
|
||||||
|
debug:,
|
||||||
|
quiet:,
|
||||||
|
verbose:,
|
||||||
|
)
|
||||||
|
fi.fetch_bottle_tab(quiet: !debug)
|
||||||
|
|
||||||
|
all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash|
|
||||||
|
minimum_version = if (version = hash["version"])
|
||||||
|
Version.new(version)
|
||||||
|
end
|
||||||
|
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"].to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
if !dry_run && dependents && all_runtime_deps_installed
|
||||||
|
# Don't need to install this bottle if all of the runtime
|
||||||
|
# dependencies have the same or newer version already installed.
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
fi
|
||||||
|
rescue CannotInstallFormulaError => e
|
||||||
|
ofail e
|
||||||
|
nil
|
||||||
|
rescue UnsatisfiedRequirements, DownloadError => e
|
||||||
|
ofail "#{formula}: #{e}"
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dependency_graph = Utils::TopologicalHash.graph_package_dependencies(formulae_to_install)
|
sig { params(formula_installers: T::Array[FormulaInstaller], dry_run: T::Boolean, verbose: T::Boolean).void }
|
||||||
begin
|
def upgrade_formulae(formula_installers, dry_run: false, verbose: false)
|
||||||
formulae_to_install = dependency_graph.tsort & formulae_to_install
|
valid_formula_installers = if dry_run
|
||||||
rescue TSort::Cyclic
|
formula_installers
|
||||||
raise CyclicDependencyError, dependency_graph.strongly_connected_components if Homebrew::EnvConfig.developer?
|
else
|
||||||
|
Install.fetch_formulae(formula_installers)
|
||||||
|
end
|
||||||
|
|
||||||
|
valid_formula_installers.each do |fi|
|
||||||
|
upgrade_formula(fi, dry_run:, verbose:)
|
||||||
|
Cleanup.install_formula_clean!(fi.formula, dry_run:)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
formulae_to_install.filter_map do |formula|
|
sig { params(formula: Formula).returns(T::Array[Keg]) }
|
||||||
Migrator.migrate_if_needed(formula, force:, dry_run:)
|
def outdated_kegs(formula)
|
||||||
begin
|
[formula, *formula.old_installed_formulae].map(&:linked_keg)
|
||||||
fi = create_formula_installer(
|
.select(&:directory?)
|
||||||
|
.map { |k| Keg.new(k.resolved_path) }
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { params(formula: Formula, fi_options: Options).void }
|
||||||
|
def print_upgrade_message(formula, fi_options)
|
||||||
|
version_upgrade = if formula.optlinked?
|
||||||
|
"#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}"
|
||||||
|
else
|
||||||
|
"-> #{formula.pkg_version}"
|
||||||
|
end
|
||||||
|
oh1 "Upgrading #{Formatter.identifier(formula.full_specified_name)}"
|
||||||
|
puts " #{version_upgrade} #{fi_options.to_a.join(" ")}"
|
||||||
|
end
|
||||||
|
|
||||||
|
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,
|
formula,
|
||||||
flags:,
|
flags:,
|
||||||
force_bottle:,
|
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,
|
||||||
|
flags:,
|
||||||
|
force_bottle: false,
|
||||||
|
build_from_source_formulae: [],
|
||||||
|
interactive: false,
|
||||||
|
keep_tmp: false,
|
||||||
|
debug_symbols: false,
|
||||||
|
force: false,
|
||||||
|
overwrite: false,
|
||||||
|
debug: false,
|
||||||
|
quiet: false,
|
||||||
|
verbose: false
|
||||||
|
)
|
||||||
|
keg = if formula.optlinked?
|
||||||
|
Keg.new(formula.opt_prefix.resolved_path)
|
||||||
|
else
|
||||||
|
formula.installed_kegs.find(&:optlinked?)
|
||||||
|
end
|
||||||
|
|
||||||
|
if keg
|
||||||
|
tab = keg.tab
|
||||||
|
link_keg = keg.linked?
|
||||||
|
installed_as_dependency = tab.installed_as_dependency == true
|
||||||
|
installed_on_request = tab.installed_on_request == true
|
||||||
|
build_bottle = tab.built_bottle?
|
||||||
|
else
|
||||||
|
link_keg = nil
|
||||||
|
installed_as_dependency = false
|
||||||
|
installed_on_request = true
|
||||||
|
build_bottle = false
|
||||||
|
end
|
||||||
|
|
||||||
|
build_options = BuildOptions.new(Options.create(flags), formula.options)
|
||||||
|
options = build_options.used_options
|
||||||
|
options |= formula.build.used_options
|
||||||
|
options &= formula.options
|
||||||
|
|
||||||
|
FormulaInstaller.new(
|
||||||
|
formula,
|
||||||
|
**{
|
||||||
|
options:,
|
||||||
|
link_keg:,
|
||||||
|
installed_as_dependency:,
|
||||||
|
installed_on_request:,
|
||||||
|
build_bottle:,
|
||||||
|
force_bottle:,
|
||||||
build_from_source_formulae:,
|
build_from_source_formulae:,
|
||||||
interactive:,
|
interactive:,
|
||||||
keep_tmp:,
|
keep_tmp:,
|
||||||
@ -66,442 +485,19 @@ module Homebrew
|
|||||||
debug:,
|
debug:,
|
||||||
quiet:,
|
quiet:,
|
||||||
verbose:,
|
verbose:,
|
||||||
)
|
}.compact,
|
||||||
fi.fetch_bottle_tab(quiet: !debug)
|
|
||||||
|
|
||||||
all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash|
|
|
||||||
minimum_version = Version.new(hash["version"]) if hash["version"].present?
|
|
||||||
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"])
|
|
||||||
end
|
|
||||||
|
|
||||||
if !dry_run && dependents && all_runtime_deps_installed
|
|
||||||
# Don't need to install this bottle if all of the runtime
|
|
||||||
# dependencies have the same or newer version already installed.
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
fi
|
|
||||||
rescue CannotInstallFormulaError => e
|
|
||||||
ofail e
|
|
||||||
nil
|
|
||||||
rescue UnsatisfiedRequirements, DownloadError => e
|
|
||||||
ofail "#{formula}: #{e}"
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.upgrade_formulae(formula_installers, dry_run: false, verbose: false)
|
|
||||||
valid_formula_installers = formula_installers.dup
|
|
||||||
|
|
||||||
unless dry_run
|
|
||||||
valid_formula_installers.select! do |fi|
|
|
||||||
fi.prelude
|
|
||||||
fi.fetch
|
|
||||||
true
|
|
||||||
rescue CannotInstallFormulaError => e
|
|
||||||
ofail e
|
|
||||||
false
|
|
||||||
rescue UnsatisfiedRequirements, DownloadError => e
|
|
||||||
ofail "#{fi.formula.full_name}: #{e}"
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
valid_formula_installers.each do |fi|
|
|
||||||
upgrade_formula(fi, dry_run:, verbose:)
|
|
||||||
Cleanup.install_formula_clean!(fi.formula, dry_run:)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private_class_method def self.outdated_kegs(formula)
|
|
||||||
[formula, *formula.old_installed_formulae].map(&:linked_keg)
|
|
||||||
.select(&:directory?)
|
|
||||||
.map { |k| Keg.new(k.resolved_path) }
|
|
||||||
end
|
|
||||||
|
|
||||||
private_class_method def self.print_upgrade_message(formula, fi_options)
|
|
||||||
version_upgrade = if formula.optlinked?
|
|
||||||
"#{Keg.new(formula.opt_prefix).version} -> #{formula.pkg_version}"
|
|
||||||
else
|
|
||||||
"-> #{formula.pkg_version}"
|
|
||||||
end
|
|
||||||
oh1 "Upgrading #{Formatter.identifier(formula.full_specified_name)}"
|
|
||||||
puts " #{version_upgrade} #{fi_options.to_a.join(" ")}"
|
|
||||||
end
|
|
||||||
|
|
||||||
private_class_method def self.create_formula_installer(
|
|
||||||
formula,
|
|
||||||
flags:,
|
|
||||||
force_bottle: false,
|
|
||||||
build_from_source_formulae: [],
|
|
||||||
interactive: false,
|
|
||||||
keep_tmp: false,
|
|
||||||
debug_symbols: false,
|
|
||||||
force: false,
|
|
||||||
overwrite: false,
|
|
||||||
debug: false,
|
|
||||||
quiet: false,
|
|
||||||
verbose: false
|
|
||||||
)
|
|
||||||
keg = if formula.optlinked?
|
|
||||||
Keg.new(formula.opt_prefix.resolved_path)
|
|
||||||
else
|
|
||||||
formula.installed_kegs.find(&:optlinked?)
|
|
||||||
end
|
|
||||||
|
|
||||||
if keg
|
|
||||||
tab = keg.tab
|
|
||||||
link_keg = keg.linked?
|
|
||||||
installed_as_dependency = tab.installed_as_dependency == true
|
|
||||||
installed_on_request = tab.installed_on_request == true
|
|
||||||
build_bottle = tab.built_bottle?
|
|
||||||
else
|
|
||||||
link_keg = nil
|
|
||||||
installed_as_dependency = false
|
|
||||||
installed_on_request = true
|
|
||||||
build_bottle = false
|
|
||||||
end
|
|
||||||
|
|
||||||
build_options = BuildOptions.new(Options.create(flags), formula.options)
|
|
||||||
options = build_options.used_options
|
|
||||||
options |= formula.build.used_options
|
|
||||||
options &= formula.options
|
|
||||||
|
|
||||||
FormulaInstaller.new(
|
|
||||||
formula,
|
|
||||||
**{
|
|
||||||
options:,
|
|
||||||
link_keg:,
|
|
||||||
installed_as_dependency:,
|
|
||||||
installed_on_request:,
|
|
||||||
build_bottle:,
|
|
||||||
force_bottle:,
|
|
||||||
build_from_source_formulae:,
|
|
||||||
interactive:,
|
|
||||||
keep_tmp:,
|
|
||||||
debug_symbols:,
|
|
||||||
force:,
|
|
||||||
overwrite:,
|
|
||||||
debug:,
|
|
||||||
quiet:,
|
|
||||||
verbose:,
|
|
||||||
}.compact,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.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_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
|
end
|
||||||
|
|
||||||
# Update installed formulae after upgrading
|
sig { params(one: Formula, two: Formula).returns(Integer) }
|
||||||
installed_formulae = FormulaInstaller.installed.to_a
|
def depends_on(one, two)
|
||||||
|
if one.any_installed_keg
|
||||||
# Assess the dependents tree again now we've upgraded.
|
&.runtime_dependencies
|
||||||
unless dry_run
|
&.any? { |dependency| dependency["full_name"] == two.full_name }
|
||||||
oh1 "Checking for dependents of upgraded formulae..."
|
1
|
||||||
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
|
else
|
||||||
ohai "No broken dependents found!"
|
T.must(one <=> two)
|
||||||
end
|
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
|
|
||||||
&.runtime_dependencies
|
|
||||||
&.any? { |dependency| dependency["full_name"] == two.full_name }
|
|
||||||
1
|
|
||||||
else
|
|
||||||
one <=> two
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user