mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Reapply "Refactor Formulary::loader_for
."
This reverts commit 24683525cb5abf3cc79a9e0e268fa6efd0af558b.
This commit is contained in:
parent
deb048874a
commit
e0743a1436
@ -59,7 +59,7 @@ module Homebrew
|
||||
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!
|
||||
@ -69,7 +69,7 @@ module Homebrew
|
||||
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!
|
||||
|
@ -130,7 +130,7 @@ module Homebrew
|
||||
|
||||
formula = begin
|
||||
Formulary.from_rack(HOMEBREW_CELLAR/formula_name)
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError
|
||||
nil
|
||||
end
|
||||
|
||||
@ -300,7 +300,7 @@ module Homebrew
|
||||
args.each do |arg|
|
||||
formula = begin
|
||||
Formulary.resolve(arg)
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError
|
||||
nil
|
||||
end
|
||||
|
||||
|
@ -733,8 +733,7 @@ module Homebrew
|
||||
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?
|
||||
@ -752,7 +751,7 @@ module Homebrew
|
||||
else
|
||||
begin
|
||||
Formulary.from_rack(rack).keg_only?
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError
|
||||
false
|
||||
end
|
||||
end
|
||||
@ -835,16 +834,30 @@ module Homebrew
|
||||
kegs = Keg.all
|
||||
|
||||
deleted_formulae = kegs.map do |keg|
|
||||
next if Formulary.tap_paths(keg.name).any?
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
keg.name
|
||||
if loader
|
||||
# If we know the tap, ignore all other taps.
|
||||
next false if tap && loader.tap != tap
|
||||
|
||||
next true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
keg.name unless loadable
|
||||
end.compact.uniq
|
||||
|
||||
return if deleted_formulae.blank?
|
||||
|
@ -259,40 +259,20 @@ end
|
||||
|
||||
# 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
|
||||
@loaders = loaders
|
||||
@taps = loaders.map(&:tap)
|
||||
|
||||
formulae = taps.map { |tap| "#{tap}/#{name}" }
|
||||
formula_list = formulae.map { |f| "\n * #{f}" }.join
|
||||
|
||||
super <<~EOS
|
||||
Formulae found in multiple taps: #{formulae.map { |f| "\n * #{f}" }.join}
|
||||
Formulae found in multiple taps:#{formula_list}
|
||||
|
||||
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
|
||||
|
||||
super <<~EOS
|
||||
Formulae with '#{name}' old name found in multiple taps: #{taps.map { |t| "\n * #{t}" }.join}
|
||||
|
||||
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
|
||||
|
@ -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}.
|
||||
@ -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)
|
||||
@ -326,18 +326,22 @@ class Formula
|
||||
# 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
|
||||
@ -346,14 +350,13 @@ class Formula
|
||||
|
||||
# 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.
|
||||
@ -1315,7 +1318,7 @@ class Formula
|
||||
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
|
||||
@ -2362,7 +2365,7 @@ class Formula
|
||||
|
||||
# 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"),
|
||||
)
|
||||
|
@ -64,8 +64,7 @@ module Homebrew
|
||||
|
||||
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?
|
||||
@ -285,9 +284,6 @@ module Homebrew
|
||||
rescue TapFormulaAmbiguityError
|
||||
problem "Ambiguous dependency '#{dep.name}'."
|
||||
next
|
||||
rescue TapFormulaWithOldnameAmbiguityError
|
||||
problem "Ambiguous oldname dependency '#{dep.name.inspect}'."
|
||||
next
|
||||
end
|
||||
|
||||
if dep_f.oldnames.include?(dep.name.split("/").last)
|
||||
@ -461,7 +457,7 @@ module Homebrew
|
||||
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
|
||||
|
@ -16,6 +16,7 @@ require "extend/hash/keys"
|
||||
#
|
||||
# @api private
|
||||
module Formulary
|
||||
extend Context
|
||||
extend Cachable
|
||||
|
||||
URL_START_REGEX = %r{(https?|ftp|file)://}
|
||||
@ -480,17 +481,26 @@ module Formulary
|
||||
include Context
|
||||
|
||||
# The formula's name
|
||||
sig { returns(String) }
|
||||
attr_reader :name
|
||||
|
||||
# The formula's ruby file's path or filename
|
||||
sig { returns(Pathname) }
|
||||
attr_reader :path
|
||||
|
||||
# The name used to install the formula
|
||||
sig { returns(T.nilable(Pathname)) }
|
||||
attr_reader :alias_path
|
||||
|
||||
# The formula's tap (nil if it should be implicitly determined)
|
||||
sig { returns(T.nilable(Tap)) }
|
||||
attr_reader :tap
|
||||
|
||||
def initialize(name, path, tap: nil)
|
||||
sig { params(name: String, path: Pathname, alias_path: Pathname, tap: Tap).void }
|
||||
def initialize(name, path, alias_path: T.unsafe(nil), tap: T.unsafe(nil))
|
||||
@name = name
|
||||
@path = path
|
||||
@alias_path = alias_path
|
||||
@tap = tap
|
||||
end
|
||||
|
||||
@ -511,7 +521,6 @@ module Formulary
|
||||
private
|
||||
|
||||
def load_file(flags:, ignore_errors:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
||||
raise FormulaUnavailableError, name unless path.file?
|
||||
|
||||
Formulary.load_formula_from_path(name, path, flags: flags, ignore_errors: ignore_errors)
|
||||
@ -519,7 +528,17 @@ module Formulary
|
||||
end
|
||||
|
||||
# Loads a formula from a bottle.
|
||||
class BottleLoader < FormulaLoader
|
||||
class FromBottleLoader < FormulaLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
ref = ref.to_s
|
||||
|
||||
new(ref) if HOMEBREW_BOTTLES_EXTNAME_REGEX.match?(ref)
|
||||
end
|
||||
|
||||
def initialize(bottle_name)
|
||||
case bottle_name
|
||||
when URL_START_REGEX
|
||||
@ -562,27 +581,67 @@ module Formulary
|
||||
end
|
||||
end
|
||||
|
||||
# Loads a formula from a path to an alias.
|
||||
class AliasLoader < FormulaLoader
|
||||
def initialize(alias_path)
|
||||
path = alias_path.resolved_path
|
||||
name = path.basename(".rb").to_s
|
||||
super name, path
|
||||
@alias_path = alias_path.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from disk using a path.
|
||||
class FromPathLoader < FormulaLoader
|
||||
def initialize(path)
|
||||
path = Pathname.new(path).expand_path
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
path = case ref
|
||||
when String
|
||||
Pathname(ref)
|
||||
when Pathname
|
||||
ref
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
return unless path.expand_path.exist?
|
||||
|
||||
options = if path.symlink?
|
||||
alias_path = path
|
||||
path = alias_path.resolved_path
|
||||
{ alias_path: alias_path }
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
return if path.extname != ".rb"
|
||||
|
||||
new(path, **options)
|
||||
end
|
||||
|
||||
sig { params(path: T.any(Pathname, String), alias_path: Pathname).void }
|
||||
def initialize(path, alias_path: T.unsafe(nil))
|
||||
path = Pathname(path).expand_path
|
||||
name = path.basename(".rb").to_s
|
||||
super name, path, tap: Homebrew::API.tap_from_source_download(path)
|
||||
alias_path = alias_path&.expand_path
|
||||
alias_dir = alias_path&.dirname
|
||||
|
||||
tap = Tap.from_path(path) || Homebrew::API.tap_from_source_download(path)
|
||||
|
||||
options = {
|
||||
alias_path: (alias_path if alias_dir == tap&.alias_dir),
|
||||
tap: tap,
|
||||
}.compact
|
||||
|
||||
super(name, path, **options)
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from URLs.
|
||||
class FromUrlLoader < FormulaLoader
|
||||
# Loads formula from a URI.
|
||||
class FromURILoader < FormulaLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
ref = ref.to_s
|
||||
|
||||
new(ref, from: from) if URL_START_REGEX.match?(ref)
|
||||
end
|
||||
|
||||
attr_reader :url
|
||||
|
||||
sig { params(url: T.any(URI::Generic, String), from: T.nilable(Symbol)).void }
|
||||
@ -621,7 +680,45 @@ module Formulary
|
||||
end
|
||||
|
||||
# Loads tapped formulae.
|
||||
class TapLoader < FormulaLoader
|
||||
class FromTapLoader < FormulaLoader
|
||||
sig { returns(Tap) }
|
||||
attr_reader :tap
|
||||
|
||||
sig { returns(Pathname) }
|
||||
attr_reader :path
|
||||
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
ref = ref.to_s
|
||||
return unless (name = ref[HOMEBREW_TAP_FORMULA_REGEX, :name])
|
||||
|
||||
alias_name = name
|
||||
|
||||
name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn)
|
||||
path = Formulary.find_formula_in_tap(name, tap)
|
||||
|
||||
options = if type == :alias
|
||||
{ alias_name: alias_name.downcase }
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
new(name, path, tap: tap, **options)
|
||||
end
|
||||
|
||||
sig { params(name: String, path: Pathname, tap: Tap, alias_name: String).void }
|
||||
def initialize(name, path, tap:, alias_name: T.unsafe(nil))
|
||||
options = {
|
||||
alias_path: (tap.alias_dir/alias_name if alias_name),
|
||||
tap: tap,
|
||||
}.compact
|
||||
|
||||
super(name, path, **options)
|
||||
end
|
||||
|
||||
def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
|
||||
super
|
||||
rescue FormulaUnreadableError => e
|
||||
@ -638,17 +735,94 @@ module Formulary
|
||||
e.issues_url = tap.issues_url || tap.to_s
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
class FromDefaultNameLoader < FromTapLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
return unless ref.is_a?(String)
|
||||
return unless (name = ref[HOMEBREW_DEFAULT_TAP_FORMULA_REGEX, :name])
|
||||
return unless (tap = CoreTap.instance).installed?
|
||||
|
||||
def find_formula_from_name(name, tap)
|
||||
Formulary.find_formula_in_tap(name, tap)
|
||||
return unless (loader = super("#{tap}/#{name}", warn: warn))
|
||||
|
||||
loader if loader.path.exist?
|
||||
end
|
||||
end
|
||||
|
||||
# Loads a formula from a name, as long as it exists only in a single tap.
|
||||
class FromNameLoader < FromTapLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
return unless ref.is_a?(String)
|
||||
return if ref.include?("/")
|
||||
|
||||
name = ref
|
||||
|
||||
loaders = Tap.map { |tap| super("#{tap}/#{name}") }
|
||||
.compact
|
||||
.select { _1.path.exist? }
|
||||
|
||||
case loaders.count
|
||||
when 1
|
||||
loaders.first
|
||||
when 2..Float::INFINITY
|
||||
raise TapFormulaAmbiguityError.new(name, loaders)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Loads a formula from a formula file in a keg.
|
||||
class FromKegLoader < FormulaLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
ref = ref.to_s
|
||||
|
||||
return unless (keg_formula = HOMEBREW_PREFIX/"opt/#{ref}/.brew/#{ref}.rb").file?
|
||||
|
||||
new(ref, keg_formula)
|
||||
end
|
||||
end
|
||||
|
||||
# Loads a formula from a cached formula file.
|
||||
class FromCacheLoader < FormulaLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
ref = ref.to_s
|
||||
|
||||
return unless (cached_formula = HOMEBREW_CACHE_FORMULA/"#{ref}.rb").file?
|
||||
|
||||
new(ref, cached_formula)
|
||||
end
|
||||
end
|
||||
|
||||
# Pseudo-loader which will raise a {FormulaUnavailableError} when trying to load the corresponding formula.
|
||||
class NullLoader < FormulaLoader
|
||||
def initialize(name)
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
return if ref.is_a?(URI::Generic)
|
||||
|
||||
new(ref)
|
||||
end
|
||||
|
||||
sig { params(ref: T.any(String, Pathname)).void }
|
||||
def initialize(ref)
|
||||
name = File.basename(ref, ".rb")
|
||||
super name, Formulary.core_path(name)
|
||||
end
|
||||
|
||||
@ -668,16 +842,50 @@ module Formulary
|
||||
end
|
||||
|
||||
def klass(flags:, ignore_errors:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
||||
namespace = "FormulaNamespace#{Digest::MD5.hexdigest(contents.to_s)}"
|
||||
Formulary.load_formula(name, path, contents, namespace, flags: flags, ignore_errors: ignore_errors)
|
||||
end
|
||||
end
|
||||
|
||||
# Load formulae from the API.
|
||||
class FormulaAPILoader < FormulaLoader
|
||||
def initialize(name)
|
||||
super name, Formulary.core_path(name)
|
||||
# Load a formula from the API.
|
||||
class FromAPILoader < FormulaLoader
|
||||
sig {
|
||||
params(ref: T.any(String, Pathname, URI::Generic), from: Symbol, warn: T::Boolean)
|
||||
.returns(T.nilable(T.attached_class))
|
||||
}
|
||||
def self.try_new(ref, from: T.unsafe(nil), warn: false)
|
||||
return if Homebrew::EnvConfig.no_install_from_api?
|
||||
return unless ref.is_a?(String)
|
||||
return unless (name = ref[HOMEBREW_DEFAULT_TAP_FORMULA_REGEX, :name])
|
||||
if !Homebrew::API::Formula.all_formulae.key?(name) &&
|
||||
!Homebrew::API::Formula.all_aliases.key?(name) &&
|
||||
!Homebrew::API::Formula.all_renames.key?(name)
|
||||
return
|
||||
end
|
||||
|
||||
alias_name = name
|
||||
|
||||
ref = "#{CoreTap.instance}/#{name}"
|
||||
|
||||
name, tap, type = Formulary.tap_formula_name_type(ref, warn: warn)
|
||||
|
||||
options = if type == :alias
|
||||
{ alias_name: alias_name.downcase }
|
||||
else
|
||||
{}
|
||||
end
|
||||
|
||||
new(name, tap: tap, **options)
|
||||
end
|
||||
|
||||
sig { params(name: String, tap: Tap, alias_name: String).void }
|
||||
def initialize(name, tap: T.unsafe(nil), alias_name: T.unsafe(nil))
|
||||
options = {
|
||||
alias_path: (CoreTap.instance.alias_dir/alias_name if alias_name),
|
||||
tap: tap,
|
||||
}.compact
|
||||
|
||||
super(name, Formulary.core_path(name), **options)
|
||||
end
|
||||
|
||||
def klass(flags:, ignore_errors:)
|
||||
@ -688,20 +896,10 @@ module Formulary
|
||||
private
|
||||
|
||||
def load_from_api(flags:)
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{name} from API" if debug?
|
||||
|
||||
Formulary.load_formula_from_api(name, flags: flags)
|
||||
end
|
||||
end
|
||||
|
||||
# Load aliases from the API.
|
||||
class AliasAPILoader < FormulaAPILoader
|
||||
def initialize(alias_name)
|
||||
super Homebrew::API::Formula.all_aliases[alias_name]
|
||||
@alias_path = Formulary.core_alias_path(alias_name).to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Return a {Formula} instance for the given reference.
|
||||
# `ref` is a string containing:
|
||||
#
|
||||
@ -741,6 +939,7 @@ module Formulary
|
||||
force_bottle: force_bottle,
|
||||
flags: flags,
|
||||
ignore_errors: ignore_errors }.compact
|
||||
|
||||
formula = loader_for(ref, **loader_options)
|
||||
.get_formula(spec, **formula_options)
|
||||
|
||||
@ -793,7 +992,7 @@ module Formulary
|
||||
# Return whether given rack is keg-only.
|
||||
def self.keg_only?(rack)
|
||||
Formulary.from_rack(rack).keg_only?
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
||||
rescue FormulaUnavailableError, TapFormulaAmbiguityError
|
||||
false
|
||||
end
|
||||
|
||||
@ -906,9 +1105,13 @@ module Formulary
|
||||
user, repo, name = tapped_name.split("/", 3).map(&:downcase)
|
||||
tap = Tap.fetch(user, repo)
|
||||
type = nil
|
||||
alias_name = tap.core_tap? ? name : "#{tap}/#{name}"
|
||||
|
||||
if (possible_alias = tap.alias_table[alias_name].presence)
|
||||
# FIXME: Remove the need to do this here.
|
||||
alias_table_key = tap.core_tap? ? name : "#{tap}/#{name}"
|
||||
|
||||
if (possible_alias = tap.alias_table[alias_table_key].presence)
|
||||
# FIXME: Remove the need to split the name and instead make
|
||||
# the alias table only contain short names.
|
||||
name = possible_alias.split("/").last
|
||||
type = :alias
|
||||
elsif (new_name = tap.formula_renames[name].presence)
|
||||
@ -938,103 +1141,32 @@ module Formulary
|
||||
[name, tap, type]
|
||||
end
|
||||
|
||||
def self.tap_loader_for(tapped_name, warn:)
|
||||
name, tap, type = Formulary.tap_formula_name_type(tapped_name, warn: warn)
|
||||
def self.loader_for(ref, from: T.unsafe(nil), warn: true)
|
||||
options = { from: from, warn: warn }.compact
|
||||
|
||||
if tap.core_tap? && !Homebrew::EnvConfig.no_install_from_api?
|
||||
if type == :alias
|
||||
return AliasAPILoader.new(name)
|
||||
elsif Homebrew::API::Formula.all_formulae.key?(name)
|
||||
return FormulaAPILoader.new(name)
|
||||
[
|
||||
FromBottleLoader,
|
||||
FromURILoader,
|
||||
FromAPILoader,
|
||||
FromTapLoader,
|
||||
FromPathLoader,
|
||||
FromDefaultNameLoader,
|
||||
FromNameLoader,
|
||||
FromKegLoader,
|
||||
FromCacheLoader,
|
||||
NullLoader,
|
||||
].each do |loader_class|
|
||||
if (loader = loader_class.try_new(ref, **options))
|
||||
$stderr.puts "#{$PROGRAM_NAME} (#{loader_class}): loading #{ref}" if debug?
|
||||
return loader
|
||||
end
|
||||
end
|
||||
|
||||
path = find_formula_in_tap(name, tap)
|
||||
TapLoader.new(name, path, tap: tap)
|
||||
end
|
||||
|
||||
def self.loader_for(ref, from: nil, warn: true)
|
||||
case ref
|
||||
when HOMEBREW_BOTTLES_EXTNAME_REGEX
|
||||
return BottleLoader.new(ref)
|
||||
when URL_START_REGEX
|
||||
return FromUrlLoader.new(ref, from: from)
|
||||
when HOMEBREW_TAP_FORMULA_REGEX
|
||||
return Formulary.tap_loader_for(ref, warn: warn)
|
||||
end
|
||||
|
||||
pathname_ref = Pathname.new(ref)
|
||||
return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && pathname_ref.expand_path.exist?
|
||||
|
||||
unless Homebrew::EnvConfig.no_install_from_api?
|
||||
return FormulaAPILoader.new(ref) if Homebrew::API::Formula.all_formulae.key?(ref)
|
||||
return AliasAPILoader.new(ref) if Homebrew::API::Formula.all_aliases.key?(ref)
|
||||
end
|
||||
|
||||
formula_with_that_name = core_path(ref)
|
||||
return FormulaLoader.new(ref, formula_with_that_name) if formula_with_that_name.file?
|
||||
|
||||
possible_alias = if pathname_ref.absolute?
|
||||
pathname_ref
|
||||
else
|
||||
core_alias_path(ref)
|
||||
end
|
||||
return AliasLoader.new(possible_alias) if possible_alias.symlink?
|
||||
|
||||
case (possible_tap_formulae = tap_paths(ref)).count
|
||||
when 1
|
||||
path = possible_tap_formulae.first.resolved_path
|
||||
name = path.basename(".rb").to_s
|
||||
return FormulaLoader.new(name, path)
|
||||
when 2..Float::INFINITY
|
||||
raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae)
|
||||
end
|
||||
|
||||
if CoreTap.instance.formula_renames.key?(ref)
|
||||
return Formulary.tap_loader_for("#{CoreTap.instance}/#{ref}", warn: warn)
|
||||
end
|
||||
|
||||
possible_taps = Tap.select { |tap| tap.formula_renames.key?(ref) }
|
||||
|
||||
case possible_taps.count
|
||||
when 1
|
||||
return Formulary.tap_loader_for("#{possible_taps.first}/#{ref}", warn: warn)
|
||||
when 2..Float::INFINITY
|
||||
possible_tap_newname_formulae = possible_taps.map { |tap| "#{tap}/#{tap.formula_renames[ref]}" }
|
||||
raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae)
|
||||
end
|
||||
|
||||
if (keg_formula = HOMEBREW_PREFIX/"opt/#{ref}/.brew/#{ref}.rb").file?
|
||||
return FormulaLoader.new(ref, keg_formula)
|
||||
end
|
||||
|
||||
if (cached_formula = HOMEBREW_CACHE_FORMULA/"#{ref}.rb").file?
|
||||
return FormulaLoader.new(ref, cached_formula)
|
||||
end
|
||||
|
||||
NullLoader.new(ref)
|
||||
end
|
||||
|
||||
def self.core_path(name)
|
||||
find_formula_in_tap(name.to_s.downcase, CoreTap.instance)
|
||||
end
|
||||
|
||||
def self.core_alias_path(name)
|
||||
CoreTap.instance.alias_dir/name.to_s.downcase
|
||||
end
|
||||
|
||||
def self.tap_paths(name)
|
||||
name = name.to_s.downcase
|
||||
Tap.select(&:installed?).map do |tap|
|
||||
formula_path = find_formula_in_tap(name, tap)
|
||||
|
||||
alias_path = tap.alias_dir/name
|
||||
next alias_path if !formula_path.exist? && alias_path.exist?
|
||||
|
||||
formula_path
|
||||
end.select(&:file?)
|
||||
end
|
||||
|
||||
sig { params(name: String, tap: Tap).returns(Pathname) }
|
||||
def self.find_formula_in_tap(name, tap)
|
||||
filename = if name.end_with?(".rb")
|
||||
|
@ -1,22 +1,42 @@
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Match taps' formulae, e.g. `someuser/sometap/someformula`
|
||||
HOMEBREW_TAP_FORMULA_REGEX = T.let(%r{^([\w-]+)/([\w-]+)/([\w+-.@]+)$}, Regexp)
|
||||
# Match taps' casks, e.g. `someuser/sometap/somecask`
|
||||
HOMEBREW_TAP_CASK_REGEX = T.let(%r{^([\w-]+)/([\w-]+)/([a-z0-9\-_]+)$}, Regexp)
|
||||
# Match default cask taps' casks, e.g. `homebrew/cask/somecask` or `somecask`
|
||||
# Match a formula name.
|
||||
HOMEBREW_TAP_FORMULA_NAME_REGEX = T.let(/(?<name>[\w+\-.@]+)/, Regexp)
|
||||
# Match taps' formulae, e.g. `someuser/sometap/someformula`.
|
||||
HOMEBREW_TAP_FORMULA_REGEX = T.let(
|
||||
%r{\A(?<user>[\w-]+)/(?<repo>[\w-]+)/#{HOMEBREW_TAP_FORMULA_NAME_REGEX.source}\Z},
|
||||
Regexp,
|
||||
)
|
||||
# Match default formula taps' formulae, e.g. `homebrew/core/someformula` or `someformula`.
|
||||
HOMEBREW_DEFAULT_TAP_FORMULA_REGEX = T.let(
|
||||
%r{\A(?:[Hh]omebrew/(?:homebrew-)?core/)?(?<name>#{HOMEBREW_TAP_FORMULA_NAME_REGEX.source})\Z},
|
||||
Regexp,
|
||||
)
|
||||
|
||||
# Match a cask token.
|
||||
HOMEBREW_TAP_CASK_TOKEN_REGEX = T.let(/(?<token>[a-z0-9\-_]+(?:@[a-z0-9\-_.]+)?)/, Regexp)
|
||||
# Match taps' casks, e.g. `someuser/sometap/somecask`.
|
||||
HOMEBREW_TAP_CASK_REGEX = T.let(
|
||||
%r{\A(?<user>[\w-]+)/(?<repo>[\w-]+)/#{HOMEBREW_TAP_CASK_TOKEN_REGEX.source}\Z},
|
||||
Regexp,
|
||||
)
|
||||
# Match default cask taps' casks, e.g. `homebrew/cask/somecask` or `somecask`.
|
||||
HOMEBREW_DEFAULT_TAP_CASK_REGEX = T.let(
|
||||
%r{^(?:[Hh]omebrew/(?:homebrew-)?cask/)?(?<token>[a-z0-9\-_]+)$}, Regexp
|
||||
%r{\A(?:[Hh]omebrew/(?:homebrew-)?cask/)?#{HOMEBREW_TAP_CASK_TOKEN_REGEX.source}\Z},
|
||||
Regexp,
|
||||
)
|
||||
# Match taps' directory paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap`
|
||||
|
||||
# Match taps' directory paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap`.
|
||||
HOMEBREW_TAP_DIR_REGEX = T.let(
|
||||
%r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?<user>[\w-]+)/(?<repo>[\w-]+)}, Regexp
|
||||
%r{#{Regexp.escape(HOMEBREW_LIBRARY.to_s)}/Taps/(?<user>[\w-]+)/(?<repo>[\w-]+)},
|
||||
Regexp,
|
||||
)
|
||||
# Match taps' formula paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula`
|
||||
HOMEBREW_TAP_PATH_REGEX = T.let(Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{(?:/.*)?$}.source).freeze, Regexp)
|
||||
# Match official taps' casks, e.g. `homebrew/cask/somecask or homebrew/cask-versions/somecask`
|
||||
HOMEBREW_CASK_TAP_CASK_REGEX =
|
||||
T.let(%r{^(?:([Cc]askroom)/(cask|versions)|([Hh]omebrew)/(?:homebrew-)?(cask|cask-[\w-]+))/([\w+-.]+)$},
|
||||
Regexp)
|
||||
HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = T.let(/^(home|linux)brew-/, Regexp)
|
||||
# Match taps' formula paths, e.g. `HOMEBREW_LIBRARY/Taps/someuser/sometap/someformula`.
|
||||
HOMEBREW_TAP_PATH_REGEX = T.let(Regexp.new(HOMEBREW_TAP_DIR_REGEX.source + %r{(?:/.*)?\Z}.source).freeze, Regexp)
|
||||
# Match official taps' casks, e.g. `homebrew/cask/somecask or homebrew/cask-versions/somecask`.
|
||||
HOMEBREW_CASK_TAP_CASK_REGEX = T.let(
|
||||
%r{\A(?:([Cc]askroom)/(cask|versions)|([Hh]omebrew)/(?:homebrew-)?(cask|cask-[\w-]+))/([\w+-.]+)\Z},
|
||||
Regexp,
|
||||
)
|
||||
HOMEBREW_OFFICIAL_REPO_PREFIXES_REGEX = T.let(/\A(home|linux)brew-/, Regexp)
|
||||
|
@ -28,7 +28,7 @@ RSpec.describe Formula do
|
||||
let(:path) { Formulary.core_path(name) }
|
||||
let(:spec) { :stable }
|
||||
let(:alias_name) { "baz@1" }
|
||||
let(:alias_path) { (CoreTap.instance.alias_dir/alias_name).to_s }
|
||||
let(:alias_path) { CoreTap.instance.alias_dir/alias_name }
|
||||
let(:f) { klass.new(name, path, spec) }
|
||||
let(:f_alias) { klass.new(name, path, spec, alias_path: alias_path) }
|
||||
|
||||
@ -190,11 +190,11 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
alias_name = "bar"
|
||||
alias_path = "#{CoreTap.instance.alias_dir}/#{alias_name}"
|
||||
alias_path = CoreTap.instance.alias_dir/alias_name
|
||||
CoreTap.instance.alias_dir.mkpath
|
||||
FileUtils.ln_sf f.path, alias_path
|
||||
|
||||
f.build = Tab.new(source: { "path" => alias_path })
|
||||
f.build = Tab.new(source: { "path" => alias_path.to_s })
|
||||
|
||||
expect(f.installed_alias_path).to eq(alias_path)
|
||||
expect(f.installed_alias_name).to eq(alias_name)
|
||||
@ -225,12 +225,12 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
alias_name = "bar"
|
||||
alias_path = tap.alias_dir/alias_name
|
||||
full_alias_name = "#{tap.user}/#{tap.repo}/#{alias_name}"
|
||||
alias_path = "#{tap.alias_dir}/#{alias_name}"
|
||||
tap.alias_dir.mkpath
|
||||
FileUtils.ln_sf f.path, alias_path
|
||||
|
||||
f.build = Tab.new(source: { "path" => alias_path })
|
||||
f.build = Tab.new(source: { "path" => alias_path.to_s })
|
||||
|
||||
expect(f.installed_alias_path).to eq(alias_path)
|
||||
expect(f.installed_alias_name).to eq(alias_name)
|
||||
@ -451,7 +451,7 @@ RSpec.describe Formula do
|
||||
FileUtils.ln_sf f.path, source_path
|
||||
|
||||
expect(f.alias_path).to eq(alias_path)
|
||||
expect(f.installed_alias_path).to eq(source_path.to_s)
|
||||
expect(f.installed_alias_path).to eq(source_path)
|
||||
end
|
||||
end
|
||||
|
||||
@ -491,14 +491,14 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
specify "with alias path with a path" do
|
||||
alias_path = "#{CoreTap.instance.alias_dir}/alias"
|
||||
different_alias_path = "#{CoreTap.instance.alias_dir}/another_alias"
|
||||
alias_path = CoreTap.instance.alias_dir/"alias"
|
||||
different_alias_path = CoreTap.instance.alias_dir/"another_alias"
|
||||
|
||||
formula_with_alias = formula "foo" do
|
||||
url "foo-1.0"
|
||||
end
|
||||
formula_with_alias.build = Tab.empty
|
||||
formula_with_alias.build.source["path"] = alias_path
|
||||
formula_with_alias.build.source["path"] = alias_path.to_s
|
||||
|
||||
formula_without_alias = formula "bar" do
|
||||
url "bar-1.0"
|
||||
@ -510,7 +510,7 @@ RSpec.describe Formula do
|
||||
url "baz-1.0"
|
||||
end
|
||||
formula_with_different_alias.build = Tab.empty
|
||||
formula_with_different_alias.build.source["path"] = different_alias_path
|
||||
formula_with_different_alias.build.source["path"] = different_alias_path.to_s
|
||||
|
||||
formulae = [
|
||||
formula_with_alias,
|
||||
@ -1239,8 +1239,8 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
let(:tab) { Tab.empty }
|
||||
let(:alias_path) { "#{CoreTap.instance.alias_dir}/bar" }
|
||||
let(:alias_name) { "bar" }
|
||||
let(:alias_path) { CoreTap.instance.alias_dir/alias_name }
|
||||
|
||||
before do
|
||||
allow(described_class).to receive(:installed).and_return([f])
|
||||
@ -1261,7 +1261,7 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
specify "alias changes when not changed" do
|
||||
tab.source["path"] = alias_path
|
||||
tab.source["path"] = alias_path.to_s
|
||||
stub_formula_loader(f, alias_name)
|
||||
|
||||
CoreTap.instance.alias_dir.mkpath
|
||||
@ -1276,7 +1276,7 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
specify "alias changes when new alias target" do
|
||||
tab.source["path"] = alias_path
|
||||
tab.source["path"] = alias_path.to_s
|
||||
stub_formula_loader(new_formula, alias_name)
|
||||
|
||||
CoreTap.instance.alias_dir.mkpath
|
||||
@ -1291,7 +1291,7 @@ RSpec.describe Formula do
|
||||
end
|
||||
|
||||
specify "alias changes when old formulae installed" do
|
||||
tab.source["path"] = alias_path
|
||||
tab.source["path"] = alias_path.to_s
|
||||
stub_formula_loader(new_formula, alias_name)
|
||||
|
||||
CoreTap.instance.alias_dir.mkpath
|
||||
@ -1332,8 +1332,8 @@ RSpec.describe Formula do
|
||||
end
|
||||
end
|
||||
|
||||
let(:alias_path) { "#{f.tap.alias_dir}/bar" }
|
||||
let(:alias_name) { "bar" }
|
||||
let(:alias_path) { f.tap.alias_dir/alias_name }
|
||||
|
||||
def setup_tab_for_prefix(prefix, options = {})
|
||||
prefix.mkpath
|
||||
|
@ -111,7 +111,7 @@ RSpec.describe Formulary do
|
||||
it "raises an error" do
|
||||
expect do
|
||||
described_class.factory(formula_name)
|
||||
end.to raise_error(FormulaClassUnavailableError)
|
||||
end.to raise_error(TapFormulaClassUnavailableError)
|
||||
end
|
||||
end
|
||||
|
||||
@ -139,7 +139,7 @@ RSpec.describe Formulary do
|
||||
context "when given an alias" do
|
||||
subject(:formula) { described_class.factory("foo") }
|
||||
|
||||
let(:alias_dir) { CoreTap.instance.alias_dir.tap(&:mkpath) }
|
||||
let(:alias_dir) { CoreTap.instance.alias_dir }
|
||||
let(:alias_path) { alias_dir/"foo" }
|
||||
|
||||
before do
|
||||
@ -152,7 +152,7 @@ RSpec.describe Formulary do
|
||||
end
|
||||
|
||||
it "calling #alias_path on the returned Formula returns the alias path" do
|
||||
expect(formula.alias_path).to eq(alias_path.to_s)
|
||||
expect(formula.alias_path).to eq(alias_path)
|
||||
end
|
||||
end
|
||||
|
||||
@ -229,23 +229,26 @@ RSpec.describe Formulary do
|
||||
let(:tap) { Tap.new("homebrew", "foo") }
|
||||
let(:another_tap) { Tap.new("homebrew", "bar") }
|
||||
let(:formula_path) { tap.path/"Formula/#{formula_name}.rb" }
|
||||
let(:alias_name) { "bar" }
|
||||
let(:alias_dir) { tap.alias_dir }
|
||||
let(:alias_path) { alias_dir/alias_name }
|
||||
|
||||
before do
|
||||
alias_dir.mkpath
|
||||
FileUtils.ln_s formula_path, alias_path
|
||||
tap.clear_cache
|
||||
end
|
||||
|
||||
it "returns a Formula when given a name" do
|
||||
expect(described_class.factory(formula_name)).to be_a(Formula)
|
||||
end
|
||||
|
||||
it "returns a Formula from an Alias path" do
|
||||
alias_dir = tap.path/"Aliases"
|
||||
alias_dir.mkpath
|
||||
FileUtils.ln_s formula_path, alias_dir/"bar"
|
||||
expect(described_class.factory("bar")).to be_a(Formula)
|
||||
expect(described_class.factory(alias_name)).to be_a(Formula)
|
||||
end
|
||||
|
||||
it "returns a Formula from a fully qualified Alias path" do
|
||||
alias_dir = tap.path/"Aliases"
|
||||
alias_dir.mkpath
|
||||
FileUtils.ln_s formula_path, alias_dir/"bar"
|
||||
expect(described_class.factory("#{tap}/bar")).to be_a(Formula)
|
||||
expect(described_class.factory("#{tap.name}/#{alias_name}")).to be_a(Formula)
|
||||
end
|
||||
|
||||
it "raises an error when the Formula cannot be found" do
|
||||
|
Loading…
x
Reference in New Issue
Block a user