Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Formulary::loader_for. #16623

Merged
merged 19 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
reitermarkus marked this conversation as resolved.
Show resolved Hide resolved
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
Loading