Enable loading stubbed formulae
This commit is contained in:
parent
99cdd7d8c0
commit
b04b0971a1
@ -271,6 +271,7 @@ class Formula
|
|||||||
@prefix_returns_versioned_prefix = T.let(false, T.nilable(T::Boolean))
|
@prefix_returns_versioned_prefix = T.let(false, T.nilable(T::Boolean))
|
||||||
@oldname_locks = T.let([], T::Array[FormulaLock])
|
@oldname_locks = T.let([], T::Array[FormulaLock])
|
||||||
@on_system_blocks_exist = T.let(false, T::Boolean)
|
@on_system_blocks_exist = T.let(false, T::Boolean)
|
||||||
|
@fully_loaded_formula = T.let(nil, T.nilable(Formula))
|
||||||
end
|
end
|
||||||
|
|
||||||
sig { params(spec_sym: Symbol).void }
|
sig { params(spec_sym: Symbol).void }
|
||||||
@ -557,12 +558,34 @@ class Formula
|
|||||||
# @see .loaded_from_api?
|
# @see .loaded_from_api?
|
||||||
delegate loaded_from_api?: :"self.class"
|
delegate loaded_from_api?: :"self.class"
|
||||||
|
|
||||||
|
# Whether this formula was loaded using the formulae.brew.sh API.
|
||||||
|
# @!method loaded_from_stub?
|
||||||
|
# @see .loaded_from_stub?
|
||||||
|
delegate loaded_from_stub?: :"self.class"
|
||||||
|
|
||||||
# The API source data used to load this formula.
|
# The API source data used to load this formula.
|
||||||
# Returns `nil` if the formula was not loaded from the API.
|
# Returns `nil` if the formula was not loaded from the API.
|
||||||
# @!method api_source
|
# @!method api_source
|
||||||
# @see .api_source
|
# @see .api_source
|
||||||
delegate api_source: :"self.class"
|
delegate api_source: :"self.class"
|
||||||
|
|
||||||
|
sig { returns(Formula) }
|
||||||
|
def fully_loaded_formula
|
||||||
|
@fully_loaded_formula ||= if loaded_from_stub?
|
||||||
|
json_contents = Homebrew::API::Formula.formula_json(name)
|
||||||
|
Formulary.from_json_contents(name, json_contents)
|
||||||
|
else
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
sig { params(download_queue: T.nilable(Homebrew::DownloadQueue)).void }
|
||||||
|
def fetch_fully_loaded_formula!(download_queue: nil)
|
||||||
|
return unless loaded_from_stub?
|
||||||
|
|
||||||
|
Homebrew::API::Formula.fetch_formula_json!(name, download_queue:)
|
||||||
|
end
|
||||||
|
|
||||||
sig { void }
|
sig { void }
|
||||||
def update_head_version
|
def update_head_version
|
||||||
return unless head?
|
return unless head?
|
||||||
@ -3366,6 +3389,7 @@ class Formula
|
|||||||
@skip_clean_paths = T.let(Set.new, T.nilable(T::Set[T.any(String, Symbol)]))
|
@skip_clean_paths = T.let(Set.new, T.nilable(T::Set[T.any(String, Symbol)]))
|
||||||
@link_overwrite_paths = T.let(Set.new, T.nilable(T::Set[String]))
|
@link_overwrite_paths = T.let(Set.new, T.nilable(T::Set[String]))
|
||||||
@loaded_from_api = T.let(false, T.nilable(T::Boolean))
|
@loaded_from_api = T.let(false, T.nilable(T::Boolean))
|
||||||
|
@loaded_from_stub = T.let(false, T.nilable(T::Boolean))
|
||||||
@api_source = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
@api_source = T.let(nil, T.nilable(T::Hash[String, T.untyped]))
|
||||||
@on_system_blocks_exist = T.let(false, T.nilable(T::Boolean))
|
@on_system_blocks_exist = T.let(false, T.nilable(T::Boolean))
|
||||||
@network_access_allowed = T.let(SUPPORTED_NETWORK_ACCESS_PHASES.to_h do |phase|
|
@network_access_allowed = T.let(SUPPORTED_NETWORK_ACCESS_PHASES.to_h do |phase|
|
||||||
@ -3391,6 +3415,10 @@ class Formula
|
|||||||
sig { returns(T::Boolean) }
|
sig { returns(T::Boolean) }
|
||||||
def loaded_from_api? = !!@loaded_from_api
|
def loaded_from_api? = !!@loaded_from_api
|
||||||
|
|
||||||
|
# Whether this formula was loaded using the internal formulae.brew.sh API.
|
||||||
|
sig { returns(T::Boolean) }
|
||||||
|
def loaded_from_stub? = !!@loaded_from_stub
|
||||||
|
|
||||||
# Whether this formula was loaded using the formulae.brew.sh API.
|
# Whether this formula was loaded using the formulae.brew.sh API.
|
||||||
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
||||||
attr_reader :api_source
|
attr_reader :api_source
|
||||||
|
@ -50,6 +50,11 @@ module Formulary
|
|||||||
platform_cache.key?(:api) && platform_cache[:api].key?(name)
|
platform_cache.key?(:api) && platform_cache[:api].key?(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { params(name: String).returns(T::Boolean) }
|
||||||
|
def self.formula_class_defined_from_stub?(name)
|
||||||
|
platform_cache.key?(:stub) && platform_cache.fetch(:stub).key?(name)
|
||||||
|
end
|
||||||
|
|
||||||
def self.formula_class_get_from_path(path)
|
def self.formula_class_get_from_path(path)
|
||||||
platform_cache[:path].fetch(path)
|
platform_cache[:path].fetch(path)
|
||||||
end
|
end
|
||||||
@ -58,6 +63,11 @@ module Formulary
|
|||||||
platform_cache[:api].fetch(name)
|
platform_cache[:api].fetch(name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { params(name: String).returns(T.class_of(Formula)) }
|
||||||
|
def self.formula_class_get_from_stub(name)
|
||||||
|
platform_cache.fetch(:stub).fetch(name)
|
||||||
|
end
|
||||||
|
|
||||||
def self.clear_cache
|
def self.clear_cache
|
||||||
platform_cache.each do |type, cached_objects|
|
platform_cache.each do |type, cached_objects|
|
||||||
next if type == :formulary_factory
|
next if type == :formulary_factory
|
||||||
@ -445,17 +455,59 @@ module Formulary
|
|||||||
platform_cache[:api][name] = klass
|
platform_cache[:api][name] = klass
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sig { params(name: String, formula_stub: Homebrew::FormulaStub, flags: T::Array[String]).returns(T.class_of(Formula)) }
|
||||||
|
def self.load_formula_from_stub!(name, formula_stub, flags:)
|
||||||
|
namespace = :"FormulaNamespaceStub#{namespace_key(formula_stub.to_json)}"
|
||||||
|
|
||||||
|
mod = Module.new
|
||||||
|
remove_const(namespace) if const_defined?(namespace)
|
||||||
|
const_set(namespace, mod)
|
||||||
|
|
||||||
|
mod.const_set(:BUILD_FLAGS, flags)
|
||||||
|
|
||||||
|
class_name = class_s(name)
|
||||||
|
|
||||||
|
klass = Class.new(::Formula) do
|
||||||
|
@loaded_from_api = true
|
||||||
|
@loaded_from_stub = true
|
||||||
|
|
||||||
|
url "formula-stub://#{name}/#{formula_stub.pkg_version}"
|
||||||
|
version formula_stub.version.to_s
|
||||||
|
revision formula_stub.revision
|
||||||
|
|
||||||
|
bottle do
|
||||||
|
if Homebrew::EnvConfig.bottle_domain == HOMEBREW_BOTTLE_DEFAULT_DOMAIN
|
||||||
|
root_url HOMEBREW_BOTTLE_DEFAULT_DOMAIN
|
||||||
|
else
|
||||||
|
root_url Homebrew::EnvConfig.bottle_domain
|
||||||
|
end
|
||||||
|
rebuild formula_stub.rebuild
|
||||||
|
sha256 Utils::Bottles.tag.to_sym => formula_stub.sha256
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method :install do
|
||||||
|
raise NotImplementedError, "Cannot build from source from abstract stubbed formula."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mod.const_set(class_name, klass)
|
||||||
|
|
||||||
|
platform_cache[:stub] ||= {}
|
||||||
|
platform_cache[:stub][name] = klass
|
||||||
|
end
|
||||||
|
|
||||||
sig {
|
sig {
|
||||||
params(name: String, spec: T.nilable(Symbol), force_bottle: T::Boolean, flags: T::Array[String]).returns(Formula)
|
params(name: String, spec: T.nilable(Symbol), force_bottle: T::Boolean, flags: T::Array[String], prefer_stub: T::Boolean).returns(Formula)
|
||||||
}
|
}
|
||||||
def self.resolve(
|
def self.resolve(
|
||||||
name,
|
name,
|
||||||
spec: nil,
|
spec: nil,
|
||||||
force_bottle: false,
|
force_bottle: false,
|
||||||
flags: []
|
flags: [],
|
||||||
|
prefer_stub: false
|
||||||
)
|
)
|
||||||
if name.include?("/") || File.exist?(name)
|
if name.include?("/") || File.exist?(name)
|
||||||
f = factory(name, *spec, force_bottle:, flags:)
|
f = factory(name, *spec, force_bottle:, flags:, prefer_stub:)
|
||||||
if f.any_version_installed?
|
if f.any_version_installed?
|
||||||
tab = Tab.for_formula(f)
|
tab = Tab.for_formula(f)
|
||||||
resolved_spec = spec || tab.spec
|
resolved_spec = spec || tab.spec
|
||||||
@ -468,7 +520,7 @@ module Formulary
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
rack = to_rack(name)
|
rack = to_rack(name)
|
||||||
alias_path = factory(name, force_bottle:, flags:).alias_path
|
alias_path = factory(name, force_bottle:, flags:, prefer_stub:).alias_path
|
||||||
f = from_rack(rack, *spec, alias_path:, force_bottle:, flags:)
|
f = from_rack(rack, *spec, alias_path:, force_bottle:, flags:)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -936,6 +988,46 @@ module Formulary
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Load formulae directly from their JSON contents.
|
||||||
|
class FormulaJSONContentsLoader < FromAPILoader
|
||||||
|
def initialize(name, contents, tap: nil, alias_name: nil)
|
||||||
|
@contents = contents
|
||||||
|
super(name, tap: tap, alias_name: alias_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_from_api(flags:)
|
||||||
|
Formulary.load_formula_from_json!(name, @contents, flags:)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Load a formula stub from the internal API.
|
||||||
|
class FormulaStubLoader < FromAPILoader
|
||||||
|
sig {
|
||||||
|
params(ref: T.any(String, Pathname, URI::Generic), from: T.nilable(Symbol), warn: T::Boolean)
|
||||||
|
.returns(T.nilable(T.attached_class))
|
||||||
|
}
|
||||||
|
def self.try_new(ref, from: nil, warn: false)
|
||||||
|
return unless Homebrew::EnvConfig.use_internal_api?
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def klass(flags:, ignore_errors:)
|
||||||
|
load_from_api(flags:) unless Formulary.formula_class_defined_from_stub?(name)
|
||||||
|
Formulary.formula_class_get_from_stub(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_from_api(flags:)
|
||||||
|
formula_stub = Homebrew::API::Internal.formula_stub(name)
|
||||||
|
|
||||||
|
Formulary.load_formula_from_stub!(name, formula_stub, flags:)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Return a {Formula} instance for the given reference.
|
# Return a {Formula} instance for the given reference.
|
||||||
# `ref` is a string containing:
|
# `ref` is a string containing:
|
||||||
#
|
#
|
||||||
@ -955,6 +1047,7 @@ module Formulary
|
|||||||
force_bottle: T::Boolean,
|
force_bottle: T::Boolean,
|
||||||
flags: T::Array[String],
|
flags: T::Array[String],
|
||||||
ignore_errors: T::Boolean,
|
ignore_errors: T::Boolean,
|
||||||
|
prefer_stub: T::Boolean,
|
||||||
).returns(Formula)
|
).returns(Formula)
|
||||||
}
|
}
|
||||||
def self.factory(
|
def self.factory(
|
||||||
@ -965,15 +1058,17 @@ module Formulary
|
|||||||
warn: false,
|
warn: false,
|
||||||
force_bottle: false,
|
force_bottle: false,
|
||||||
flags: [],
|
flags: [],
|
||||||
ignore_errors: false
|
ignore_errors: false,
|
||||||
|
prefer_stub: false
|
||||||
)
|
)
|
||||||
cache_key = "#{ref}-#{spec}-#{alias_path}-#{from}"
|
cache_key = "#{ref}-#{spec}-#{alias_path}-#{from}-#{prefer_stub}"
|
||||||
if factory_cached? && platform_cache[:formulary_factory]&.key?(cache_key)
|
if factory_cached? && platform_cache[:formulary_factory]&.key?(cache_key)
|
||||||
return platform_cache[:formulary_factory][cache_key]
|
return platform_cache[:formulary_factory][cache_key]
|
||||||
end
|
end
|
||||||
|
|
||||||
formula = loader_for(ref, from:, warn:)
|
loader = FormulaStubLoader.try_new(ref, from:, warn:) if prefer_stub
|
||||||
.get_formula(spec, alias_path:, force_bottle:, flags:, ignore_errors:)
|
loader ||= loader_for(ref, from:, warn:)
|
||||||
|
formula = loader.get_formula(spec, alias_path:, force_bottle:, flags:, ignore_errors:)
|
||||||
|
|
||||||
if factory_cached?
|
if factory_cached?
|
||||||
platform_cache[:formulary_factory] ||= {}
|
platform_cache[:formulary_factory] ||= {}
|
||||||
@ -1098,6 +1193,31 @@ module Formulary
|
|||||||
.get_formula(spec, alias_path:, force_bottle:, flags:, ignore_errors:)
|
.get_formula(spec, alias_path:, force_bottle:, flags:, ignore_errors:)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return a {Formula} instance directly from JSON contents.
|
||||||
|
sig {
|
||||||
|
params(
|
||||||
|
name: String,
|
||||||
|
contents: T::Hash[String, T.untyped],
|
||||||
|
spec: Symbol,
|
||||||
|
alias_path: T.nilable(Pathname),
|
||||||
|
force_bottle: T::Boolean,
|
||||||
|
flags: T::Array[String],
|
||||||
|
ignore_errors: T::Boolean,
|
||||||
|
).returns(Formula)
|
||||||
|
}
|
||||||
|
def self.from_json_contents(
|
||||||
|
name,
|
||||||
|
contents,
|
||||||
|
spec = :stable,
|
||||||
|
alias_path: nil,
|
||||||
|
force_bottle: false,
|
||||||
|
flags: [],
|
||||||
|
ignore_errors: false
|
||||||
|
)
|
||||||
|
FormulaJSONContentsLoader.new(name, contents)
|
||||||
|
.get_formula(spec, alias_path:, force_bottle:, flags:, ignore_errors:)
|
||||||
|
end
|
||||||
|
|
||||||
def self.to_rack(ref)
|
def self.to_rack(ref)
|
||||||
# If using a fully-scoped reference, check if the formula can be resolved.
|
# If using a fully-scoped reference, check if the formula can be resolved.
|
||||||
factory(ref) if ref.include? "/"
|
factory(ref) if ref.include? "/"
|
||||||
|
3
Library/Homebrew/sorbet/rbi/dsl/formula.rbi
generated
3
Library/Homebrew/sorbet/rbi/dsl/formula.rbi
generated
@ -111,6 +111,9 @@ class Formula
|
|||||||
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
||||||
def loaded_from_api?(*args, &block); end
|
def loaded_from_api?(*args, &block); end
|
||||||
|
|
||||||
|
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
||||||
|
def loaded_from_stub?(*args, &block); end
|
||||||
|
|
||||||
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
sig { params(args: T.untyped, block: T.untyped).returns(T::Boolean) }
|
||||||
def network_access_allowed?(*args, &block); end
|
def network_access_allowed?(*args, &block); end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user