| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       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-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 | 
					
						
							|  |  |  |     rescue CannotInstallFormulaError => e | 
					
						
							|  |  |  |       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 | 
					
						
							|  |  |  |     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 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           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:) | 
					
						
							|  |  |  |       installed_formulae = FormulaInstaller.installed.to_a | 
					
						
							|  |  |  |       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) | 
					
						
							|  |  |  |                           .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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # 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 | 
					
						
							|  |  |  |       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) | 
					
						
							| 
									
										
										
										
											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 |