Skip to content

Commit

Permalink
Merge pull request #16623 from reitermarkus/formulary-loader-for
Browse files Browse the repository at this point in the history
Refactor `Formulary::loader_for`.
  • Loading branch information
reitermarkus authored Feb 15, 2024
2 parents 9acd5c9 + efa6130 commit ebd30fb
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 229 deletions.
4 changes: 2 additions & 2 deletions Library/Homebrew/api/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def download_and_cache_data!
end
private :download_and_cache_data!

sig { returns(Hash) }
sig { returns(T::Hash[String, Hash]) }
def all_formulae
unless cache.key?("formulae")
json_updated = download_and_cache_data!
Expand All @@ -69,7 +69,7 @@ def all_formulae
cache["formulae"]
end

sig { returns(Hash) }
sig { returns(T::Hash[String, String]) }
def all_aliases
unless cache.key?("aliases")
json_updated = download_and_cache_data!
Expand Down
4 changes: 2 additions & 2 deletions Library/Homebrew/cleanup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def stale_formula?(pathname, scrub)

formula = begin
Formulary.from_rack(HOMEBREW_CELLAR/formula_name)
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue FormulaUnavailableError, TapFormulaAmbiguityError
nil
end

Expand Down Expand Up @@ -300,7 +300,7 @@ def clean!(quiet: false, periodic: false)
args.each do |arg|
formula = begin
Formulary.resolve(arg)
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue FormulaUnavailableError, TapFormulaAmbiguityError
nil
end

Expand Down
33 changes: 23 additions & 10 deletions Library/Homebrew/diagnostic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -733,8 +733,7 @@ def check_for_unreadable_installed_formula
rescue FormulaUnreadableError, FormulaClassUnavailableError,
TapFormulaUnreadableError, TapFormulaClassUnavailableError => e
formula_unavailable_exceptions << e
rescue FormulaUnavailableError,
TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue FormulaUnavailableError, TapFormulaAmbiguityError
nil
end
return if formula_unavailable_exceptions.empty?
Expand All @@ -752,7 +751,7 @@ def check_for_unlinked_but_not_keg_only
else
begin
Formulary.from_rack(rack).keg_only?
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue FormulaUnavailableError, TapFormulaAmbiguityError
false
end
end
Expand Down Expand Up @@ -835,16 +834,30 @@ def check_deleted_formula
kegs = Keg.all

deleted_formulae = kegs.map do |keg|
next if Formulary.tap_paths(keg.name).any?
tap = Tab.for_keg(keg).tap

loadable = [
Formulary::FromAPILoader,
Formulary::FromDefaultNameLoader,
Formulary::FromNameLoader,
].any? do |loader_class|
loader = begin
loader_class.try_new(keg.name, warn: false)
rescue TapFormulaAmbiguityError => e
e.loaders.first
end

if loader
# If we know the tap, ignore all other taps.
next false if tap && loader.tap != tap

next true
end

unless EnvConfig.no_install_from_api?
# Formulae installed from the API should not count as deleted formulae
# but may not have a tap listed in their tab
tap = Tab.for_keg(keg).tap
next if (tap.blank? || tap.core_tap?) && Homebrew::API::Formula.all_formulae.key?(keg.name)
false
end

keg.name
keg.name unless loadable
end.compact.uniq

return if deleted_formulae.blank?
Expand Down
36 changes: 8 additions & 28 deletions Library/Homebrew/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -259,40 +259,20 @@ def initialize(tap, name, error)

# Raised when a formula with the same name is found in multiple taps.
class TapFormulaAmbiguityError < RuntimeError
attr_reader :name, :paths, :formulae
attr_reader :name, :taps, :loaders

def initialize(name, paths)
def initialize(name, loaders)
@name = name
@paths = paths
@formulae = paths.map do |path|
"#{Tap.from_path(path).name}/#{path.basename(".rb")}"
end

super <<~EOS
Formulae found in multiple taps: #{formulae.map { |f| "\n * #{f}" }.join}
@loaders = loaders
@taps = loaders.map(&:tap)

Please use the fully-qualified name (e.g. #{formulae.first}) to refer to the formula.
EOS
end
end

# Raised when a formula's old name in a specific tap is found in multiple taps.
class TapFormulaWithOldnameAmbiguityError < RuntimeError
attr_reader :name, :possible_tap_newname_formulae, :taps

def initialize(name, possible_tap_newname_formulae)
@name = name
@possible_tap_newname_formulae = possible_tap_newname_formulae

@taps = possible_tap_newname_formulae.map do |newname|
newname =~ HOMEBREW_TAP_FORMULA_REGEX
"#{Regexp.last_match(1)}/#{Regexp.last_match(2)}"
end
formulae = taps.map { |tap| "#{tap}/#{name}" }
formula_list = formulae.map { |f| "\n * #{f}" }.join

super <<~EOS
Formulae with '#{name}' old name found in multiple taps: #{taps.map { |t| "\n * #{t}" }.join}
Formulae found in multiple taps:#{formula_list}
Please use the fully-qualified name (e.g. #{taps.first}/#{name}) to refer to the formula or use its new name.
Please use the fully-qualified name (e.g. #{formulae.first}) to refer to a specific formula.
EOS
end
end
Expand Down
21 changes: 12 additions & 9 deletions Library/Homebrew/formula.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class Formula

# The path to the alias that was used to identify this {Formula}.
# e.g. `/usr/local/Library/Taps/homebrew/homebrew-core/Aliases/another-name-for-this-formula`
sig { returns(T.any(NilClass, Pathname, String)) }
sig { returns(T.nilable(Pathname)) }
attr_reader :alias_path

# The name of the alias that was used to identify this {Formula}.
Expand Down Expand Up @@ -199,7 +199,7 @@ class Formula

# @private
sig {
params(name: String, path: Pathname, spec: Symbol, alias_path: T.any(NilClass, Pathname, String),
params(name: String, path: Pathname, spec: Symbol, alias_path: T.nilable(Pathname),
tap: T.nilable(Tap), force_bottle: T::Boolean).void
}
def initialize(name, path, spec, alias_path: nil, tap: nil, force_bottle: false)
Expand Down Expand Up @@ -326,18 +326,22 @@ def validate_attributes!
# The alias path that was used to install this formula, if it exists.
# Can differ from {#alias_path}, which is the alias used to find the formula,
# and is specified to this instance.
sig { returns(T.nilable(Pathname)) }
def installed_alias_path
build_tab = build
path = build_tab.source["path"] if build_tab.is_a?(Tab)

return unless path&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}o)
return unless File.symlink?(path)

path = Pathname(path)
return unless path.symlink?

path
end

sig { returns(T.nilable(String)) }
def installed_alias_name
File.basename(installed_alias_path) if installed_alias_path
installed_alias_path&.basename&.to_s
end

def full_installed_alias_name
Expand All @@ -346,14 +350,13 @@ def full_installed_alias_name

# The path that was specified to find this formula.
def specified_path
alias_pathname = Pathname(T.must(alias_path)) if alias_path.present?
return alias_pathname if alias_pathname&.exist?
return alias_path if alias_path&.exist?

return @unresolved_path if @unresolved_path.exist?

return local_bottle_path if local_bottle_path.presence&.exist?

alias_pathname || @unresolved_path
alias_path || @unresolved_path
end

# The name specified to find this formula.
Expand Down Expand Up @@ -1315,7 +1318,7 @@ def link_overwrite?(path)
f = Formulary.factory(keg.name)
rescue FormulaUnavailableError
# formula for this keg is deleted, so defer to allowlist
rescue TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue TapFormulaAmbiguityError
return false # this keg belongs to another formula
else
# this keg belongs to another unrelated formula
Expand Down Expand Up @@ -2362,7 +2365,7 @@ def to_hash_with_variations(hash_method: :to_hash)

# Take from API, merging in local install status.
if loaded_from_api? && !Homebrew::EnvConfig.no_install_from_api?
json_formula = Homebrew::API::Formula.all_formulae[name].dup
json_formula = Homebrew::API::Formula.all_formulae.fetch(name).dup
return json_formula.merge(
hash.slice("name", "installed", "linked_keg", "pinned", "outdated"),
)
Expand Down
8 changes: 2 additions & 6 deletions Library/Homebrew/formula_auditor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ def audit_file

unversioned_formula = begin
Formulary.factory(full_name).path
rescue FormulaUnavailableError, TapFormulaAmbiguityError,
TapFormulaWithOldnameAmbiguityError
rescue FormulaUnavailableError, TapFormulaAmbiguityError
Pathname.new formula.path.to_s.gsub(/@.*\.rb$/, ".rb")
end
unless unversioned_formula.exist?
Expand Down Expand Up @@ -285,9 +284,6 @@ def audit_deps
rescue TapFormulaAmbiguityError
problem "Ambiguous dependency '#{dep.name.inspect}'."
next
rescue TapFormulaWithOldnameAmbiguityError
problem "Ambiguous oldname dependency '#{dep.name.inspect}'."
next
end

if dep_f.oldnames.include?(dep.name.split("/").last)
Expand Down Expand Up @@ -461,7 +457,7 @@ def audit_conflicts
next
rescue FormulaUnavailableError
problem "Can't find conflicting formula #{conflict.name.inspect}."
rescue TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
rescue TapFormulaAmbiguityError
problem "Ambiguous conflicting formula #{conflict.name.inspect}."
end
end
Expand Down
Loading

0 comments on commit ebd30fb

Please sign in to comment.