| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  | # typed: strict | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | require "reinstall" | 
					
						
							|  |  |  | require "formula_installer" | 
					
						
							|  |  |  | require "development_tools" | 
					
						
							|  |  |  | require "messages" | 
					
						
							|  |  |  | require "cleanup" | 
					
						
							| 
									
										
										
										
											2021-09-09 13:30:55 -07:00
										 |  |  | require "utils/topological_hash" | 
					
						
							| 
									
										
										
										
											2025-08-20 19:20:19 +01:00
										 |  |  | require "utils/output" | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | module Homebrew | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |   # Helper functions for upgrading formulae. | 
					
						
							|  |  |  |   module Upgrade | 
					
						
							| 
									
										
										
										
											2025-08-20 19:20:19 +01:00
										 |  |  |     extend Utils::Output::Mixin | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |     class Dependents < T::Struct | 
					
						
							|  |  |  |       const :upgradeable, T::Array[Formula] | 
					
						
							|  |  |  |       const :pinned, T::Array[Formula] | 
					
						
							|  |  |  |       const :skipped, T::Array[Formula] | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |     class << self | 
					
						
							|  |  |  |       sig { | 
					
						
							|  |  |  |         params( | 
					
						
							|  |  |  |           formulae_to_install: T::Array[Formula], flags: T::Array[String], dry_run: T::Boolean, | 
					
						
							|  |  |  |           force_bottle: T::Boolean, build_from_source_formulae: T::Array[String], | 
					
						
							|  |  |  |           dependents: T::Boolean, interactive: T::Boolean, keep_tmp: T::Boolean, | 
					
						
							|  |  |  |           debug_symbols: T::Boolean, force: T::Boolean, overwrite: T::Boolean, | 
					
						
							|  |  |  |           debug: T::Boolean, quiet: T::Boolean, verbose: T::Boolean | 
					
						
							|  |  |  |         ).returns(T::Array[FormulaInstaller]) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       def formula_installers( | 
					
						
							|  |  |  |         formulae_to_install, | 
					
						
							|  |  |  |         flags:, | 
					
						
							|  |  |  |         dry_run: false, | 
					
						
							|  |  |  |         force_bottle: false, | 
					
						
							|  |  |  |         build_from_source_formulae: [], | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  |         # 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 | 
					
						
							| 
									
										
										
										
											2021-09-09 13:30:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         dependency_graph = Utils::TopologicalHash.graph_package_dependencies(formulae_to_install) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         begin | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |           formulae_to_install = dependency_graph.tsort & formulae_to_install | 
					
						
							|  |  |  |         rescue TSort::Cyclic | 
					
						
							|  |  |  |           if Homebrew::EnvConfig.developer? | 
					
						
							|  |  |  |             raise CyclicDependencyError, dependency_graph.strongly_connected_components | 
					
						
							| 
									
										
										
										
											2025-06-20 16:41:32 -04:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-06-20 16:41:32 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2021-09-10 06:01:37 -07:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2024-02-22 23:29:55 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-08-30 07:47:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       sig { params(formula_installers: T::Array[FormulaInstaller], dry_run: T::Boolean, verbose: T::Boolean).void } | 
					
						
							|  |  |  |       def upgrade_formulae(formula_installers, dry_run: false, verbose: false) | 
					
						
							|  |  |  |         valid_formula_installers = if dry_run | 
					
						
							|  |  |  |           formula_installers | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           Install.fetch_formulae(formula_installers) | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-07-23 21:13:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         valid_formula_installers.each do |fi| | 
					
						
							|  |  |  |           upgrade_formula(fi, dry_run:, verbose:) | 
					
						
							|  |  |  |           Cleanup.install_formula_clean!(fi.formula, dry_run:) | 
					
						
							| 
									
										
										
										
											2025-06-08 11:31:40 -04:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-06-20 16:41:32 -04:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2025-06-08 11:31:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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) } | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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(" ")}" | 
					
						
							| 
									
										
										
										
											2021-06-15 10:11:40 -04:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2025-08-02 23:27:59 -04:00
										 |  |  |               `$HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK` is set: not checking for outdated | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |               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? | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         already_broken = check_broken_dependents(formulae_to_install) | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # TODO: this should be refactored to use FormulaInstaller new logic | 
					
						
							|  |  |  |         outdated = formulae_to_install.flat_map(&:runtime_installed_formula_dependents) | 
					
						
							|  |  |  |                                       .uniq | 
					
						
							|  |  |  |                                       .select(&:outdated?) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # 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? | 
					
						
							| 
									
										
										
										
											2021-08-30 07:47:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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) } | 
					
						
							| 
									
										
										
										
											2021-08-30 07:47:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         Dependents.new(upgradeable:, pinned:, skipped:) | 
					
						
							| 
									
										
										
										
											2021-08-31 12:27:14 -04:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-08-30 07:47:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2021-09-10 08:31:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         upgradeable.reject! { |f| FormulaInstaller.installed.include?(f) } | 
					
						
							| 
									
										
										
										
											2021-09-10 08:31:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # 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 | 
					
						
							| 
									
										
										
										
											2021-09-13 07:38:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         return if upgradeable.blank? | 
					
						
							| 
									
										
										
										
											2021-09-10 08:31:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2021-09-09 10:54:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # Update installed formulae after upgrading | 
					
						
							|  |  |  |         installed_formulae = FormulaInstaller.installed.to_a | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # 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 | 
					
						
							| 
									
										
										
										
											2020-08-30 01:24:42 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2021-11-26 13:14:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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) } | 
					
						
							| 
									
										
										
										
											2021-11-26 13:14:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # 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(", ")) | 
					
						
							| 
									
										
										
										
											2023-08-04 09:36:27 +01:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-08-30 01:24:42 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         # 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 | 
					
						
							| 
									
										
										
										
											2020-08-26 08:53:47 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         return if dry_run | 
					
						
							| 
									
										
										
										
											2021-11-27 15:51:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         reinstall_contexts = reinstallable_broken_dependents.map do |formula| | 
					
						
							|  |  |  |           Reinstall.build_install_context( | 
					
						
							|  |  |  |             formula, | 
					
						
							|  |  |  |             flags:, | 
					
						
							|  |  |  |             force_bottle:, | 
					
						
							|  |  |  |             build_from_source_formulae: build_from_source_formulae + [formula.full_name], | 
					
						
							|  |  |  |             interactive:, | 
					
						
							|  |  |  |             keep_tmp:, | 
					
						
							|  |  |  |             debug_symbols:, | 
					
						
							|  |  |  |             force:, | 
					
						
							|  |  |  |             debug:, | 
					
						
							|  |  |  |             quiet:, | 
					
						
							|  |  |  |             verbose:, | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2021-11-27 15:51:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         valid_formula_installers = Install.fetch_formulae(reinstall_contexts.map(&:formula_installer)) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         reinstall_contexts.each do |reinstall_context| | 
					
						
							|  |  |  |           next unless valid_formula_installers.include?(reinstall_context.formula_installer) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |           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 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       private | 
					
						
							| 
									
										
										
										
											2025-06-08 11:31:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2025-07-17 19:29:56 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |           return | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         Install.install_formula(formula_installer, upgrade: true) | 
					
						
							|  |  |  |       rescue BuildError => e | 
					
						
							|  |  |  |         e.dump(verbose:) | 
					
						
							|  |  |  |         puts | 
					
						
							|  |  |  |         Homebrew.failed = true | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2021-03-18 14:46:48 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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 | 
					
						
							| 
									
										
										
										
											2020-10-26 19:29:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-02 23:27:59 -04:00
										 |  |  |         puts "Disable this behaviour by setting `HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1`." | 
					
						
							|  |  |  |         puts "Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`)." | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         @puts_no_installed_dependents_check_disable_message_if_not_already = T.let(true, T.nilable(T::Boolean)) | 
					
						
							| 
									
										
										
										
											2021-11-26 13:14:10 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2021-11-25 09:10:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       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) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         else | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |           formula.installed_kegs.find(&:optlinked?) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         build_options = BuildOptions.new(Options.create(flags), formula.options) | 
					
						
							|  |  |  |         options = build_options.used_options | 
					
						
							|  |  |  |         options |= formula.build.used_options | 
					
						
							|  |  |  |         options &= formula.options | 
					
						
							| 
									
										
										
										
											2021-03-18 14:46:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |         FormulaInstaller.new( | 
					
						
							| 
									
										
										
										
											2021-03-18 14:46:48 +00:00
										 |  |  |           formula, | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |           **{ | 
					
						
							|  |  |  |             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, | 
					
						
							| 
									
										
										
										
											2025-06-09 00:14:16 -04:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 09:42:11 +01:00
										 |  |  |       sig { params(one: Formula, two: Formula).returns(Integer) } | 
					
						
							|  |  |  |       def depends_on(one, two) | 
					
						
							|  |  |  |         if one.any_installed_keg | 
					
						
							|  |  |  |               &.runtime_dependencies | 
					
						
							|  |  |  |               &.any? { |dependency| dependency["full_name"] == two.full_name } | 
					
						
							|  |  |  |           1
 | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           T.must(one <=> two) | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-08-24 00:37:22 +02:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2020-07-02 12:53:52 +01:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |