Module: Formulary

Defined in:
Library/Homebrew/formulary.rb

Overview

The Formulary is responsible for creating instances of Formula. It is not meant to be used directly from formulae.

Defined Under Namespace

Classes: AliasLoader, BottleLoader, FormulaContentsLoader, FormulaLoader, FromPathLoader, FromUrlLoader, NullLoader, TapLoader

Constant Summary

FORMULAE =
{}

Class Method Summary collapse

Class Method Details

.canonical_name(ref) ⇒ Object



336
337
338
339
340
341
342
# File 'Library/Homebrew/formulary.rb', line 336

def self.canonical_name(ref)
  loader_for(ref).name
rescue TapFormulaAmbiguityError
  # If there are multiple tap formulae with the name of ref,
  # then ref is the canonical name
  ref.downcase
end

.class_s(name) ⇒ Object



60
61
62
63
64
65
66
# File 'Library/Homebrew/formulary.rb', line 60

def self.class_s(name)
  class_name = name.capitalize
  class_name.gsub!(/[-_.\s]([a-zA-Z0-9])/) { Regexp.last_match(1).upcase }
  class_name.tr!("+", "x")
  class_name.sub!(/(.)@(\d)/, "\\1AT\\2")
  class_name
end

.core_path(name) ⇒ Object



416
417
418
# File 'Library/Homebrew/formulary.rb', line 416

def self.core_path(name)
  CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb"
end

.ensure_utf8_encoding(io) ⇒ Object



51
52
53
# File 'Library/Homebrew/formulary.rb', line 51

def self.ensure_utf8_encoding(io)
  io.set_encoding(Encoding::UTF_8)
end

.factory(ref, spec = :stable, alias_path: nil, from: nil) ⇒ Object

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


274
275
276
# File 'Library/Homebrew/formulary.rb', line 274

def self.factory(ref, spec = :stable, alias_path: nil, from: nil)
  loader_for(ref, from: from).get_formula(spec, alias_path: alias_path)
end

.find_with_priority(ref, spec = :stable) ⇒ Object



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'Library/Homebrew/formulary.rb', line 432

def self.find_with_priority(ref, spec = :stable)
  possible_pinned_tap_formulae = tap_paths(ref, Dir["#{HOMEBREW_LIBRARY}/PinnedTaps/*/*/"]).map(&:realpath)
  if possible_pinned_tap_formulae.size > 1
    raise TapFormulaAmbiguityError.new(ref, possible_pinned_tap_formulae)
  end

  if possible_pinned_tap_formulae.size == 1
    selected_formula = factory(possible_pinned_tap_formulae.first, spec)
    if core_path(ref).file?
      opoo <<-EOS.undent
        #{ref} is provided by core, but is now shadowed by #{selected_formula.full_name}.
        To refer to the core formula, use Homebrew/core/#{ref} instead.
      EOS
    end
    selected_formula
  else
    factory(ref, spec)
  end
end

.formula_class_defined?(path) ⇒ Boolean

Returns:

  • (Boolean)


10
11
12
# File 'Library/Homebrew/formulary.rb', line 10

def self.formula_class_defined?(path)
  FORMULAE.key?(path)
end

.formula_class_get(path) ⇒ Object



14
15
16
# File 'Library/Homebrew/formulary.rb', line 14

def self.formula_class_get(path)
  FORMULAE.fetch(path)
end

.from_contents(name, path, contents, spec = :stable) ⇒ Object

Return a Formula instance directly from contents



319
320
321
# File 'Library/Homebrew/formulary.rb', line 319

def self.from_contents(name, path, contents, spec = :stable)
  FormulaContentsLoader.new(name, path, contents).get_formula(spec)
end

.from_keg(keg, spec = nil, alias_path: nil) ⇒ Object

Return a Formula instance for the given keg. It will auto resolve formula's spec when requested spec is nil



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'Library/Homebrew/formulary.rb', line 297

def self.from_keg(keg, spec = nil, alias_path: nil)
  tab = Tab.for_keg(keg)
  tap = tab.tap
  spec ||= tab.spec

  f = if tap.nil?
    factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg)
  else
    begin
      factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg)
    rescue FormulaUnavailableError
      # formula may be migrated to different tap. Try to search in core and all taps.
      factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg)
    end
  end
  f.build = tab
  f.build.used_options = Tab.remap_deprecated_options(f.deprecated_options, tab.used_options).as_flags
  f.version.update_commit(keg.version.version.commit) if f.head? && keg.version.head?
  f
end

.from_rack(rack, spec = nil, alias_path: nil) ⇒ Object

Return a Formula instance for the given rack. It will auto resolve formula's spec when requested spec is nil

The :alias_path option will be used if the formula is found not to be installed, and discarded if it is installed because the alias_path used to install the formula will be set instead.



284
285
286
287
288
289
290
291
292
293
# File 'Library/Homebrew/formulary.rb', line 284

def self.from_rack(rack, spec = nil, alias_path: nil)
  kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : []
  keg = kegs.detect(&:linked?) || kegs.detect(&:optlinked?) || kegs.max_by(&:version)

  if keg
    from_keg(keg, spec, alias_path: alias_path)
  else
    factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack)
  end
end

.load_formula(name, path, contents, namespace) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'Library/Homebrew/formulary.rb', line 18

def self.load_formula(name, path, contents, namespace)
  if ENV["HOMEBREW_DISABLE_LOAD_FORMULA"]
    raise "Formula loading disabled by HOMEBREW_DISABLE_LOAD_FORMULA!"
  end

  mod = Module.new
  const_set(namespace, mod)
  begin
    mod.module_eval(contents, path)
  rescue ScriptError => e
    raise FormulaUnreadableError.new(name, e)
  end
  class_name = class_s(name)

  begin
    mod.const_get(class_name)
  rescue NameError => original_exception
    class_list = mod.constants
                    .map { |const_name| mod.const_get(const_name) }
                    .select { |const| const.is_a?(Class) }
    e = FormulaClassUnavailableError.new(name, path, class_name, class_list)
    raise e, "", original_exception.backtrace
  end
end

.load_formula_from_path(name, path) ⇒ Object



43
44
45
46
47
48
# File 'Library/Homebrew/formulary.rb', line 43

def self.load_formula_from_path(name, path)
  contents = path.open("r") { |f| ensure_utf8_encoding(f).read }
  namespace = "FormulaNamespace#{Digest::MD5.hexdigest(path.to_s)}"
  klass = load_formula(name, path, contents, namespace)
  FORMULAE[path] = klass
end

.loader_for(ref, from: nil) ⇒ Object



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
# File 'Library/Homebrew/formulary.rb', line 348

def self.loader_for(ref, from: nil)
  case ref
  when Pathname::BOTTLE_EXTNAME_RX
    return BottleLoader.new(ref)
  when %r{(https?|ftp|file)://}
    return FromUrlLoader.new(ref)
  when HOMEBREW_TAP_FORMULA_REGEX
    return TapLoader.new(ref, from: from)
  end

  if File.extname(ref) == ".rb" && Pathname.new(ref).expand_path.exist?
    return FromPathLoader.new(ref)
  end

  formula_with_that_name = core_path(ref)
  if formula_with_that_name.file?
    return FormulaLoader.new(ref, formula_with_that_name)
  end

  possible_alias = CoreTap.instance.alias_dir/ref
  return AliasLoader.new(possible_alias) if possible_alias.file?

  possible_tap_formulae = tap_paths(ref)
  if possible_tap_formulae.size > 1
    raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae)
  end

  if possible_tap_formulae.size == 1
    path = possible_tap_formulae.first.resolved_path
    name = path.basename(".rb").to_s
    return FormulaLoader.new(name, path)
  end

  if newref = CoreTap.instance.formula_renames[ref]
    formula_with_that_oldname = core_path(newref)
    if formula_with_that_oldname.file?
      return FormulaLoader.new(newref, formula_with_that_oldname)
    end
  end

  possible_tap_newname_formulae = []
  Tap.each do |tap|
    if newref = tap.formula_renames[ref]
      possible_tap_newname_formulae << "#{tap.name}/#{newref}"
    end
  end

  if possible_tap_newname_formulae.size > 1
    raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae)
  end

  unless possible_tap_newname_formulae.empty?
    return TapLoader.new(possible_tap_newname_formulae.first, from: from)
  end

  possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{ref}/.brew/#{ref}.rb")
  if possible_keg_formula.file?
    return FormulaLoader.new(ref, possible_keg_formula)
  end

  possible_cached_formula = Pathname.new("#{HOMEBREW_CACHE_FORMULA}/#{ref}.rb")
  if possible_cached_formula.file?
    return FormulaLoader.new(ref, possible_cached_formula)
  end

  NullLoader.new(ref)
end

.path(ref) ⇒ Object



344
345
346
# File 'Library/Homebrew/formulary.rb', line 344

def self.path(ref)
  loader_for(ref).path
end

.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"]) ⇒ Object



420
421
422
423
424
425
426
427
428
429
430
# File 'Library/Homebrew/formulary.rb', line 420

def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"])
  name = name.to_s.downcase
  taps.map do |tap|
    Pathname.glob([
                    "#{tap}Formula/#{name}.rb",
                    "#{tap}HomebrewFormula/#{name}.rb",
                    "#{tap}#{name}.rb",
                    "#{tap}Aliases/#{name}",
                  ]).detect(&:file?)
  end.compact
end

.to_rack(ref) ⇒ Object



323
324
325
326
327
328
329
330
331
332
333
334
# File 'Library/Homebrew/formulary.rb', line 323

def self.to_rack(ref)
  # If using a fully-scoped reference, check if the formula can be resolved.
  factory(ref) if ref.include? "/"

  # Check whether the rack with the given name exists.
  if (rack = HOMEBREW_CELLAR/File.basename(ref, ".rb")).directory?
    return rack.resolved_path
  end

  # Use canonical name to locate rack.
  (HOMEBREW_CELLAR/canonical_name(ref)).resolved_path
end