mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Use Formula Loaders
This commit is contained in:
parent
099a62c95b
commit
c2a5e3608c
@ -1,6 +1,142 @@
|
||||
# The Formulary is responsible for creating instances
|
||||
# of Formula.
|
||||
class Formulary
|
||||
# Return a Formula instance for the given `name`.
|
||||
# `name` may be:
|
||||
|
||||
def self.formula_class_defined? formula_name
|
||||
Object.const_defined?(Formula.class_s(formula_name))
|
||||
end
|
||||
|
||||
def self.get_formula_class formula_name
|
||||
Object.const_get(Formula.class_s(formula_name))
|
||||
end
|
||||
|
||||
# A FormulaLoader returns instances of formulae.
|
||||
# Subclasses implement loaders for particular sources of formulae.
|
||||
class FormulaLoader
|
||||
# The formula's name
|
||||
attr_reader :name
|
||||
# The formula's ruby file's path or filename
|
||||
attr_reader :path
|
||||
|
||||
# Gets the formula instance.
|
||||
# Subclasses must define this.
|
||||
def get_formula; end
|
||||
|
||||
# Return the Class for this formula, `require`-ing it if
|
||||
# it has not been parsed before.
|
||||
def klass
|
||||
unless Formulary.formula_class_defined? name
|
||||
puts "#{$0}: loading #{path}" if ARGV.debug?
|
||||
begin
|
||||
require path.to_s
|
||||
rescue NoMethodError
|
||||
# This is a programming error in an existing formula, and should not
|
||||
# have a "no such formula" message.
|
||||
raise
|
||||
rescue LoadError, NameError
|
||||
# TODO - show exception details
|
||||
raise FormulaUnavailableError.new(name)
|
||||
end
|
||||
end
|
||||
klass = Formulary.get_formula_class(name)
|
||||
if (klass == Formula) || !klass.ancestors.include?(Formula)
|
||||
raise FormulaUnavailableError.new(name)
|
||||
end
|
||||
klass
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from bottles.
|
||||
class BottleLoader < FormulaLoader
|
||||
def initialize bottle_name
|
||||
@bottle_filename = Pathname(bottle_name).realpath
|
||||
version = Version.parse(@bottle_filename).to_s
|
||||
bottle_basename = @bottle_filename.basename.to_s
|
||||
name_without_version = bottle_basename.rpartition("-#{version}").first
|
||||
if name_without_version.empty?
|
||||
if ARGV.homebrew_developer?
|
||||
opoo "Add a new version regex to version.rb to parse this filename."
|
||||
end
|
||||
@name = name
|
||||
else
|
||||
@name = name_without_version
|
||||
end
|
||||
@path = Formula.path(@name)
|
||||
end
|
||||
|
||||
def get_formula
|
||||
formula = klass.new(name)
|
||||
formula.downloader.local_bottle_path = @bottle_filename
|
||||
return formula
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from Homebrew's provided Library
|
||||
class StandardLoader < FormulaLoader
|
||||
def initialize name
|
||||
@name = name
|
||||
@path = Formula.path(name)
|
||||
end
|
||||
|
||||
def get_formula
|
||||
return klass.new(name)
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from disk using a path
|
||||
class FromPathLoader < FormulaLoader
|
||||
def initialize path
|
||||
# require allows filenames to drop the .rb extension, but everything else
|
||||
# in our codebase will require an exact and fullpath.
|
||||
path = "#{name}.rb" unless path =~ /\.rb$/
|
||||
|
||||
@path = Pathname.new(path)
|
||||
@name = @path.stem
|
||||
end
|
||||
|
||||
def get_formula
|
||||
klass.new(name, path.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Loads formulae from URLs.
|
||||
class FromUrlLoader < FormulaLoader
|
||||
attr_reader :url
|
||||
|
||||
def initialize url
|
||||
@url = url
|
||||
@path = (HOMEBREW_CACHE_FORMULA/(File.basename(url)))
|
||||
@name = File.basename(url, '.rb')
|
||||
end
|
||||
|
||||
# Downloads the formula's .rb file
|
||||
def fetch
|
||||
unless Formulary.formula_class_defined? name
|
||||
HOMEBREW_CACHE_FORMULA.mkpath
|
||||
FileUtils.rm path.to_s, :force => true
|
||||
curl url, '-o', path.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def get_formula
|
||||
return klass.new(name, path.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Loads tapped formulae.
|
||||
class TapLoader < FormulaLoader
|
||||
def initialize tapped_name
|
||||
@name = tapped_name
|
||||
@path = Pathname.new(tapped_name)
|
||||
end
|
||||
|
||||
def get_formula
|
||||
klass.new(tapped_name, path.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# Return a Formula instance for the given reference.
|
||||
# `ref` may be:
|
||||
# * a Formula instance, in which case it is returned
|
||||
# TODO: is this code path used?
|
||||
# * a Pathname to a local formula
|
||||
@ -8,99 +144,35 @@ class Formulary
|
||||
# * a string containing a formula URL
|
||||
# * a string containing a formula name
|
||||
# * a string containing a local bottle reference
|
||||
def self.factory name
|
||||
def self.factory ref
|
||||
# If an instance of Formula is passed, just return it
|
||||
return name if name.kind_of? Formula
|
||||
return ref if ref.kind_of? Formula
|
||||
|
||||
# Otherwise, convert to String in case a Pathname comes in
|
||||
name = name.to_s
|
||||
# TODO - do we call with a Pathname instead of a string anywhere?
|
||||
ref = ref.to_s
|
||||
|
||||
# If a URL is passed, download to the cache and install
|
||||
if name =~ %r[(https?|ftp)://]
|
||||
url = name
|
||||
name = Pathname.new(name).basename
|
||||
path = HOMEBREW_CACHE_FORMULA+name
|
||||
name = name.basename(".rb").to_s
|
||||
|
||||
unless Object.const_defined? Formula.class_s(name)
|
||||
HOMEBREW_CACHE_FORMULA.mkpath
|
||||
FileUtils.rm path, :force => true
|
||||
curl url, '-o', path
|
||||
end
|
||||
|
||||
install_type = :from_url
|
||||
elsif name.match bottle_regex
|
||||
bottle_filename = Pathname(name).realpath
|
||||
version = Version.parse(bottle_filename).to_s
|
||||
bottle_basename = bottle_filename.basename.to_s
|
||||
name_without_version = bottle_basename.rpartition("-#{version}").first
|
||||
if name_without_version.empty?
|
||||
if ARGV.homebrew_developer?
|
||||
opoo "Add a new version regex to version.rb to parse this filename."
|
||||
end
|
||||
else
|
||||
name = name_without_version
|
||||
end
|
||||
path = Formula.path(name)
|
||||
install_type = :from_local_bottle
|
||||
if ref =~ %r[(https?|ftp)://]
|
||||
f = FromUrlLoader.new(ref)
|
||||
f.fetch
|
||||
elsif ref =~ Pathname::BOTTLE_EXTNAME_RX
|
||||
f = BottleLoader.new(ref)
|
||||
else
|
||||
name = Formula.canonical_name(name)
|
||||
|
||||
if name =~ %r{^(\w+)/(\w+)/([^/])+$}
|
||||
name_or_path = Formula.canonical_name(ref)
|
||||
if name_or_path =~ %r{^(\w+)/(\w+)/([^/])+$}
|
||||
# name appears to be a tapped formula, so we don't munge it
|
||||
# in order to provide a useful error message when require fails.
|
||||
path = Pathname.new(name)
|
||||
elsif name.include? "/"
|
||||
f = TapLoader.new(name_or_path)
|
||||
elsif name_or_path.include? "/"
|
||||
# If name was a path or mapped to a cached formula
|
||||
|
||||
# require allows filenames to drop the .rb extension, but everything else
|
||||
# in our codebase will require an exact and fullpath.
|
||||
name = "#{name}.rb" unless name =~ /\.rb$/
|
||||
|
||||
path = Pathname.new(name)
|
||||
name = path.stem
|
||||
install_type = :from_path
|
||||
f = FromPathLoader.new(name_or_path)
|
||||
else
|
||||
# For names, map to the path and then require
|
||||
path = Formula.path(name)
|
||||
install_type = :from_name
|
||||
f = StandardLoader.new(name_or_path)
|
||||
end
|
||||
end
|
||||
|
||||
klass_name = Formula.class_s(name)
|
||||
unless Object.const_defined? klass_name
|
||||
puts "#{$0}: loading #{path}" if ARGV.debug?
|
||||
require path
|
||||
end
|
||||
|
||||
begin
|
||||
klass = Object.const_get klass_name
|
||||
rescue NameError
|
||||
# TODO really this text should be encoded into the exception
|
||||
# and only shown if the UI deems it correct to show it
|
||||
onoe "class \"#{klass_name}\" expected but not found in #{name}.rb"
|
||||
puts "Double-check the name of the class in that formula."
|
||||
raise LoadError
|
||||
end
|
||||
|
||||
if install_type == :from_local_bottle
|
||||
formula = klass.new(name)
|
||||
formula.downloader.local_bottle_path = bottle_filename
|
||||
return formula
|
||||
end
|
||||
|
||||
raise NameError if !klass.ancestors.include? Formula
|
||||
raise NameError if klass == Formula
|
||||
|
||||
return klass.new(name) if install_type == :from_name
|
||||
return klass.new(name, path.to_s)
|
||||
rescue NoMethodError
|
||||
# This is a programming error in an existing formula, and should not
|
||||
# have a "no such formula" message.
|
||||
raise
|
||||
rescue LoadError, NameError
|
||||
# Catch NameError so that things that are invalid symbols still get
|
||||
# a useful error message.
|
||||
raise FormulaUnavailableError.new(name)
|
||||
f.get_formula
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user