brew/Library/Homebrew/upgrade.rb

267 lines
9.0 KiB
Ruby
Raw Normal View History

2020-10-10 14:16:11 +02:00
# typed: false
# frozen_string_literal: true
require "reinstall"
require "formula_installer"
require "development_tools"
require "messages"
require "cleanup"
module Homebrew
2020-08-24 00:37:22 +02:00
# Helper functions for upgrading formulae.
#
# @api private
module Upgrade
module_function
def upgrade_formulae(formulae_to_install, args:)
return if formulae_to_install.empty?
return if args.dry_run?
# Sort keg-only before non-keg-only formulae to avoid any needless conflicts
# with outdated, non-keg-only versions of formulae being upgraded.
formulae_to_install.sort! do |a, b|
if !a.keg_only? && b.keg_only?
1
elsif a.keg_only? && !b.keg_only?
-1
else
0
end
end
2020-08-24 00:37:22 +02:00
formulae_to_install.each do |f|
Migrator.migrate_if_needed(f, force: args.force?)
begin
upgrade_formula(f, args: args)
Cleanup.install_formula_clean!(f)
rescue UnsatisfiedRequirements => e
Homebrew.failed = true
onoe "#{f}: #{e}"
end
end
end
2020-08-24 00:37:22 +02:00
def upgrade_formula(f, args:)
return if args.dry_run?
2020-08-24 00:37:22 +02:00
if f.opt_prefix.directory?
keg = Keg.new(f.opt_prefix.resolved_path)
keg_had_linked_opt = true
keg_was_linked = keg.linked?
end
2020-08-24 00:37:22 +02:00
formulae_maybe_with_kegs = [f] + f.old_installed_formulae
outdated_kegs = formulae_maybe_with_kegs.map(&:linked_keg)
.select(&:directory?)
.map { |k| Keg.new(k.resolved_path) }
linked_kegs = outdated_kegs.select(&:linked?)
2020-08-24 00:37:22 +02:00
if f.opt_prefix.directory?
keg = Keg.new(f.opt_prefix.resolved_path)
tab = Tab.for_keg(keg)
end
2020-08-24 00:37:22 +02:00
build_options = BuildOptions.new(Options.create(args.flags_only), f.options)
options = build_options.used_options
options |= f.build.used_options
options &= f.options
fi = FormulaInstaller.new(f, force_bottle: args.force_bottle?,
build_from_source_formulae: args.build_from_source_formulae,
debug: args.debug?, quiet: args.quiet?, verbose: args.verbose?)
fi.options = options
fi.force = args.force?
fi.keep_tmp = args.keep_tmp?
fi.build_bottle = args.build_bottle?
fi.installed_on_request = args.named.present?
fi.link_keg ||= keg_was_linked if keg_had_linked_opt
if tab
fi.build_bottle ||= tab.built_bottle?
fi.installed_as_dependency = tab.installed_as_dependency
fi.installed_on_request ||= tab.installed_on_request
end
2020-08-24 00:37:22 +02:00
upgrade_version = if f.optlinked?
"#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}"
else
2020-08-24 00:37:22 +02:00
"-> #{f.pkg_version}"
end
2020-08-24 00:37:22 +02:00
oh1 "Upgrading #{Formatter.identifier(f.full_specified_name)} #{upgrade_version} #{fi.options.to_a.join(" ")}"
2020-08-24 00:37:22 +02:00
fi.prelude
fi.fetch
2020-08-24 00:37:22 +02:00
# 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!
outdated_kegs.each(&:unlink)
2020-08-24 00:37:22 +02:00
fi.install
fi.finish
rescue FormulaInstallationAlreadyAttemptedError
2020-08-24 00:37:22 +02:00
# 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
rescue CannotInstallFormulaError => e
ofail e
rescue BuildError => e
e.dump(verbose: args.verbose?)
puts
Homebrew.failed = true
rescue DownloadError => e
ofail e
2020-08-24 00:37:22 +02:00
ensure
# restore previous installation state if build failed
begin
linked_kegs.each(&:link) unless f.latest_version_installed?
rescue
nil
end
end
2020-08-24 00:37:22 +02:00
private_class_method :upgrade_formula
2020-08-30 01:24:42 -07:00
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
2020-08-30 01:24:42 -07:00
end
def check_installed_dependents(args:)
return if Homebrew::EnvConfig.no_installed_dependents_check?
2020-08-30 01:24:42 -07:00
installed_formulae = FormulaInstaller.installed.to_a
return if installed_formulae.empty?
already_broken_dependents = check_broken_dependents(installed_formulae)
2020-08-24 00:37:22 +02:00
outdated_dependents =
installed_formulae.flat_map(&:runtime_installed_formula_dependents)
.uniq
2020-08-24 00:37:22 +02:00
.select(&:outdated?)
return if outdated_dependents.blank? && already_broken_dependents.blank?
2020-08-24 00:37:22 +02:00
outdated_dependents -= installed_formulae if args.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) }
if pinned_dependents.present?
plural = "dependent".pluralize(pinned_dependents.count)
ohai "Not upgrading #{pinned_dependents.count} pinned #{plural}:"
puts(pinned_dependents.map do |f|
"#{f.full_specified_name} #{f.pkg_version}"
end.join(", "))
end
2020-08-24 00:37:22 +02:00
# Print the upgradable dependents.
if upgradeable_dependents.blank?
ohai "No outdated dependents to upgrade!" unless args.dry_run?
else
plural = "dependent".pluralize(upgradeable_dependents.count)
verb = args.dry_run? ? "Would upgrade" : "Upgrading"
ohai "#{verb} #{upgradeable_dependents.count} #{plural}:"
formulae_upgrades = upgradeable_dependents.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
upgrade_formulae(upgradeable_dependents, args: args)
# Update installed formulae after upgrading
installed_formulae = FormulaInstaller.installed.to_a
2020-08-24 00:37:22 +02:00
# Assess the dependents tree again now we've upgraded.
oh1 "Checking for dependents of upgraded formulae..." unless args.dry_run?
2020-08-30 01:24:42 -07:00
broken_dependents = check_broken_dependents(installed_formulae)
2020-08-24 00:37:22 +02:00
if broken_dependents.blank?
if args.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 = "dependent".pluralize(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
count = reinstallable_broken_dependents.count
plural = "dependent".pluralize(reinstallable_broken_dependents.count)
ohai "Reinstalling #{count} broken #{plural} from source:"
puts reinstallable_broken_dependents.map(&:full_specified_name)
.join(", ")
end
return if args.dry_run?
reinstallable_broken_dependents.each do |f|
Homebrew.reinstall_formula(f, build_from_source: true, args: args)
2020-08-24 00:37:22 +02:00
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 => e
ofail e
rescue BuildError => e
e.dump(verbose: args.verbose?)
puts
Homebrew.failed = true
rescue DownloadError => e
ofail e
end
end
def depends_on(a, b)
if a.any_installed_keg
2020-08-24 00:37:22 +02:00
&.runtime_dependencies
&.any? { |d| d["full_name"] == b.full_name }
1
else
a <=> b
end
end
2020-08-24 00:37:22 +02:00
private_class_method :depends_on
end
end