2013-06-23 12:47:10 -07:00
|
|
|
# The Formulary is responsible for creating instances of Formula.
|
2014-05-02 07:39:23 -07:00
|
|
|
# It is not meant to be used directy from formulae.
|
|
|
|
|
2013-06-08 20:58:43 -07:00
|
|
|
class Formulary
|
2015-01-01 01:21:59 -05:00
|
|
|
module Formulae
|
|
|
|
class << self
|
|
|
|
if instance_method(:const_defined?).arity == -1
|
|
|
|
def formula_const_defined?(name)
|
|
|
|
const_defined?(name, false)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
def formula_const_get(name)
|
|
|
|
const_get(name, false)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
def formula_const_defined?(name)
|
|
|
|
const_defined?(name)
|
|
|
|
end
|
2013-06-23 14:07:46 -07:00
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
def formula_const_get(name)
|
|
|
|
const_get(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def remove_formula_const(name)
|
|
|
|
remove_const(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def formula_const_set(name, value)
|
|
|
|
const_set(name, value)
|
|
|
|
end
|
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
def self.unload_formula formula_name
|
|
|
|
Formulae.remove_formula_const(class_s(formula_name))
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2013-12-09 21:10:32 -06:00
|
|
|
def self.restore_formula formula_name, value
|
|
|
|
old_verbose, $VERBOSE = $VERBOSE, nil
|
2015-01-01 01:21:59 -05:00
|
|
|
Formulae.formula_const_set(class_s(formula_name), value)
|
2013-12-09 21:10:32 -06:00
|
|
|
ensure
|
|
|
|
$VERBOSE = old_verbose
|
|
|
|
end
|
|
|
|
|
2014-02-21 00:43:58 -05:00
|
|
|
def self.class_s name
|
2014-04-03 22:40:40 -05:00
|
|
|
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
|
2014-04-03 22:40:40 -05:00
|
|
|
# The ruby constant name of the formula's class
|
|
|
|
attr_reader :class_name
|
|
|
|
|
|
|
|
def initialize(name, path)
|
|
|
|
@name = name
|
2014-04-06 00:31:07 -05:00
|
|
|
@path = path.resolved_path
|
2014-04-03 22:40:40 -05:00
|
|
|
@class_name = Formulary.class_s(name)
|
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
|
|
|
|
# Gets the formula instance.
|
2014-06-19 21:35:46 -05:00
|
|
|
def get_formula(spec)
|
|
|
|
klass.new(name, path, spec)
|
2014-03-07 17:23:44 -06:00
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
|
|
|
|
def klass
|
2013-06-30 10:26:12 -07:00
|
|
|
begin
|
2015-01-01 01:21:59 -05:00
|
|
|
have_klass = Formulae.formula_const_defined?(class_name)
|
2014-12-29 15:23:01 -05:00
|
|
|
rescue NameError => e
|
2014-12-31 11:13:45 -05:00
|
|
|
raise unless e.name.to_s == class_name
|
2014-12-29 15:23:01 -05:00
|
|
|
raise FormulaUnavailableError, name, e.backtrace
|
2013-06-30 10:26:12 -07:00
|
|
|
end
|
|
|
|
|
2014-12-29 14:53:22 -05:00
|
|
|
load_file unless have_klass
|
2013-06-30 10:26:12 -07:00
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
Formulae.formula_const_get(class_name)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2014-12-29 14:53:22 -05:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def load_file
|
|
|
|
STDERR.puts "#{$0} (#{self.class.name}): loading #{path}" if ARGV.debug?
|
2014-12-31 11:13:45 -05:00
|
|
|
raise FormulaUnavailableError.new(name) unless path.file?
|
2015-01-01 01:21:59 -05:00
|
|
|
Formulae.module_eval(path.read, path)
|
2014-12-29 14:53:22 -05: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 formulae from bottles.
|
|
|
|
class BottleLoader < FormulaLoader
|
|
|
|
def initialize bottle_name
|
|
|
|
@bottle_filename = Pathname(bottle_name).realpath
|
2013-07-04 11:21:50 +01:00
|
|
|
name_without_version = bottle_filename_formula_name @bottle_filename
|
2013-06-08 20:58:43 -07:00
|
|
|
if name_without_version.empty?
|
|
|
|
if ARGV.homebrew_developer?
|
2013-08-04 08:25:51 -07:00
|
|
|
opoo "Add a new regex to bottle_version.rb to parse this filename."
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2014-04-03 22:40:40 -05:00
|
|
|
name = bottle_name
|
2013-06-08 20:58:43 -07:00
|
|
|
else
|
2014-04-03 22:40:40 -05:00
|
|
|
name = name_without_version
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2014-04-03 22:40:40 -05:00
|
|
|
|
2015-05-08 18:16:15 +08:00
|
|
|
super name, Formulary.path(name)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2014-06-19 21:35:46 -05:00
|
|
|
def get_formula(spec)
|
2014-03-07 17:23:44 -06:00
|
|
|
formula = super
|
2013-09-25 18:51:30 -05:00
|
|
|
formula.local_bottle_path = @bottle_filename
|
2014-03-07 17:23:44 -06:00
|
|
|
formula
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2014-04-05 22:03:34 -05:00
|
|
|
class AliasLoader < FormulaLoader
|
|
|
|
def initialize alias_path
|
|
|
|
path = alias_path.resolved_path
|
|
|
|
name = path.basename(".rb").to_s
|
|
|
|
super name, path
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
# Loads formulae from disk using a path
|
|
|
|
class FromPathLoader < FormulaLoader
|
|
|
|
def initialize path
|
2014-04-03 22:40:40 -05:00
|
|
|
path = Pathname.new(path).expand_path
|
2014-04-05 22:03:34 -05:00
|
|
|
super path.basename(".rb").to_s, path
|
2013-06-18 10:11:06 -07:00
|
|
|
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
|
2014-04-10 18:58:09 -04:00
|
|
|
uri = URI(url)
|
|
|
|
formula = File.basename(uri.path, ".rb")
|
|
|
|
super formula, HOMEBREW_CACHE_FORMULA/File.basename(uri.path)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
|
|
|
|
2014-12-29 14:53:22 -05:00
|
|
|
def load_file
|
|
|
|
HOMEBREW_CACHE_FORMULA.mkpath
|
|
|
|
FileUtils.rm_f(path)
|
|
|
|
curl url, "-o", path
|
2014-03-07 17:23:44 -06:00
|
|
|
super
|
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
|
2014-04-05 22:03:33 -05:00
|
|
|
attr_reader :tapped_name
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
def initialize tapped_name
|
2014-04-05 22:03:33 -05:00
|
|
|
@tapped_name = tapped_name
|
|
|
|
user, repo, name = tapped_name.split("/", 3).map(&:downcase)
|
2014-04-24 11:26:45 +09:00
|
|
|
tap = Pathname.new("#{HOMEBREW_LIBRARY}/Taps/#{user}/homebrew-#{repo}")
|
2014-04-07 18:44:29 -05:00
|
|
|
path = tap.join("#{name}.rb")
|
|
|
|
|
|
|
|
if tap.directory?
|
2014-04-25 18:58:16 -05:00
|
|
|
tap.find_formula do |file|
|
|
|
|
if file.basename(".rb").to_s == name
|
|
|
|
path = file
|
2014-04-07 18:44:29 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-05 22:03:33 -05:00
|
|
|
super name, path
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2014-06-19 21:35:46 -05:00
|
|
|
def get_formula(spec)
|
2014-03-07 17:23:44 -06:00
|
|
|
super
|
2014-12-29 15:23:01 -05:00
|
|
|
rescue FormulaUnavailableError => e
|
|
|
|
raise TapFormulaUnavailableError, tapped_name, e.backtrace
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
class NullLoader < FormulaLoader
|
|
|
|
def initialize(name)
|
2015-05-08 18:59:08 +08:00
|
|
|
super name, Formulary.core_path(name)
|
2015-01-01 01:21:59 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def get_formula(spec)
|
|
|
|
raise FormulaUnavailableError.new(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
# 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
|
2014-06-19 21:35:46 -05:00
|
|
|
def self.factory(ref, spec=:stable)
|
|
|
|
loader_for(ref).get_formula(spec)
|
2014-04-05 22:03:33 -05:00
|
|
|
end
|
|
|
|
|
2015-05-16 11:26:26 +08:00
|
|
|
# Return a Formula instance for the given rack.
|
|
|
|
def self.from_rack(rack, spec=:stable)
|
|
|
|
kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : []
|
|
|
|
|
|
|
|
keg = kegs.detect(&:linked?) || kegs.detect(&:optlinked?) || kegs.max_by(&:version)
|
|
|
|
return factory(rack.basename.to_s, spec) unless keg
|
|
|
|
|
|
|
|
tap = Tab.for_keg(keg).tap
|
|
|
|
|
2015-05-28 16:23:20 -07:00
|
|
|
if tap.nil? || tap == "Homebrew/homebrew" || tap == "mxcl/master"
|
2015-05-16 11:26:26 +08:00
|
|
|
factory(rack.basename.to_s, spec)
|
|
|
|
else
|
|
|
|
factory("#{tap.sub("homebrew-", "")}/#{rack.basename}", spec)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-04-05 22:03:34 -05:00
|
|
|
def self.canonical_name(ref)
|
|
|
|
loader_for(ref).name
|
2015-05-15 19:41:31 +08:00
|
|
|
rescue TapFormulaAmbiguityError
|
|
|
|
# If there are multiple tap formulae with the name of ref,
|
|
|
|
# then ref is the canonical name
|
|
|
|
ref.downcase
|
2014-04-05 22:03:34 -05:00
|
|
|
end
|
|
|
|
|
2015-05-08 13:48:36 +08:00
|
|
|
def self.path(ref)
|
|
|
|
loader_for(ref).path
|
|
|
|
end
|
|
|
|
|
2014-04-05 22:03:33 -05:00
|
|
|
def self.loader_for(ref)
|
|
|
|
case ref
|
|
|
|
when %r[(https?|ftp)://]
|
|
|
|
return FromUrlLoader.new(ref)
|
|
|
|
when Pathname::BOTTLE_EXTNAME_RX
|
|
|
|
return BottleLoader.new(ref)
|
2014-04-05 22:03:33 -05:00
|
|
|
when HOMEBREW_TAP_FORMULA_REGEX
|
|
|
|
return TapLoader.new(ref)
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2014-04-05 22:03:34 -05:00
|
|
|
if File.extname(ref) == ".rb"
|
2014-04-05 22:03:33 -05:00
|
|
|
return FromPathLoader.new(ref)
|
|
|
|
end
|
|
|
|
|
2015-05-08 18:59:08 +08:00
|
|
|
formula_with_that_name = core_path(ref)
|
2014-04-05 22:03:33 -05:00
|
|
|
if formula_with_that_name.file?
|
2015-01-01 01:21:59 -05:00
|
|
|
return FormulaLoader.new(ref, formula_with_that_name)
|
2014-04-05 22:03:33 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
possible_alias = Pathname.new("#{HOMEBREW_LIBRARY}/Aliases/#{ref}")
|
|
|
|
if possible_alias.file?
|
2014-04-05 22:03:34 -05:00
|
|
|
return AliasLoader.new(possible_alias)
|
2014-04-05 22:03:33 -05:00
|
|
|
end
|
|
|
|
|
2015-05-08 19:16:06 +08:00
|
|
|
possible_tap_formulae = tap_paths(ref)
|
|
|
|
if possible_tap_formulae.size > 1
|
|
|
|
raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae)
|
|
|
|
elsif possible_tap_formulae.size == 1
|
|
|
|
return FormulaLoader.new(ref, possible_tap_formulae.first)
|
|
|
|
end
|
|
|
|
|
2014-04-05 22:03:33 -05:00
|
|
|
possible_cached_formula = Pathname.new("#{HOMEBREW_CACHE_FORMULA}/#{ref}.rb")
|
|
|
|
if possible_cached_formula.file?
|
2015-01-01 01:21:59 -05:00
|
|
|
return FormulaLoader.new(ref, possible_cached_formula)
|
2014-04-05 22:03:33 -05:00
|
|
|
end
|
|
|
|
|
2015-01-01 01:21:59 -05:00
|
|
|
return NullLoader.new(ref)
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
2015-05-08 18:59:08 +08:00
|
|
|
|
|
|
|
def self.core_path(name)
|
|
|
|
Pathname.new("#{HOMEBREW_LIBRARY}/Formula/#{name.downcase}.rb")
|
|
|
|
end
|
2015-05-08 19:16:06 +08:00
|
|
|
|
|
|
|
def self.tap_paths(name)
|
|
|
|
name = name.downcase
|
|
|
|
Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"].map do |tap|
|
|
|
|
Pathname.glob(["#{tap}#{name}.rb", "#{tap}Formula/#{name}.rb",
|
|
|
|
"#{tap}HomebrewFormula/#{name}.rb"])
|
|
|
|
end.flatten.select(&:file?)
|
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|