2020-10-10 14:16:11 +02:00
|
|
|
# typed: false
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-06-02 20:51:16 -04:00
|
|
|
require "digest/md5"
|
2017-10-07 00:31:28 +02:00
|
|
|
require "extend/cachable"
|
2020-08-18 00:23:23 +01:00
|
|
|
require "tab"
|
2021-03-30 17:35:13 +01:00
|
|
|
require "utils/bottles"
|
2015-06-02 20:51:16 -04:00
|
|
|
|
2021-12-07 00:13:56 +00:00
|
|
|
require "active_support/core_ext/hash/deep_transform_values"
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# The {Formulary} is responsible for creating instances of {Formula}.
|
2016-05-22 21:29:22 +01:00
|
|
|
# It is not meant to be used directly from formulae.
|
2020-08-17 06:10:42 +02:00
|
|
|
#
|
|
|
|
# @api private
|
2017-02-20 13:06:23 +01:00
|
|
|
module Formulary
|
2020-10-20 12:03:48 +02:00
|
|
|
extend T::Sig
|
|
|
|
|
2017-10-07 00:31:28 +02:00
|
|
|
extend Cachable
|
2015-01-01 01:21:59 -05:00
|
|
|
|
2020-05-29 13:15:08 +01:00
|
|
|
URL_START_REGEX = %r{(https?|ftp|file)://}.freeze
|
|
|
|
|
2023-02-14 02:03:58 +00:00
|
|
|
# :codesign and custom requirement classes are not supported
|
|
|
|
API_SUPPORTED_REQUIREMENTS = [:arch, :linux, :macos, :maximum_macos, :xcode].freeze
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { void }
|
2019-11-06 10:03:44 +00:00
|
|
|
def self.enable_factory_cache!
|
|
|
|
@factory_cache = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.factory_cached?
|
|
|
|
!@factory_cache.nil?
|
|
|
|
end
|
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
def self.formula_class_defined_from_path?(path)
|
|
|
|
cache.key?(:path) && cache[:path].key?(path)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
def self.formula_class_defined_from_api?(name)
|
|
|
|
cache.key?(:api) && cache[:api].key?(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.formula_class_get_from_path(path)
|
|
|
|
cache[:path].fetch(path)
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.formula_class_get_from_api(name)
|
|
|
|
cache[:api].fetch(name)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2021-02-17 15:14:16 +00:00
|
|
|
def self.clear_cache
|
2022-06-14 14:49:00 -04:00
|
|
|
cache.each do |type, cached_objects|
|
|
|
|
next if type == :formulary_factory
|
2021-02-17 15:14:16 +00:00
|
|
|
|
2022-06-14 15:09:44 -04:00
|
|
|
cached_objects.each_value do |klass|
|
2022-06-14 14:49:00 -04:00
|
|
|
namespace = klass.name.deconstantize
|
|
|
|
next if namespace.deconstantize != name
|
2021-02-17 15:14:16 +00:00
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
remove_const(namespace.demodulize)
|
|
|
|
end
|
2021-02-17 15:14:16 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2021-05-03 13:24:32 +01:00
|
|
|
# @private
|
|
|
|
module PathnameWriteMkpath
|
|
|
|
refine Pathname do
|
|
|
|
def write(content, offset = nil, **open_args)
|
|
|
|
raise "Will not overwrite #{self}" if exist? && !offset && !open_args[:mode]&.match?(/^a\+?$/)
|
|
|
|
|
|
|
|
dirname.mkpath
|
|
|
|
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
using PathnameWriteMkpath
|
2021-04-13 17:40:41 +01:00
|
|
|
def self.load_formula(name, path, contents, namespace, flags:, ignore_errors:)
|
2020-04-05 15:44:50 +01:00
|
|
|
raise "Formula loading disabled by HOMEBREW_DISABLE_LOAD_FORMULA!" if Homebrew::EnvConfig.disable_load_formula?
|
2016-05-28 15:54:05 +01:00
|
|
|
|
2020-08-18 00:23:23 +01:00
|
|
|
require "formula"
|
2021-04-13 17:40:41 +01:00
|
|
|
require "ignorable"
|
2020-08-18 00:23:23 +01:00
|
|
|
|
2015-06-01 19:20:11 -04:00
|
|
|
mod = Module.new
|
2021-03-31 09:27:07 +01:00
|
|
|
remove_const(namespace) if const_defined?(namespace)
|
2015-09-02 16:12:26 +08:00
|
|
|
const_set(namespace, mod)
|
2020-07-26 07:24:14 +02:00
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
eval_formula = lambda do
|
2020-07-30 10:10:42 +02:00
|
|
|
# Set `BUILD_FLAGS` in the formula's namespace so we can
|
|
|
|
# access them from within the formula's class scope.
|
2020-07-26 07:24:14 +02:00
|
|
|
mod.const_set(:BUILD_FLAGS, flags)
|
2017-02-01 18:30:55 +00:00
|
|
|
mod.module_eval(contents, path)
|
2021-04-07 17:03:44 +01:00
|
|
|
rescue NameError, ArgumentError, ScriptError, MethodDeprecatedError, MacOSVersionError => e
|
2021-04-13 17:40:41 +01:00
|
|
|
if e.is_a?(Ignorable::ExceptionMixin)
|
|
|
|
e.ignore
|
|
|
|
else
|
|
|
|
remove_const(namespace)
|
|
|
|
raise FormulaUnreadableError.new(name, e)
|
|
|
|
end
|
2017-02-01 18:30:55 +00:00
|
|
|
end
|
2021-04-13 17:40:41 +01:00
|
|
|
if ignore_errors
|
|
|
|
Ignorable.hook_raise(&eval_formula)
|
|
|
|
else
|
|
|
|
eval_formula.call
|
|
|
|
end
|
|
|
|
|
2015-06-01 19:20:11 -04:00
|
|
|
class_name = class_s(name)
|
|
|
|
|
|
|
|
begin
|
2015-09-02 16:12:26 +08:00
|
|
|
mod.const_get(class_name)
|
2019-04-30 08:44:35 +01:00
|
|
|
rescue NameError => e
|
2016-09-17 15:17:27 +01:00
|
|
|
class_list = mod.constants
|
|
|
|
.map { |const_name| mod.const_get(const_name) }
|
|
|
|
.select { |const| const.is_a?(Class) }
|
2019-04-30 08:44:35 +01:00
|
|
|
new_exception = FormulaClassUnavailableError.new(name, path, class_name, class_list)
|
2021-02-17 17:38:16 +00:00
|
|
|
remove_const(namespace)
|
2019-04-30 08:44:35 +01:00
|
|
|
raise new_exception, "", e.backtrace
|
2015-06-01 19:20:11 -04:00
|
|
|
end
|
2013-12-09 21:10:32 -06:00
|
|
|
end
|
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def self.load_formula_from_path(name, path, flags:, ignore_errors:)
|
2016-09-24 17:59:14 +02:00
|
|
|
contents = path.open("r") { |f| ensure_utf8_encoding(f).read }
|
2015-09-02 16:12:26 +08:00
|
|
|
namespace = "FormulaNamespace#{Digest::MD5.hexdigest(path.to_s)}"
|
2021-04-13 17:40:41 +01:00
|
|
|
klass = load_formula(name, path, contents, namespace, flags: flags, ignore_errors: ignore_errors)
|
2022-06-14 14:49:00 -04:00
|
|
|
cache[:path] ||= {}
|
|
|
|
cache[:path][path] = klass
|
|
|
|
end
|
|
|
|
|
2022-06-15 16:57:15 -04:00
|
|
|
def self.load_formula_from_api(name, flags:)
|
2022-06-14 14:49:00 -04:00
|
|
|
namespace = "FormulaNamespaceAPI#{Digest::MD5.hexdigest(name)}"
|
|
|
|
|
|
|
|
mod = Module.new
|
|
|
|
remove_const(namespace) if const_defined?(namespace)
|
|
|
|
const_set(namespace, mod)
|
|
|
|
|
|
|
|
mod.const_set(:BUILD_FLAGS, flags)
|
|
|
|
|
|
|
|
class_s = Formulary.class_s(name)
|
|
|
|
json_formula = Homebrew::API::Formula.all_formulae[name]
|
2023-02-10 12:07:36 -05:00
|
|
|
json_formula = Homebrew::API.merge_variations(json_formula)
|
2022-07-24 22:59:42 +02:00
|
|
|
|
2022-07-25 08:49:19 +02:00
|
|
|
uses_from_macos_names = json_formula["uses_from_macos"].map do |dep|
|
|
|
|
next dep unless dep.is_a? Hash
|
|
|
|
|
|
|
|
dep.keys.first
|
|
|
|
end
|
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
klass = Class.new(::Formula) do
|
|
|
|
desc json_formula["desc"]
|
|
|
|
homepage json_formula["homepage"]
|
2023-02-06 09:57:25 +00:00
|
|
|
license SPDX.string_to_license_expression(json_formula["license"])
|
2022-06-14 14:49:00 -04:00
|
|
|
revision json_formula["revision"]
|
|
|
|
version_scheme json_formula["version_scheme"]
|
|
|
|
|
|
|
|
if (urls_stable = json_formula["urls"]["stable"]).present?
|
|
|
|
stable do
|
|
|
|
url urls_stable["url"]
|
|
|
|
version json_formula["versions"]["stable"]
|
2022-10-21 01:22:36 -04:00
|
|
|
sha256 urls_stable["checksum"] if urls_stable["checksum"].present?
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-02-06 13:04:16 +00:00
|
|
|
if (urls_head = json_formula["urls"]["head"]).present?
|
|
|
|
head urls_head["url"], branch: urls_head["branch"]
|
|
|
|
end
|
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
if (bottles_stable = json_formula["bottle"]["stable"]).present?
|
|
|
|
bottle do
|
2023-02-07 12:12:00 +00:00
|
|
|
if Homebrew::EnvConfig.bottle_domain != HOMEBREW_BOTTLE_DEFAULT_DOMAIN \
|
|
|
|
&& bottles_stable["root_url"] == HOMEBREW_BOTTLE_DEFAULT_DOMAIN
|
|
|
|
root_url Homebrew::EnvConfig.bottle_domain
|
|
|
|
else
|
|
|
|
root_url bottles_stable["root_url"]
|
|
|
|
end
|
2022-06-14 14:49:00 -04:00
|
|
|
rebuild bottles_stable["rebuild"]
|
|
|
|
bottles_stable["files"].each do |tag, bottle_spec|
|
2022-06-16 13:26:51 -04:00
|
|
|
cellar = Formulary.convert_to_string_or_symbol bottle_spec["cellar"]
|
2022-06-14 14:49:00 -04:00
|
|
|
sha256 cellar: cellar, tag.to_sym => bottle_spec["sha256"]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if (keg_only_reason = json_formula["keg_only_reason"]).present?
|
2022-06-16 13:26:51 -04:00
|
|
|
reason = Formulary.convert_to_string_or_symbol keg_only_reason["reason"]
|
2022-06-14 14:49:00 -04:00
|
|
|
keg_only reason, keg_only_reason["explanation"]
|
|
|
|
end
|
|
|
|
|
|
|
|
if (deprecation_date = json_formula["deprecation_date"]).present?
|
2022-06-16 16:14:39 -04:00
|
|
|
reason = Formulary.convert_to_deprecate_disable_reason_string_or_symbol json_formula["deprecation_reason"]
|
|
|
|
deprecate! date: deprecation_date, because: reason
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
if (disable_date = json_formula["disable_date"]).present?
|
2022-06-16 16:14:39 -04:00
|
|
|
reason = Formulary.convert_to_deprecate_disable_reason_string_or_symbol json_formula["disable_reason"]
|
|
|
|
disable! date: disable_date, because: reason
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
json_formula["dependencies"].each do |dep|
|
2023-02-14 04:07:36 +00:00
|
|
|
next if uses_from_macos_names.include?(dep) && !Homebrew::SimulateSystem.simulating_or_running_on_macos?
|
2022-07-25 08:49:19 +02:00
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
depends_on dep
|
|
|
|
end
|
|
|
|
|
2022-09-21 02:42:14 -04:00
|
|
|
[:build, :test, :recommended, :optional].each do |type|
|
2022-07-25 08:49:19 +02:00
|
|
|
json_formula["#{type}_dependencies"].each do |dep|
|
2023-02-14 04:07:36 +00:00
|
|
|
next if uses_from_macos_names.include?(dep) && !Homebrew::SimulateSystem.simulating_or_running_on_macos?
|
2022-06-14 14:49:00 -04:00
|
|
|
|
2022-07-25 08:49:19 +02:00
|
|
|
depends_on dep => type
|
|
|
|
end
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
json_formula["uses_from_macos"].each do |dep|
|
|
|
|
dep = dep.deep_transform_values(&:to_sym) if dep.is_a?(Hash)
|
|
|
|
uses_from_macos dep
|
|
|
|
end
|
|
|
|
|
2023-02-14 02:03:58 +00:00
|
|
|
json_formula["requirements"].each do |req|
|
|
|
|
req_name = req["name"].to_sym
|
|
|
|
next if API_SUPPORTED_REQUIREMENTS.exclude?(req_name)
|
|
|
|
|
|
|
|
req_version = case req_name
|
|
|
|
when :arch
|
|
|
|
req["version"]&.to_sym
|
|
|
|
when :macos, :maximum_macos
|
|
|
|
MacOSVersions::SYMBOLS.key(req["version"])
|
|
|
|
else
|
|
|
|
req["version"]
|
|
|
|
end
|
|
|
|
|
|
|
|
req_tags = []
|
|
|
|
req_tags << req_version if req_version.present?
|
|
|
|
req_tags += req["contexts"].map do |tag|
|
|
|
|
case tag
|
|
|
|
when String
|
|
|
|
tag.to_sym
|
|
|
|
when Hash
|
|
|
|
tag.deep_transform_keys(&:to_sym)
|
|
|
|
else
|
|
|
|
tag
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
depends_on req_name => req_tags
|
|
|
|
end
|
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
def install
|
|
|
|
raise "Cannot build from source from abstract formula."
|
|
|
|
end
|
|
|
|
|
|
|
|
@caveats_string = json_formula["caveats"]
|
|
|
|
def caveats
|
2022-06-16 19:36:32 -04:00
|
|
|
self.class.instance_variable_get(:@caveats_string)
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
2023-02-06 13:06:11 +00:00
|
|
|
|
|
|
|
@tap_git_head_string = json_formula["tap_git_head"]
|
|
|
|
def tap_git_head
|
|
|
|
self.class.instance_variable_get(:@tap_git_head_string)
|
|
|
|
end
|
2023-02-14 02:03:58 +00:00
|
|
|
|
|
|
|
@oldname_string = json_formula["oldname"]
|
|
|
|
def oldname
|
|
|
|
self.class.instance_variable_get(:@oldname_string)
|
|
|
|
end
|
|
|
|
|
|
|
|
@aliases_array = json_formula["aliases"]
|
|
|
|
def aliases
|
|
|
|
self.class.instance_variable_get(:@aliases_array)
|
|
|
|
end
|
|
|
|
|
|
|
|
@versioned_formulae_array = json_formula["versioned_formulae"]
|
|
|
|
def versioned_formulae_names
|
|
|
|
self.class.instance_variable_get(:@versioned_formulae_array)
|
|
|
|
end
|
2022-06-14 14:49:00 -04:00
|
|
|
end
|
|
|
|
|
2022-09-10 20:28:21 -04:00
|
|
|
klass.loaded_from_api = true
|
2022-06-14 14:49:00 -04:00
|
|
|
mod.const_set(class_s, klass)
|
|
|
|
|
|
|
|
cache[:api] ||= {}
|
|
|
|
cache[:api][name] = klass
|
2015-09-02 16:12:26 +08:00
|
|
|
end
|
|
|
|
|
2020-07-26 07:24:14 +02:00
|
|
|
def self.resolve(name, spec: nil, force_bottle: false, flags: [])
|
2018-09-11 17:44:18 +02:00
|
|
|
if name.include?("/") || File.exist?(name)
|
2020-07-26 07:24:14 +02:00
|
|
|
f = factory(name, *spec, force_bottle: force_bottle, flags: flags)
|
2018-09-11 17:44:18 +02:00
|
|
|
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)
|
2020-07-26 07:24:14 +02:00
|
|
|
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)
|
2018-09-11 17:44:18 +02:00
|
|
|
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,
|
2019-08-19 14:27:29 +10:00
|
|
|
# not any formula targeted by the same alias, so in this context
|
2018-09-11 17:44:18 +02:00
|
|
|
# the formula shouldn't be considered outdated if the alias used to
|
|
|
|
# install it has changed.
|
|
|
|
f.follow_installed_alias = false
|
|
|
|
|
|
|
|
f
|
|
|
|
end
|
|
|
|
|
2018-07-09 15:29:40 +01:00
|
|
|
def self.ensure_utf8_encoding(io)
|
|
|
|
io.set_encoding(Encoding::UTF_8)
|
2015-06-06 18:10:47 -04:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def self.class_s(name)
|
2014-04-03 22:40:40 -05:00
|
|
|
class_name = name.capitalize
|
2017-06-10 20:23:20 +03:00
|
|
|
class_name.gsub!(/[-_.\s]([a-zA-Z0-9])/) { Regexp.last_match(1).upcase }
|
2015-08-06 15:45:52 +08:00
|
|
|
class_name.tr!("+", "x")
|
2016-08-29 16:00:40 +01:00
|
|
|
class_name.sub!(/(.)@(\d)/, "\\1AT\\2")
|
2014-04-03 22:40:40 -05:00
|
|
|
class_name
|
2014-02-21 00:43:58 -05:00
|
|
|
end
|
|
|
|
|
2022-06-16 13:26:51 -04:00
|
|
|
def self.convert_to_string_or_symbol(string)
|
|
|
|
return string[1..].to_sym if string.start_with?(":")
|
|
|
|
|
|
|
|
string
|
|
|
|
end
|
|
|
|
|
2022-06-16 16:14:39 -04:00
|
|
|
def self.convert_to_deprecate_disable_reason_string_or_symbol(string)
|
2022-07-19 06:29:14 -06:00
|
|
|
require "deprecate_disable"
|
2022-06-16 16:14:39 -04:00
|
|
|
return string unless DeprecateDisable::DEPRECATE_DISABLE_REASONS.keys.map(&:to_s).include?(string)
|
|
|
|
|
|
|
|
string.to_sym
|
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# A {FormulaLoader} returns instances of formulae.
|
2013-06-18 10:11:06 -07:00
|
|
|
# Subclasses implement loaders for particular sources of formulae.
|
|
|
|
class FormulaLoader
|
2020-08-02 14:32:31 +02:00
|
|
|
include Context
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
# The formula's name
|
|
|
|
attr_reader :name
|
|
|
|
# The formula's ruby file's path or filename
|
|
|
|
attr_reader :path
|
2016-09-03 21:10:44 +01:00
|
|
|
# The name used to install the formula
|
2016-09-05 01:11:36 +01:00
|
|
|
attr_reader :alias_path
|
2014-04-03 22:40:40 -05:00
|
|
|
|
|
|
|
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
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
|
|
|
|
# Gets the formula instance.
|
2016-09-15 16:01:18 +01:00
|
|
|
# `alias_path` can be overridden here in case an alias was used to refer to
|
|
|
|
# a formula that was loaded in another way.
|
2021-04-13 17:40:41 +01:00
|
|
|
def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
|
2020-07-30 03:24:37 +02:00
|
|
|
alias_path ||= self.alias_path
|
2021-04-13 17:40:41 +01:00
|
|
|
klass(flags: flags, ignore_errors: ignore_errors)
|
|
|
|
.new(name, path, spec, alias_path: alias_path, force_bottle: force_bottle)
|
2014-03-07 17:23:44 -06:00
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def klass(flags:, ignore_errors:)
|
2022-06-14 14:49:00 -04:00
|
|
|
load_file(flags: flags, ignore_errors: ignore_errors) unless Formulary.formula_class_defined_from_path?(path)
|
|
|
|
Formulary.formula_class_get_from_path(path)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2014-12-29 14:53:22 -05:00
|
|
|
|
|
|
|
private
|
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def load_file(flags:, ignore_errors:)
|
2020-08-02 14:32:31 +02:00
|
|
|
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
2016-09-17 15:17:27 +01:00
|
|
|
raise FormulaUnavailableError, name unless path.file?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
Formulary.load_formula_from_path(name, path, flags: flags, ignore_errors: ignore_errors)
|
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
|
|
|
|
2020-08-17 06:10:42 +02:00
|
|
|
# Loads a formula from a bottle.
|
2013-06-18 10:11:06 -07:00
|
|
|
class BottleLoader < FormulaLoader
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(bottle_name)
|
2016-07-13 13:51:44 -05:00
|
|
|
case bottle_name
|
2020-05-29 13:15:08 +01:00
|
|
|
when URL_START_REGEX
|
2016-07-13 13:51:44 -05:00
|
|
|
# The name of the formula is found between the last slash and the last hyphen.
|
2017-06-09 11:44:25 -07:00
|
|
|
formula_name = File.basename(bottle_name)[/(.+)-/, 1]
|
|
|
|
resource = Resource.new(formula_name) { url bottle_name }
|
2018-03-24 10:53:49 +00:00
|
|
|
resource.specs[:bottle] = true
|
2018-08-01 05:33:03 +02:00
|
|
|
downloader = resource.downloader
|
2018-07-05 11:53:29 +02:00
|
|
|
cached = downloader.cached_location.exist?
|
2016-07-13 13:51:44 -05:00
|
|
|
downloader.fetch
|
|
|
|
ohai "Pouring the cached bottle" if cached
|
2018-07-05 11:53:29 +02:00
|
|
|
@bottle_filename = downloader.cached_location
|
2016-07-13 13:51:44 -05:00
|
|
|
else
|
|
|
|
@bottle_filename = Pathname(bottle_name).realpath
|
|
|
|
end
|
2016-04-25 17:57:51 +01:00
|
|
|
name, full_name = Utils::Bottles.resolve_formula_names @bottle_filename
|
2015-05-27 22:49:18 +08:00
|
|
|
super name, Formulary.path(full_name)
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def get_formula(spec, force_bottle: false, flags: [], ignore_errors: false, **)
|
2017-10-04 10:14:06 +01:00
|
|
|
formula = begin
|
2021-04-01 16:52:58 +01:00
|
|
|
contents = Utils::Bottles.formula_contents @bottle_filename, name: name
|
2021-04-13 17:40:41 +01:00
|
|
|
Formulary.from_contents(name, path, contents, spec, force_bottle: force_bottle,
|
|
|
|
flags: flags, ignore_errors: ignore_errors)
|
2017-10-04 10:14:06 +01:00
|
|
|
rescue FormulaUnreadableError => e
|
2017-10-15 02:28:32 +02:00
|
|
|
opoo <<~EOS
|
2017-10-04 10:14:06 +01:00
|
|
|
Unreadable formula in #{@bottle_filename}:
|
|
|
|
#{e}
|
|
|
|
EOS
|
|
|
|
super
|
2021-04-01 16:52:58 +01:00
|
|
|
rescue BottleFormulaUnavailableError => e
|
|
|
|
opoo <<~EOS
|
|
|
|
#{e}
|
|
|
|
Falling back to non-bottle formula.
|
|
|
|
EOS
|
|
|
|
super
|
2017-10-04 10:14:06 +01:00
|
|
|
end
|
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
|
|
|
|
2020-08-17 06:10:42 +02:00
|
|
|
# Loads a formula from a path to an alias.
|
2014-04-05 22:03:34 -05:00
|
|
|
class AliasLoader < FormulaLoader
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(alias_path)
|
2014-04-05 22:03:34 -05:00
|
|
|
path = alias_path.resolved_path
|
|
|
|
name = path.basename(".rb").to_s
|
|
|
|
super name, path
|
2016-09-15 16:01:18 +01:00
|
|
|
@alias_path = alias_path.to_s
|
2014-04-05 22:03:34 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Loads formulae from disk using a path.
|
2013-06-18 10:11:06 -07:00
|
|
|
class FromPathLoader < FormulaLoader
|
2015-08-03 13:09:07 +01:00
|
|
|
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
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Loads formulae from URLs.
|
2013-06-18 10:11:06 -07:00
|
|
|
class FromUrlLoader < FormulaLoader
|
2020-10-20 12:03:48 +02:00
|
|
|
extend T::Sig
|
|
|
|
|
2013-06-18 10:11:06 -07:00
|
|
|
attr_reader :url
|
|
|
|
|
2023-02-06 13:06:11 +00:00
|
|
|
sig { params(url: T.any(URI::Generic, String), from: T.nilable(Symbol)).void }
|
|
|
|
def initialize(url, from: nil)
|
2013-06-18 10:11:06 -07:00
|
|
|
@url = url
|
2023-02-06 13:06:11 +00:00
|
|
|
@from = from
|
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
|
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def load_file(flags:, ignore_errors:)
|
2023-02-06 13:06:11 +00:00
|
|
|
if @from != :formula_installer
|
|
|
|
if %r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(?:/Formula)?/(?<formula_name>[\w+-.@]+).rb} =~ url
|
|
|
|
raise UnsupportedInstallationMethod,
|
|
|
|
"Installation of #{formula_name} from a GitHub commit URL is unsupported! " \
|
|
|
|
"`brew extract #{formula_name}` to a stable tap on GitHub instead."
|
|
|
|
elsif url.match?(%r{^(https?|ftp)://})
|
|
|
|
raise UnsupportedInstallationMethod,
|
|
|
|
"Non-checksummed download of #{name} formula file from an arbitrary URL is unsupported! " \
|
|
|
|
"`brew extract` or `brew create` and `brew tap-new` to create a formula file in a tap " \
|
|
|
|
"on GitHub instead."
|
|
|
|
end
|
2019-05-07 10:50:19 +01:00
|
|
|
end
|
2014-12-29 14:53:22 -05:00
|
|
|
HOMEBREW_CACHE_FORMULA.mkpath
|
|
|
|
FileUtils.rm_f(path)
|
2017-08-08 18:10:13 +02:00
|
|
|
curl_download url, to: path
|
2014-03-07 17:23:44 -06:00
|
|
|
super
|
2016-12-10 13:04:14 +00:00
|
|
|
rescue MethodDeprecatedError => e
|
2020-10-09 11:12:06 +02:00
|
|
|
if %r{github.com/(?<user>[\w-]+)/(?<repo>[\w-]+)/} =~ url
|
|
|
|
e.issues_url = "https://github.com/#{user}/#{repo}/issues/new"
|
2016-12-10 13:04:14 +00:00
|
|
|
end
|
|
|
|
raise
|
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
|
2015-07-14 21:33:29 +08:00
|
|
|
attr_reader :tap
|
2014-04-05 22:03:33 -05:00
|
|
|
|
2016-11-11 16:42:29 +00:00
|
|
|
def initialize(tapped_name, from: nil)
|
2020-12-01 17:04:59 +00:00
|
|
|
warn = [:keg, :rack].exclude?(from)
|
2016-11-11 16:42:29 +00:00
|
|
|
name, path = formula_name_path(tapped_name, warn: warn)
|
2016-11-02 14:47:24 -04:00
|
|
|
super name, path
|
|
|
|
end
|
|
|
|
|
|
|
|
def formula_name_path(tapped_name, warn: true)
|
2014-04-05 22:03:33 -05:00
|
|
|
user, repo, name = tapped_name.split("/", 3).map(&:downcase)
|
2015-12-02 14:35:42 +08:00
|
|
|
@tap = Tap.fetch user, repo
|
2016-02-24 20:06:33 +08:00
|
|
|
formula_dir = @tap.formula_dir || @tap.path
|
|
|
|
path = formula_dir/"#{name}.rb"
|
2015-09-12 18:04:06 +08:00
|
|
|
|
2016-02-24 20:06:33 +08:00
|
|
|
unless path.file?
|
2015-12-06 21:44:18 +08:00
|
|
|
if (possible_alias = @tap.alias_dir/name).file?
|
2015-09-12 18:04:06 +08:00
|
|
|
path = possible_alias.resolved_path
|
|
|
|
name = path.basename(".rb").to_s
|
2016-02-24 20:06:33 +08:00
|
|
|
elsif (new_name = @tap.formula_renames[name]) &&
|
|
|
|
(new_path = formula_dir/"#{new_name}.rb").file?
|
2016-11-02 14:47:24 -04:00
|
|
|
old_name = name
|
2016-02-24 20:06:33 +08:00
|
|
|
path = new_path
|
|
|
|
name = new_name
|
2016-11-02 14:47:24 -04:00
|
|
|
new_name = @tap.core_tap? ? name : "#{@tap}/#{name}"
|
|
|
|
elsif (new_tap_name = @tap.tap_migrations[name])
|
2016-12-18 13:18:46 -08:00
|
|
|
new_tap_user, new_tap_repo, = new_tap_name.split("/")
|
|
|
|
new_tap_name = "#{new_tap_user}/#{new_tap_repo}"
|
2016-11-02 14:47:24 -04:00
|
|
|
new_tap = Tap.fetch new_tap_name
|
|
|
|
new_tap.install unless new_tap.installed?
|
|
|
|
new_tapped_name = "#{new_tap_name}/#{name}"
|
|
|
|
name, path = formula_name_path(new_tapped_name, warn: false)
|
|
|
|
old_name = tapped_name
|
|
|
|
new_name = new_tap.core_tap? ? name : new_tapped_name
|
|
|
|
end
|
|
|
|
|
2019-02-19 13:11:32 +00:00
|
|
|
opoo "Use #{new_name} instead of deprecated #{old_name}" if warn && old_name && new_name
|
2015-09-12 18:04:06 +08:00
|
|
|
end
|
2014-04-07 18:44:29 -05:00
|
|
|
|
2016-11-02 14:47:24 -04:00
|
|
|
[name, path]
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def get_formula(spec, alias_path: nil, force_bottle: false, flags: [], ignore_errors: false)
|
2014-03-07 17:23:44 -06:00
|
|
|
super
|
2017-04-18 08:43:39 +01:00
|
|
|
rescue FormulaUnreadableError => e
|
|
|
|
raise TapFormulaUnreadableError.new(tap, name, e.formula_error), "", e.backtrace
|
|
|
|
rescue FormulaClassUnavailableError => e
|
|
|
|
raise TapFormulaClassUnavailableError.new(tap, name, e.path, e.class_name, e.class_list), "", e.backtrace
|
2014-12-29 15:23:01 -05:00
|
|
|
rescue FormulaUnavailableError => e
|
2015-07-14 21:33:29 +08:00
|
|
|
raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
2016-12-10 13:04:14 +00:00
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def load_file(flags:, ignore_errors:)
|
2016-12-10 13:04:14 +00:00
|
|
|
super
|
|
|
|
rescue MethodDeprecatedError => e
|
2016-12-14 05:07:54 +03:00
|
|
|
e.issues_url = tap.issues_url || tap.to_s
|
2016-12-10 13:04:14 +00:00
|
|
|
raise
|
|
|
|
end
|
2013-06-18 10:11:06 -07:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Pseudo-loader which will raise a {FormulaUnavailableError} when trying to load the corresponding formula.
|
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
|
|
|
|
|
2016-09-19 16:28:28 +01:00
|
|
|
def get_formula(*)
|
2016-09-17 15:17:27 +01:00
|
|
|
raise FormulaUnavailableError, name
|
2015-01-01 01:21:59 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Load formulae directly from their contents.
|
2015-09-02 16:25:46 +08:00
|
|
|
class FormulaContentsLoader < FormulaLoader
|
2020-11-05 17:17:03 -05:00
|
|
|
# The formula's contents.
|
2015-09-02 16:25:46 +08:00
|
|
|
attr_reader :contents
|
|
|
|
|
|
|
|
def initialize(name, path, contents)
|
|
|
|
@contents = contents
|
|
|
|
super name, path
|
|
|
|
end
|
|
|
|
|
2021-04-13 17:40:41 +01:00
|
|
|
def klass(flags:, ignore_errors:)
|
2020-08-02 14:32:31 +02:00
|
|
|
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if debug?
|
2020-07-26 07:24:14 +02:00
|
|
|
namespace = "FormulaNamespace#{Digest::MD5.hexdigest(contents.to_s)}"
|
2021-04-13 17:40:41 +01:00
|
|
|
Formulary.load_formula(name, path, contents, namespace, flags: flags, ignore_errors: ignore_errors)
|
2015-09-02 16:25:46 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-12-07 00:13:56 +00:00
|
|
|
# Load formulae from the API.
|
|
|
|
class FormulaAPILoader < FormulaLoader
|
|
|
|
def initialize(name)
|
|
|
|
super name, Formulary.core_path(name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def klass(flags:, ignore_errors:)
|
2022-06-15 16:57:15 -04:00
|
|
|
load_from_api(flags: flags) unless Formulary.formula_class_defined_from_api?(name)
|
2022-06-14 14:49:00 -04:00
|
|
|
Formulary.formula_class_get_from_api(name)
|
|
|
|
end
|
2021-12-07 00:13:56 +00:00
|
|
|
|
2022-06-14 14:49:00 -04:00
|
|
|
private
|
2021-12-07 00:13:56 +00:00
|
|
|
|
2022-06-15 16:57:15 -04:00
|
|
|
def load_from_api(flags:)
|
2022-06-14 14:49:00 -04:00
|
|
|
$stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{name} from API" if debug?
|
2021-12-07 00:13:56 +00:00
|
|
|
|
2022-06-15 16:57:15 -04:00
|
|
|
Formulary.load_formula_from_api(name, flags: flags)
|
2021-12-07 00:13:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-09-14 23:54:14 -04:00
|
|
|
# Load aliases from the API.
|
|
|
|
class AliasAPILoader < FormulaAPILoader
|
|
|
|
def initialize(alias_name)
|
|
|
|
super Homebrew::API::Formula.all_aliases[alias_name]
|
|
|
|
@alias_path = Formulary.core_alias_path(alias_name).to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Return a {Formula} instance for the given reference.
|
2018-10-18 21:42:43 -04:00
|
|
|
# `ref` is a string containing:
|
|
|
|
#
|
2013-06-23 12:47:10 -07:00
|
|
|
# * a formula name
|
|
|
|
# * a formula pathname
|
|
|
|
# * a formula URL
|
|
|
|
# * a local bottle reference
|
2021-04-13 17:40:41 +01:00
|
|
|
def self.factory(
|
|
|
|
ref, spec = :stable, alias_path: nil, from: nil,
|
|
|
|
force_bottle: false, flags: [], ignore_errors: false
|
|
|
|
)
|
2018-06-01 19:22:33 +01:00
|
|
|
raise ArgumentError, "Formulae must have a ref!" unless ref
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2019-11-06 10:03:44 +00:00
|
|
|
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
|
|
|
|
|
2020-07-26 07:24:14 +02:00
|
|
|
formula = loader_for(ref, from: from).get_formula(spec, alias_path: alias_path,
|
2021-04-13 17:40:41 +01:00
|
|
|
force_bottle: force_bottle, flags: flags,
|
|
|
|
ignore_errors: ignore_errors)
|
2019-11-06 10:03:44 +00:00
|
|
|
if factory_cached?
|
|
|
|
cache[:formulary_factory] ||= {}
|
|
|
|
cache[:formulary_factory][cache_key] ||= formula
|
|
|
|
end
|
|
|
|
formula
|
2014-04-05 22:03:33 -05:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Return a {Formula} instance for the given rack.
|
2016-09-15 16:01:18 +01:00
|
|
|
#
|
2020-11-05 15:19:56 -05:00
|
|
|
# @param spec when nil, will auto resolve the formula's spec.
|
|
|
|
# @param :alias_path 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.
|
2020-07-26 07:24:14 +02:00
|
|
|
def self.from_rack(rack, spec = nil, alias_path: nil, force_bottle: false, flags: [])
|
2015-05-16 11:26:26 +08:00
|
|
|
kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : []
|
2018-09-02 20:14:54 +01:00
|
|
|
keg = kegs.find(&:linked?) || kegs.find(&:optlinked?) || kegs.max_by(&:version)
|
2015-05-16 11:26:26 +08:00
|
|
|
|
2016-07-15 17:45:21 +08:00
|
|
|
if keg
|
2021-10-26 15:02:29 +01:00
|
|
|
from_keg(keg, spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags)
|
2016-07-15 17:45:21 +08:00
|
|
|
else
|
2020-07-26 07:24:14 +02:00
|
|
|
factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack,
|
|
|
|
force_bottle: force_bottle, flags: flags)
|
2016-07-15 17:45:21 +08:00
|
|
|
end
|
|
|
|
end
|
2018-06-05 23:19:18 -04:00
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Return whether given rack is keg-only.
|
2018-06-05 23:19:18 -04:00
|
|
|
def self.keg_only?(rack)
|
|
|
|
Formulary.from_rack(rack).keg_only?
|
|
|
|
rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError
|
|
|
|
false
|
|
|
|
end
|
2016-07-15 17:45:21 +08:00
|
|
|
|
2020-11-05 15:19:56 -05:00
|
|
|
# Return a {Formula} instance for the given keg.
|
|
|
|
#
|
|
|
|
# @param spec when nil, will auto resolve the formula's spec.
|
2020-07-26 07:24:14 +02:00
|
|
|
def self.from_keg(keg, spec = nil, alias_path: nil, force_bottle: false, flags: [])
|
2015-07-30 16:33:19 +08:00
|
|
|
tab = Tab.for_keg(keg)
|
|
|
|
tap = tab.tap
|
|
|
|
spec ||= tab.spec
|
2015-05-16 11:26:26 +08:00
|
|
|
|
2016-07-15 15:03:58 +08:00
|
|
|
f = if tap.nil?
|
2020-07-26 07:24:14 +02:00
|
|
|
factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg,
|
|
|
|
force_bottle: force_bottle, flags: flags)
|
2015-05-16 11:26:26 +08:00
|
|
|
else
|
2015-10-09 15:27:59 +08:00
|
|
|
begin
|
2020-07-26 07:24:14 +02:00
|
|
|
factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg,
|
|
|
|
force_bottle: force_bottle, flags: flags)
|
2015-10-09 15:27:59 +08:00
|
|
|
rescue FormulaUnavailableError
|
|
|
|
# formula may be migrated to different tap. Try to search in core and all taps.
|
2020-07-26 07:24:14 +02:00
|
|
|
factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg,
|
|
|
|
force_bottle: force_bottle, flags: flags)
|
2015-10-09 15:27:59 +08:00
|
|
|
end
|
2015-05-16 11:26:26 +08:00
|
|
|
end
|
2016-07-15 15:03:58 +08:00
|
|
|
f.build = tab
|
2016-12-09 17:59:08 +00:00
|
|
|
f.build.used_options = Tab.remap_deprecated_options(f.deprecated_options, tab.used_options).as_flags
|
2016-08-06 11:22:12 +03:00
|
|
|
f.version.update_commit(keg.version.version.commit) if f.head? && keg.version.head?
|
2016-07-15 15:03:58 +08:00
|
|
|
f
|
2015-05-16 11:26:26 +08:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Return a {Formula} instance directly from contents.
|
2021-04-13 17:40:41 +01:00
|
|
|
def self.from_contents(
|
|
|
|
name, path, contents, spec = :stable, alias_path: nil,
|
|
|
|
force_bottle: false, flags: [], ignore_errors: false
|
|
|
|
)
|
2020-07-26 07:24:14 +02:00
|
|
|
FormulaContentsLoader.new(name, path, contents)
|
2021-04-13 17:40:41 +01:00
|
|
|
.get_formula(spec, alias_path: alias_path, force_bottle: force_bottle,
|
|
|
|
flags: flags, ignore_errors: ignore_errors)
|
2015-09-02 16:25:46 +08:00
|
|
|
end
|
|
|
|
|
2015-08-09 14:56:01 +03:00
|
|
|
def self.to_rack(ref)
|
2016-08-10 08:48:30 +01:00
|
|
|
# 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.
|
2015-08-10 21:35:59 +08:00
|
|
|
if (rack = HOMEBREW_CELLAR/File.basename(ref, ".rb")).directory?
|
2015-08-15 16:12:42 +08:00
|
|
|
return rack.resolved_path
|
2015-08-09 14:56:01 +03:00
|
|
|
end
|
|
|
|
|
2016-08-10 08:48:30 +01:00
|
|
|
# Use canonical name to locate rack.
|
2015-08-15 16:12:42 +08:00
|
|
|
(HOMEBREW_CELLAR/canonical_name(ref)).resolved_path
|
2015-08-09 14:56:01 +03:00
|
|
|
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
|
|
|
|
|
2016-11-11 16:42:29 +00:00
|
|
|
def self.loader_for(ref, from: nil)
|
2014-04-05 22:03:33 -05:00
|
|
|
case ref
|
2021-03-30 17:35:13 +01:00
|
|
|
when HOMEBREW_BOTTLES_EXTNAME_REGEX
|
2014-04-05 22:03:33 -05:00
|
|
|
return BottleLoader.new(ref)
|
2020-05-29 13:15:08 +01:00
|
|
|
when URL_START_REGEX
|
2023-02-06 13:06:11 +00:00
|
|
|
return FromUrlLoader.new(ref, from: from)
|
2014-04-05 22:03:33 -05:00
|
|
|
when HOMEBREW_TAP_FORMULA_REGEX
|
2022-06-17 13:43:52 -04:00
|
|
|
if ref.start_with?("homebrew/core/") && Homebrew::EnvConfig.install_from_api?
|
2021-10-30 23:54:01 -04:00
|
|
|
name = ref.split("/", 3).last
|
2021-12-07 00:13:56 +00:00
|
|
|
return FormulaAPILoader.new(name) if Homebrew::API::Formula.all_formulae.key?(name)
|
2022-09-14 23:54:14 -04:00
|
|
|
return AliasAPILoader.new(name) if Homebrew::API::Formula.all_aliases.key?(name)
|
2021-10-30 23:54:01 -04:00
|
|
|
end
|
|
|
|
|
2016-11-11 16:42:29 +00:00
|
|
|
return TapLoader.new(ref, from: from)
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|
|
|
|
|
2022-09-19 02:34:54 +01:00
|
|
|
pathname_ref = Pathname.new(ref)
|
|
|
|
return FromPathLoader.new(ref) if File.extname(ref) == ".rb" && pathname_ref.expand_path.exist?
|
2014-04-05 22:03:33 -05:00
|
|
|
|
2022-09-14 23:54:14 -04:00
|
|
|
if Homebrew::EnvConfig.install_from_api?
|
|
|
|
return FormulaAPILoader.new(ref) if Homebrew::API::Formula.all_formulae.key?(ref)
|
|
|
|
return AliasAPILoader.new(ref) if Homebrew::API::Formula.all_aliases.key?(ref)
|
2022-06-17 13:43:52 -04:00
|
|
|
end
|
|
|
|
|
2015-05-08 18:59:08 +08:00
|
|
|
formula_with_that_name = core_path(ref)
|
2019-02-19 13:11:32 +00:00
|
|
|
return FormulaLoader.new(ref, formula_with_that_name) if formula_with_that_name.file?
|
2014-04-05 22:03:33 -05:00
|
|
|
|
2022-09-19 02:34:54 +01:00
|
|
|
possible_alias = if pathname_ref.absolute?
|
|
|
|
pathname_ref
|
|
|
|
else
|
|
|
|
core_alias_path(ref)
|
|
|
|
end
|
|
|
|
return AliasLoader.new(possible_alias) if possible_alias.symlink?
|
2014-04-05 22:03:33 -05:00
|
|
|
|
2015-08-22 13:15:33 +08:00
|
|
|
possible_tap_formulae = tap_paths(ref)
|
2019-02-19 13:11:32 +00:00
|
|
|
raise TapFormulaAmbiguityError.new(ref, possible_tap_formulae) if possible_tap_formulae.size > 1
|
2016-09-23 22:02:23 +02:00
|
|
|
|
|
|
|
if possible_tap_formulae.size == 1
|
2015-09-12 18:04:06 +08:00
|
|
|
path = possible_tap_formulae.first.resolved_path
|
|
|
|
name = path.basename(".rb").to_s
|
|
|
|
return FormulaLoader.new(name, path)
|
2015-05-08 19:16:06 +08:00
|
|
|
end
|
|
|
|
|
2021-11-17 22:20:48 -08:00
|
|
|
return TapLoader.new("#{CoreTap.instance}/#{ref}", from: from) if CoreTap.instance.formula_renames.key?(ref)
|
2015-08-09 14:52:49 +03:00
|
|
|
|
2021-11-17 22:20:48 -08:00
|
|
|
possible_taps = Tap.select { |tap| tap.formula_renames.key?(ref) }
|
2015-08-09 14:52:49 +03:00
|
|
|
|
2021-11-17 22:20:48 -08:00
|
|
|
if possible_taps.size > 1
|
|
|
|
possible_tap_newname_formulae = possible_taps.map { |tap| "#{tap}/#{tap.formula_renames[ref]}" }
|
2015-08-09 14:52:49 +03:00
|
|
|
raise TapFormulaWithOldnameAmbiguityError.new(ref, possible_tap_newname_formulae)
|
2016-09-23 22:02:23 +02:00
|
|
|
end
|
|
|
|
|
2021-11-17 22:20:48 -08:00
|
|
|
return TapLoader.new("#{possible_taps.first}/#{ref}", from: from) unless possible_taps.empty?
|
2015-08-09 14:52:49 +03:00
|
|
|
|
2017-01-09 21:42:09 +00:00
|
|
|
possible_keg_formula = Pathname.new("#{HOMEBREW_PREFIX}/opt/#{ref}/.brew/#{ref}.rb")
|
2019-02-19 13:11:32 +00:00
|
|
|
return FormulaLoader.new(ref, possible_keg_formula) if possible_keg_formula.file?
|
2017-01-09 21:42:09 +00:00
|
|
|
|
2014-04-05 22:03:33 -05:00
|
|
|
possible_cached_formula = Pathname.new("#{HOMEBREW_CACHE_FORMULA}/#{ref}.rb")
|
2019-02-19 13:11:32 +00:00
|
|
|
return FormulaLoader.new(ref, possible_cached_formula) if possible_cached_formula.file?
|
2014-04-05 22:03:33 -05:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
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)
|
2016-09-15 16:01:18 +01:00
|
|
|
CoreTap.instance.formula_dir/"#{name.to_s.downcase}.rb"
|
2015-05-08 18:59:08 +08:00
|
|
|
end
|
2015-05-08 19:16:06 +08:00
|
|
|
|
2022-09-14 23:54:14 -04:00
|
|
|
def self.core_alias_path(name)
|
|
|
|
CoreTap.instance.alias_dir/name.to_s.downcase
|
|
|
|
end
|
|
|
|
|
2017-10-21 19:57:01 +02:00
|
|
|
def self.tap_paths(name, taps = Dir[HOMEBREW_LIBRARY/"Taps/*/*/"])
|
2017-04-07 10:26:00 +09:00
|
|
|
name = name.to_s.downcase
|
2015-08-09 22:43:01 +08:00
|
|
|
taps.map do |tap|
|
2015-06-12 13:26:46 +08:00
|
|
|
Pathname.glob([
|
2021-02-11 13:24:19 +00:00
|
|
|
"#{tap}Formula/#{name}.rb",
|
|
|
|
"#{tap}HomebrewFormula/#{name}.rb",
|
|
|
|
"#{tap}#{name}.rb",
|
|
|
|
"#{tap}Aliases/#{name}",
|
|
|
|
]).find(&:file?)
|
2015-06-12 13:26:46 +08:00
|
|
|
end.compact
|
2015-05-08 19:16:06 +08:00
|
|
|
end
|
2013-06-08 20:58:43 -07:00
|
|
|
end
|