Make formula upgrades more liberal based on bottle

When we're installing a formula from a bottle, we currently always
upgrade all dependencies in the dependency tree to be safe.

However, if we're installing a bottle and the `runtime_dependencies`
within that bottle's tab all have older or equal versions to those
already installed: we do not need to upgrade these dependencies.

This should help a lot of upgrading a lot of the time, at least for
users using bottles (which is the huge majority).

The only downside or other noticeable change is that this requires us
to download or attempt to download the bottle tab before we compute
the dependencies at installation time.

Co-authored-by: Kevin <apainintheneck@gmail.com>
This commit is contained in:
Mike McQuaid 2023-09-02 21:46:07 -04:00
parent fc72dfc6ca
commit 9fcdaa2b85
No known key found for this signature in database
GPG Key ID: 3338A31AFDB1D829
2 changed files with 40 additions and 10 deletions

View File

@ -46,14 +46,23 @@ class Dependency
formula
end
def installed?
to_formula.latest_version_installed?
rescue FormulaUnavailableError
false
def installed?(minimum_version: nil)
formula = begin
to_formula
rescue FormulaUnavailableError
nil
end
return false unless formula
if minimum_version.present?
formula.any_version_installed? && (formula.any_installed_version.version >= minimum_version)
else
formula.latest_version_installed?
end
end
def satisfied?(inherited_options = [])
installed? && missing_options(inherited_options).empty?
def satisfied?(inherited_options = [], minimum_version: nil)
installed?(minimum_version: minimum_version) && missing_options(inherited_options).empty?
end
def missing_options(inherited_options)
@ -239,8 +248,8 @@ class UsesFromMacOSDependency < Dependency
[name, tags, bounds].hash
end
def installed?
use_macos_install? || super
def installed?(minimum_version: nil)
use_macos_install? || super(minimum_version: minimum_version)
end
sig { returns(T::Boolean) }

View File

@ -96,6 +96,7 @@ class FormulaInstaller
@requirement_messages = []
@poured_bottle = false
@start_time = nil
@bottle_tab_runtime_dependencies = {}
# Take the original formula instance, which might have been swapped from an API instance to a source instance
@formula = previously_fetched_formula if previously_fetched_formula
@ -218,6 +219,7 @@ class FormulaInstaller
end
Tab.clear_cache
verify_deps_exist unless ignore_deps?
forbidden_license_check
@ -518,6 +520,9 @@ on_request: installed_on_request?, options: options)
def compute_dependencies(use_cache: true)
@compute_dependencies = nil unless use_cache
@compute_dependencies ||= begin
# Needs to be done before expand_dependencies
fetch_bottle_tab if pour_bottle?
check_requirements(expand_requirements)
expand_dependencies
end
@ -610,9 +615,11 @@ on_request: installed_on_request?, options: options)
keep_build_test ||= dep.build? && !install_bottle_for?(dependent, build) &&
(formula.head? || !dependent.latest_version_installed?)
bottle_runtime_version = @bottle_tab_runtime_dependencies.dig(dep.name, "version")
if dep.prune_from_option?(build) || ((dep.build? || dep.test?) && !keep_build_test)
Dependency.prune
elsif dep.satisfied?(inherited_options[dep.name])
elsif dep.satisfied?(inherited_options[dep.name], minimum_version: bottle_runtime_version)
Dependency.skip
end
end
@ -1197,6 +1204,20 @@ on_request: installed_on_request?, options: options)
end
end
sig { void }
def fetch_bottle_tab
@fetch_bottle_tab ||= begin
formula.fetch_bottle_tab
@bottle_tab_runtime_dependencies = formula.bottle_tab_attributes
.fetch("runtime_dependencies", [])
.index_by { |dep| dep["full_name"] }
.freeze
true
rescue DownloadError
@fetch_bottle_tab = true
end
end
sig { void }
def fetch
return if previously_fetched_formula
@ -1208,7 +1229,7 @@ on_request: installed_on_request?, options: options)
oh1 "Fetching #{Formatter.identifier(formula.full_name)}".strip
if pour_bottle?(output_warning: true)
formula.fetch_bottle_tab
fetch_bottle_tab
else
@formula = Homebrew::API::Formula.source_download(formula) if formula.loaded_from_api?