Module: Formulary Private
- Extended by:
- Cachable, T::Sig
- Defined in:
- formulary.rb
Overview
This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.
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 collapse
- URL_START_REGEX =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
%r{(https?|ftp|file)://}.freeze
Class Method Summary collapse
-
.canonical_name(ref) ⇒ Object
private
-
.class_s(name) ⇒ Object
private
-
.clear_cache ⇒ Object
private
-
.core_path(name) ⇒ Object
private
-
.enable_factory_cache! ⇒ void
private
-
.ensure_utf8_encoding(io) ⇒ Object
private
-
.factory(ref, spec = :stable, alias_path: nil, from: nil, force_bottle: false, flags: [], ignore_errors: false) ⇒ Object
private
Return a Formula instance for the given reference.
-
.factory_cached? ⇒ Boolean
private
-
.formula_class_defined?(path) ⇒ Boolean
private
-
.formula_class_get(path) ⇒ Object
private
-
.from_contents(name, path, contents, spec = :stable, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false) ⇒ Object
private
Return a Formula instance directly from contents.
-
.from_keg(keg, spec = nil, alias_path: nil, force_bottle: false, flags: []) ⇒ Object
private
Return a Formula instance for the given keg.
-
.from_rack(rack, spec = nil, alias_path: nil, force_bottle: false, flags: []) ⇒ Object
private
Return a Formula instance for the given rack.
-
.keg_only?(rack) ⇒ Boolean
private
Return whether given rack is keg-only.
-
.load_formula(name, path, contents, namespace, flags:, ignore_errors:) ⇒ Object
private
-
.load_formula_from_path(name, path, flags:, ignore_errors:) ⇒ Object
private
-
.loader_for(ref, from: nil) ⇒ Object
private
-
.map_formula_name_to_local_bottle_path(formula_name, local_bottle_path) ⇒ Object
private
Map a formula name to a local/fetched bottle archive.
-
.path(ref) ⇒ Object
private
-
.resolve(name, spec: nil, force_bottle: false, flags: []) ⇒ Object
private
-
.tap_paths(name, taps = ) ⇒ Object
private
-
.to_rack(ref) ⇒ Object
private
Methods included from Cachable
Class Method Details
.canonical_name(ref) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
523 524 525 526 527 528 529 |
# File 'formulary.rb', line 523 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
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
150 151 152 153 154 155 156 |
# File 'formulary.rb', line 150 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 |
.clear_cache ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'formulary.rb', line 37 def self.clear_cache cache.each do |key, klass| next if key == :formulary_factory namespace = klass.name.deconstantize next if namespace.deconstantize != name remove_const(namespace.demodulize) end super end |
.core_path(name) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
589 590 591 |
# File 'formulary.rb', line 589 def self.core_path(name) CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb" end |
.enable_factory_cache! ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
This method returns an undefined value.
21 22 23 |
# File 'formulary.rb', line 21 def self.enable_factory_cache! @factory_cache = true end |
.ensure_utf8_encoding(io) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
146 147 148 |
# File 'formulary.rb', line 146 def self.ensure_utf8_encoding(io) io.set_encoding(Encoding::UTF_8) end |
.factory(ref, spec = :stable, alias_path: nil, from: nil, force_bottle: false, flags: [], ignore_errors: false) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Return a Formula instance for the given reference.
ref
is a string containing:
- a formula name
- a formula pathname
- a formula URL
- a local bottle reference
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'formulary.rb', line 402 def self.factory( ref, spec = :stable, alias_path: nil, from: nil, force_bottle: false, flags: [], ignore_errors: false ) raise ArgumentError, "Formulae must have a ref!" unless ref if Homebrew::EnvConfig.install_from_api? && @formula_name_local_bottle_path_map.present? && @formula_name_local_bottle_path_map.key?(ref) ref = @formula_name_local_bottle_path_map[ref] end cache_key = "#{ref}-#{spec}-#{alias_path}-#{from}" if factory_cached? && cache[:formulary_factory] && cache[:formulary_factory][cache_key] return cache[:formulary_factory][cache_key] end formula = loader_for(ref, from: from).get_formula(spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags, ignore_errors: ignore_errors) if factory_cached? cache[:formulary_factory] ||= {} cache[:formulary_factory][cache_key] ||= formula end formula end |
.factory_cached? ⇒ Boolean
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
25 26 27 |
# File 'formulary.rb', line 25 def self.factory_cached? !@factory_cache.nil? end |
.formula_class_defined?(path) ⇒ Boolean
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
29 30 31 |
# File 'formulary.rb', line 29 def self.formula_class_defined?(path) cache.key?(path) end |
.formula_class_get(path) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
33 34 35 |
# File 'formulary.rb', line 33 def self.formula_class_get(path) cache.fetch(path) end |
.from_contents(name, path, contents, spec = :stable, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Return a Formula instance directly from contents.
501 502 503 504 505 506 507 508 |
# File 'formulary.rb', line 501 def self.from_contents( name, path, contents, spec = :stable, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false ) FormulaContentsLoader.new(name, path, contents) .get_formula(spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags, ignore_errors: ignore_errors) end |
.from_keg(keg, spec = nil, alias_path: nil, force_bottle: false, flags: []) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Return a Formula instance for the given keg.
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 |
# File 'formulary.rb', line 476 def self.from_keg(keg, spec = nil, alias_path: nil, force_bottle: false, flags: []) 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, force_bottle: force_bottle, flags: flags) else begin factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg, force_bottle: force_bottle, flags: flags) 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, force_bottle: force_bottle, flags: flags) end end f.build = tab f.build. = Tab.(f., tab.).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, force_bottle: false, flags: []) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Return a Formula instance for the given rack.
454 455 456 457 458 459 460 461 462 463 464 |
# File 'formulary.rb', line 454 def self.from_rack(rack, spec = nil, alias_path: nil, force_bottle: false, flags: []) kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : [] keg = kegs.find(&:linked?) || kegs.find(&:optlinked?) || kegs.max_by(&:version) if keg from_keg(keg, spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags) else factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack, force_bottle: force_bottle, flags: flags) end end |
.keg_only?(rack) ⇒ Boolean
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Return whether given rack is keg-only.
467 468 469 470 471 |
# File 'formulary.rb', line 467 def self.keg_only?(rack) Formulary.from_rack(rack).keg_only? rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError false end |
.load_formula(name, path, contents, namespace, flags:, ignore_errors:) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'formulary.rb', line 64 def self.load_formula(name, path, contents, namespace, flags:, ignore_errors:) raise "Formula loading disabled by HOMEBREW_DISABLE_LOAD_FORMULA!" if Homebrew::EnvConfig.disable_load_formula? require "formula" require "ignorable" mod = Module.new remove_const(namespace) if const_defined?(namespace) const_set(namespace, mod) eval_formula = lambda do # Set `BUILD_FLAGS` in the formula's namespace so we can # access them from within the formula's class scope. mod.const_set(:BUILD_FLAGS, flags) mod.module_eval(contents, path) rescue NameError, ArgumentError, ScriptError, MethodDeprecatedError, MacOSVersionError => e if e.is_a?(Ignorable::ExceptionMixin) e.ignore else remove_const(namespace) raise FormulaUnreadableError.new(name, e) end end if ignore_errors Ignorable.hook_raise(&eval_formula) else eval_formula.call end class_name = class_s(name) begin mod.const_get(class_name) rescue NameError => e class_list = mod.constants .map { |const_name| mod.const_get(const_name) } .select { |const| const.is_a?(Class) } new_exception = FormulaClassUnavailableError.new(name, path, class_name, class_list) remove_const(namespace) raise new_exception, "", e.backtrace end end |
.load_formula_from_path(name, path, flags:, ignore_errors:) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
107 108 109 110 111 112 |
# File 'formulary.rb', line 107 def self.load_formula_from_path(name, path, flags:, ignore_errors:) 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, flags: flags, ignore_errors: ignore_errors) cache[path] = klass end |
.loader_for(ref, from: nil) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 |
# File 'formulary.rb', line 535 def self.loader_for(ref, from: nil) case ref when HOMEBREW_BOTTLES_EXTNAME_REGEX return BottleLoader.new(ref) when URL_START_REGEX return FromUrlLoader.new(ref) when HOMEBREW_TAP_FORMULA_REGEX # If `homebrew/core` is specified and not installed, check whether the formula is already installed. if ref.start_with?("homebrew/core/") && !CoreTap.instance.installed? && Homebrew::EnvConfig.install_from_api? name = ref.split("/", 3).last possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{name}/.brew/#{name}.rb") return FormulaLoader.new(name, possible_keg_formula) if possible_keg_formula.file? end return TapLoader.new(ref, from: from) end return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && Pathname.new(ref)..exist? formula_with_that_name = core_path(ref) return FormulaLoader.new(ref, formula_with_that_name) if formula_with_that_name.file? possible_alias = CoreTap.instance.alias_dir/ref return AliasLoader.new(possible_alias) if possible_alias.file? possible_tap_formulae = tap_paths(ref) raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae) if possible_tap_formulae.size > 1 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 return TapLoader.new("#{CoreTap.instance}/#{ref}", from: from) if CoreTap.instance.formula_renames.key?(ref) possible_taps = Tap.select { |tap| tap.formula_renames.key?(ref) } if possible_taps.size > 1 possible_tap_newname_formulae = possible_taps.map { |tap| "#{tap}/#{tap.formula_renames[ref]}" } raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae) end return TapLoader.new("#{possible_taps.first}/#{ref}", from: from) unless possible_taps.empty? possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{ref}/.brew/#{ref}.rb") return FormulaLoader.new(ref, possible_keg_formula) if possible_keg_formula.file? possible_cached_formula = Pathname.new("#{HOMEBREW_CACHE_FORMULA}/#{ref}.rb") return FormulaLoader.new(ref, possible_cached_formula) if possible_cached_formula.file? NullLoader.new(ref) end |
.map_formula_name_to_local_bottle_path(formula_name, local_bottle_path) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
Map a formula name to a local/fetched bottle archive. This mapping will be used by factory
to allow formulae to be loaded automatically from their local bottle archive without
needing to exist in a tap or be passed as a complete path. For example,
to map hello
from its bottle archive:
Formulary.map_formula_name_to_local_bottle_path "hello", HOMEBREW_CACHE/"hello--2.10"
Formulary.factory "hello" # returns the hello formula from the local bottle archive
439 440 441 442 443 444 445 446 |
# File 'formulary.rb', line 439 def self.map_formula_name_to_local_bottle_path(formula_name, local_bottle_path) unless Homebrew::EnvConfig.install_from_api? raise UsageError, "HOMEBREW_INSTALL_FROM_API not set but required for #{__method__}!" end @formula_name_local_bottle_path_map ||= {} @formula_name_local_bottle_path_map[formula_name] = Pathname(local_bottle_path).realpath end |
.path(ref) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
531 532 533 |
# File 'formulary.rb', line 531 def self.path(ref) loader_for(ref).path end |
.resolve(name, spec: nil, force_bottle: false, flags: []) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'formulary.rb', line 114 def self.resolve(name, spec: nil, force_bottle: false, flags: []) if name.include?("/") || File.exist?(name) f = factory(name, *spec, force_bottle: force_bottle, flags: flags) if f.any_version_installed? tab = Tab.for_formula(f) resolved_spec = spec || tab.spec f.active_spec = resolved_spec if f.send(resolved_spec) f.build = tab if f.head? && tab.tabfile k = Keg.new(tab.tabfile.parent) f.version.update_commit(k.version.version.commit) if k.version.head? end end else rack = to_rack(name) alias_path = factory(name, force_bottle: force_bottle, flags: flags).alias_path f = from_rack(rack, *spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags) end # If this formula was installed with an alias that has since changed, # then it was specified explicitly in ARGV. (Using the alias would # instead have found the new formula.) # # Because of this, the user is referring to this specific formula, # not any formula targeted by the same alias, so in this context # the formula shouldn't be considered outdated if the alias used to # install it has changed. f.follow_installed_alias = false f end |
.tap_paths(name, taps = ) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
593 594 595 596 597 598 599 600 601 602 603 |
# File 'formulary.rb', line 593 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}", ]).find(&:file?) end.compact end |
.to_rack(ref) ⇒ Object
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'formulary.rb', line 510 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 |