brew/Library/Homebrew/formulary.rb

219 lines
5.7 KiB
Ruby
Raw Normal View History

# The Formulary is responsible for creating instances of Formula.
class Formulary
2013-06-23 14:07:46 -07:00
def self.unload_formula formula_name
2014-02-21 00:43:58 -05:00
Object.send(:remove_const, class_s(formula_name))
2013-06-23 14:07:46 -07:00
end
def self.formula_class_defined? class_name
Object.const_defined?(class_name)
2013-06-18 10:11:06 -07:00
end
def self.get_formula_class class_name
Object.const_get(class_name)
2013-06-18 10:11:06 -07:00
end
def self.restore_formula formula_name, value
old_verbose, $VERBOSE = $VERBOSE, nil
2014-02-21 00:43:58 -05:00
Object.const_set(class_s(formula_name), value)
ensure
$VERBOSE = old_verbose
end
2014-02-21 00:43:58 -05:00
def self.class_s name
class_name = name.capitalize
class_name.gsub!(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase }
class_name.gsub!('+', 'x')
class_name
2014-02-21 00:43:58 -05:00
end
2013-06-18 10:11:06 -07:00
# 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
# The ruby constant name of the formula's class
attr_reader :class_name
def initialize(name, path)
@name = name
@path = path
@class_name = Formulary.class_s(name)
end
2013-06-18 10:11:06 -07:00
# Gets the formula instance.
def get_formula
klass.new(name, path)
end
2013-06-18 10:11:06 -07:00
# Return the Class for this formula, `require`-ing it if
# it has not been parsed before.
def klass
begin
have_klass = Formulary.formula_class_defined? class_name
rescue NameError
raise FormulaUnavailableError.new(name)
end
unless have_klass
2014-03-07 17:33:02 -06:00
puts "#{$0} (#{self.class.name}): loading #{path}" if ARGV.debug?
2013-06-18 10:11:06 -07:00
begin
require path
2013-06-18 10:11:06 -07:00
rescue NoMethodError
# This is a programming error in an existing formula, and should not
# have a "no such formula" message.
raise
rescue LoadError, NameError
raise if ARGV.debug? # let's see the REAL error
2013-06-18 10:11:06 -07:00
raise FormulaUnavailableError.new(name)
end
end
klass = Formulary.get_formula_class(class_name)
if klass == Formula || !(klass < Formula)
2013-06-18 10:11:06 -07:00
raise FormulaUnavailableError.new(name)
end
klass
end
end
2013-06-18 10:11:06 -07:00
# Loads formulae from bottles.
class BottleLoader < FormulaLoader
def initialize bottle_name
@bottle_filename = Pathname(bottle_name).realpath
name_without_version = bottle_filename_formula_name @bottle_filename
if name_without_version.empty?
if ARGV.homebrew_developer?
opoo "Add a new regex to bottle_version.rb to parse this filename."
end
name = bottle_name
else
name = name_without_version
end
super name, Formula.path(name)
2013-06-18 10:11:06 -07:00
end
2013-06-18 10:11:06 -07:00
def get_formula
formula = super
formula.local_bottle_path = @bottle_filename
formula
2013-06-18 10:11:06 -07:00
end
end
2013-06-18 10:11:06 -07:00
# Loads formulae from Homebrew's provided Library
class StandardLoader < FormulaLoader
def initialize name
super name, Formula.path(name)
2013-06-18 10:11:06 -07:00
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.
2014-04-05 17:41:36 -05:00
path = "#{path}.rb" unless File.extname(path) == ".rb"
path = Pathname.new(path).expand_path
super path.stem, path
2013-06-18 10:11:06 -07:00
end
end
# Loads formulae from URLs
2013-06-18 10:11:06 -07:00
class FromUrlLoader < FormulaLoader
attr_reader :url
def initialize url
@url = url
super File.basename(url, ".rb"), HOMEBREW_CACHE_FORMULA/File.basename(url)
2013-06-18 10:11:06 -07:00
end
# Downloads the formula's .rb file
def fetch
unless Formulary.formula_class_defined? class_name
2013-06-18 10:11:06 -07:00
HOMEBREW_CACHE_FORMULA.mkpath
FileUtils.rm path.to_s, :force => true
curl url, '-o', path.to_s
end
end
2013-06-18 10:11:06 -07:00
def get_formula
fetch
super
end
2013-06-18 10:11:06 -07:00
end
2013-06-18 10:11:06 -07:00
# Loads tapped formulae.
class TapLoader < FormulaLoader
def initialize tapped_name
super tapped_name, Pathname.new(tapped_name)
end
2013-06-18 10:11:06 -07:00
def get_formula
super
rescue FormulaUnavailableError => e
raise TapFormulaUnavailableError.new(e.name)
2013-06-18 10:11:06 -07:00
end
end
# Return a Formula instance for the given reference.
# `ref` is string containing:
# * a formula name
# * a formula pathname
# * a formula URL
# * a local bottle reference
2013-06-18 10:11:06 -07:00
def self.factory ref
loader_for(ref).get_formula
end
def self.loader_for(ref)
case ref
when %r[(https?|ftp)://]
return FromUrlLoader.new(ref)
when Pathname::BOTTLE_EXTNAME_RX
return BottleLoader.new(ref)
end
if ref =~ HOMEBREW_TAP_FORMULA_REGEX
tap_name = "#$1-#$2".downcase
tapd = Pathname.new("#{HOMEBREW_LIBRARY}/Taps/#{tap_name}")
if tapd.directory?
tapd.find_formula do |relative_pathname|
path = "#{tapd}/#{relative_pathname}"
if relative_pathname.stem.to_s == $3
return FromPathLoader.new(path)
end
end
2013-06-18 10:11:06 -07:00
else
return TapLoader.new(ref)
2013-06-18 10:11:06 -07:00
end
end
if ref.include?("/") || File.extname(ref) == ".rb"
return FromPathLoader.new(ref)
end
formula_with_that_name = Formula.path(ref)
if formula_with_that_name.file? and formula_with_that_name.readable?
return StandardLoader.new(ref)
end
# test if the name is a formula alias
possible_alias = Pathname.new("#{HOMEBREW_LIBRARY}/Aliases/#{ref}")
if possible_alias.file?
name = possible_alias.resolved_path.basename(".rb").to_s
return StandardLoader.new(name)
end
possible_cached_formula = Pathname.new("#{HOMEBREW_CACHE_FORMULA}/#{ref}.rb")
if possible_cached_formula.file?
return FromPathLoader.new(possible_cached_formula.to_s)
end
return StandardLoader.new(ref)
end
end