2013-06-23 12:47:10 -07:00
|
|
|
# The Formulary is responsible for creating instances of Formula.
|
2013-06-08 20:58:43 -07:00
|
|
|
class Formulary
|
|
|
|
|
2013-06-23 14:07:46 -07:00
|
|
|
def self.unload_formula formula_name
|
|
|
|
Object.send(:remove_const, Formula.class_s(formula_name))
|
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def self.formula_class_defined? formula_name
|
|
|
|
Object.const_defined?(Formula.class_s(formula_name))
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def self.get_formula_class formula_name
|
|
|
|
Object.const_get(Formula.class_s(formula_name))
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
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
|
|
|
|
|
|
|
|
# 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
|
2013-06-30 10:26:12 -07:00
|
|
|
begin
|
|
|
|
have_klass = Formulary.formula_class_defined? name
|
|
|
|
rescue NameError
|
|
|
|
raise FormulaUnavailableError.new(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
unless have_klass
|
2013-06-18 10:11:06 -07:00
|
|
|
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
|
|
|
|
raise FormulaUnavailableError.new(name)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2013-06-30 10:26:12 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
klass = Formulary.get_formula_class(name)
|
|
|
|
if (klass == Formula) || !klass.ancestors.include?(Formula)
|
|
|
|
raise FormulaUnavailableError.new(name)
|
|
|
|
end
|
|
|
|
klass
|
|
|
|
end
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
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
|
|
|
|
version = Version.parse(@bottle_filename).to_s
|
|
|
|
bottle_basename = @bottle_filename.basename.to_s
|
2013-06-08 20:58:43 -07:00
|
|
|
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
|
2013-07-04 11:20:59 +01:00
|
|
|
@name = bottle_name
|
2013-06-08 20:58:43 -07:00
|
|
|
else
|
2013-06-18 10:11:06 -07:00
|
|
|
@name = name_without_version
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
@path = Formula.path(@name)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def get_formula
|
|
|
|
formula = klass.new(name)
|
|
|
|
formula.downloader.local_bottle_path = @bottle_filename
|
|
|
|
return formula
|
|
|
|
end
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
# Loads formulae from Homebrew's provided Library
|
|
|
|
class StandardLoader < FormulaLoader
|
|
|
|
def initialize name
|
|
|
|
@name = name
|
|
|
|
@path = Formula.path(name)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
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.
|
2013-06-30 14:36:12 -07:00
|
|
|
path = "#{path}.rb" unless path =~ /\.rb$/
|
2013-06-18 10:11:06 -07:00
|
|
|
|
|
|
|
@path = Pathname.new(path)
|
|
|
|
@name = @path.stem
|
|
|
|
end
|
|
|
|
|
|
|
|
def get_formula
|
|
|
|
klass.new(name, path.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-23 12:47:10 -07:00
|
|
|
# Loads formulae from URLs
|
2013-06-18 10:11:06 -07:00
|
|
|
class FromUrlLoader < FormulaLoader
|
|
|
|
attr_reader :url
|
|
|
|
|
|
|
|
def initialize url
|
|
|
|
@url = url
|
2013-06-23 12:47:10 -07:00
|
|
|
@path = HOMEBREW_CACHE_FORMULA/File.basename(url)
|
2013-06-18 10:11:06 -07:00
|
|
|
@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
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def get_formula
|
|
|
|
return klass.new(name, path.to_s)
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
# Loads tapped formulae.
|
|
|
|
class TapLoader < FormulaLoader
|
|
|
|
def initialize tapped_name
|
|
|
|
@name = tapped_name
|
|
|
|
@path = Pathname.new(tapped_name)
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def get_formula
|
|
|
|
klass.new(tapped_name, path.to_s)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Return a Formula instance for the given reference.
|
2013-06-23 12:47:10 -07:00
|
|
|
# `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
|
|
|
|
# If a URL is passed, download to the cache and install
|
|
|
|
if ref =~ %r[(https?|ftp)://]
|
|
|
|
f = FromUrlLoader.new(ref)
|
|
|
|
f.fetch
|
|
|
|
elsif ref =~ Pathname::BOTTLE_EXTNAME_RX
|
|
|
|
f = BottleLoader.new(ref)
|
|
|
|
else
|
|
|
|
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.
|
|
|
|
f = TapLoader.new(name_or_path)
|
|
|
|
elsif name_or_path.include? "/"
|
|
|
|
# If name was a path or mapped to a cached formula
|
|
|
|
f = FromPathLoader.new(name_or_path)
|
2013-06-30 16:43:28 -07:00
|
|
|
elsif name_or_path =~ /\.rb$/
|
|
|
|
f = FromPathLoader.new("./#{name_or_path}")
|
2013-06-18 10:11:06 -07:00
|
|
|
else
|
|
|
|
f = StandardLoader.new(name_or_path)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
f.get_formula
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
end
|