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



320
321
322
323
324
325
326
# File 'Library/Homebrew/formulary.rb', line 320

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])/) { $1.upcase }
  class_name.tr!("+", "x")
  class_name.sub!(/(.)@(\d)/, "\\1AT\\2")
  class_name
end

.core_path(name) ⇒ Object



400
401
402
# File 'Library/Homebrew/formulary.rb', line 400

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


258
259
260
# File 'Library/Homebrew/formulary.rb', line 258

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



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
# File 'Library/Homebrew/formulary.rb', line 416

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



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



303
304
305
# File 'Library/Homebrew/formulary.rb', line 303

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



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'Library/Homebrew/formulary.rb', line 281

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.



268
269
270
271
272
273
274
275
276
277
# File 'Library/Homebrew/formulary.rb', line 268

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



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
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
# File 'Library/Homebrew/formulary.rb', line 332

def self.loader_for(ref, from: nil)
  case ref
  when %r{(https?|ftp|file)://}
    return FromUrlLoader.new(ref)
  when Pathname::BOTTLE_EXTNAME_RX
    return BottleLoader.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



328
329
330
# File 'Library/Homebrew/formulary.rb', line 328

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

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



404
405
406
407
408
409
410
411
412
413
414
# File 'Library/Homebrew/formulary.rb', line 404

def self.tap_paths(name, taps = Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/"])
  name = name.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



307
308
309
310
311
312
313
314
315
316
317
318
# File 'Library/Homebrew/formulary.rb', line 307

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