| 
									
										
										
										
											2020-10-10 14:16:11 +02:00
										 |  |  | # typed: false | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | # 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 | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |     def upgrade_formula(f, args:) | 
					
						
							|  |  |  |       return if args.dry_run? | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-18 05:37:12 +01:00
										 |  |  |       fi = FormulaInstaller.new( | 
					
						
							|  |  |  |         f, | 
					
						
							|  |  |  |         **{ | 
					
						
							|  |  |  |           options:                    options, | 
					
						
							|  |  |  |           link_keg:                   keg_had_linked_opt ? keg_was_linked : nil, | 
					
						
							|  |  |  |           installed_as_dependency:    tab&.installed_as_dependency, | 
					
						
							|  |  |  |           installed_on_request:       args.named.present? || tab&.installed_on_request, | 
					
						
							|  |  |  |           build_bottle:               args.build_bottle? || tab&.built_bottle?, | 
					
						
							|  |  |  |           force_bottle:               args.force_bottle?, | 
					
						
							|  |  |  |           build_from_source_formulae: args.build_from_source_formulae, | 
					
						
							|  |  |  |           keep_tmp:                   args.keep_tmp?, | 
					
						
							|  |  |  |           force:                      args.force?, | 
					
						
							|  |  |  |           debug:                      args.debug?, | 
					
						
							|  |  |  |           quiet:                      args.quiet?, | 
					
						
							|  |  |  |           verbose:                    args.verbose?, | 
					
						
							|  |  |  |         }.compact, | 
					
						
							|  |  |  |       ) | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       upgrade_version = if f.optlinked? | 
					
						
							|  |  |  |         "#{Keg.new(f.opt_prefix).version} -> #{f.pkg_version}" | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       else | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         "-> #{f.pkg_version}" | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       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-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       fi.prelude | 
					
						
							|  |  |  |       fi.fetch | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       fi.install | 
					
						
							|  |  |  |       fi.finish | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |     rescue FormulaInstallationAlreadyAttemptedError | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       # We already attempted to upgrade f as part of the dependency tree of | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       # another formula. In that case, don't generate an error, just move on. | 
					
						
							|  |  |  |       nil | 
					
						
							| 
									
										
										
										
											2020-11-13 10:07:02 -05:00
										 |  |  |     rescue CannotInstallFormulaError, DownloadError => e | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       ofail e | 
					
						
							|  |  |  |     rescue BuildError => e | 
					
						
							| 
									
										
										
										
											2020-08-02 02:52:24 +02:00
										 |  |  |       e.dump(verbose: args.verbose?) | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       puts | 
					
						
							|  |  |  |       Homebrew.failed = true | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |     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| | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  |         installed_formulae.flat_map(&:runtime_installed_formula_dependents) | 
					
						
							|  |  |  |                           .uniq | 
					
						
							|  |  |  |                           .select do |f| | 
					
						
							| 
									
										
										
										
											2020-08-31 10:58:36 -07:00
										 |  |  |           keg = f.any_installed_keg | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  |           next unless keg | 
					
						
							| 
									
										
										
										
											2020-10-29 08:31:04 +00:00
										 |  |  |           next unless keg.directory? | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |           LinkageChecker.new(keg, cache_db: db) | 
					
						
							|  |  |  |                         .broken_library_linkage? | 
					
						
							|  |  |  |         end.compact | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-08-30 01:24:42 -07:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 12:26:04 -05:00
										 |  |  |     def check_installed_dependents(formulae, args:) | 
					
						
							| 
									
										
										
										
											2020-10-06 10:50:51 +01:00
										 |  |  |       return if Homebrew::EnvConfig.no_installed_dependents_check? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 12:26:04 -05:00
										 |  |  |       installed_formulae = args.dry_run? ? formulae : FormulaInstaller.installed.to_a | 
					
						
							| 
									
										
										
										
											2020-08-30 01:24:42 -07:00
										 |  |  |       return if installed_formulae.empty? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       already_broken_dependents = check_broken_dependents(installed_formulae) | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       outdated_dependents = | 
					
						
							|  |  |  |         installed_formulae.flat_map(&:runtime_installed_formula_dependents) | 
					
						
							| 
									
										
										
										
											2020-09-18 00:58:53 -07:00
										 |  |  |                           .uniq | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |                           .select(&:outdated?) | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  |       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-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 08:31:04 +00:00
										 |  |  |       # Update installed formulae after upgrading | 
					
						
							| 
									
										
										
										
											2020-10-26 19:29:55 -07:00
										 |  |  |       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| | 
					
						
							| 
									
										
										
										
											2020-08-25 18:34:18 -07:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2020-11-13 10:07:02 -05:00
										 |  |  |       rescue CannotInstallFormulaError, DownloadError => e | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         ofail e | 
					
						
							|  |  |  |       rescue BuildError => e | 
					
						
							|  |  |  |         e.dump(verbose: args.verbose?) | 
					
						
							|  |  |  |         puts | 
					
						
							|  |  |  |         Homebrew.failed = true | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def depends_on(a, b) | 
					
						
							| 
									
										
										
										
											2020-08-31 10:58:36 -07:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |     private_class_method :depends_on | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |   end | 
					
						
							|  |  |  | end |