WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 0c7da2a

Browse files
committed
Improve FormulaStruct usage in Formulary
1 parent e0534de commit 0c7da2a

File tree

5 files changed

+349
-204
lines changed

5 files changed

+349
-204
lines changed
Lines changed: 299 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,243 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4+
require "utils/spdx"
5+
46
class FormulaStruct < T::Struct
5-
const :uses_from_macos, T::Array[T.any(String, T::Hash[String, String])], default: []
6-
const :requirements, T::Array[T::Hash[String, T.untyped]], default: []
7-
const :dependencies, T::Array[String], default: []
8-
const :build_dependencies, T::Array[String], default: []
9-
const :test_dependencies, T::Array[String], default: []
10-
const :recommended_dependencies, T::Array[String], default: []
11-
const :optional_dependencies, T::Array[String], default: []
12-
const :stable_dependencies, T::Hash[String, T::Array[String]], default: { dependencies: [], build_dependencies: [], test_dependencies: [], recommended_dependencies: [], optional_dependencies: [], uses_from_macos_bounds: [] }
13-
const :head_dependencies, T::Hash[String, T::Array[String]], default: { dependencies: [], build_dependencies: [], test_dependencies: [], recommended_dependencies: [], optional_dependencies: [], uses_from_macos_bounds: [] }
7+
# `:codesign` and custom requirement classes are not supported.
8+
API_SUPPORTED_REQUIREMENTS = [:arch, :linux, :macos, :maximum_macos, :xcode].freeze
9+
private_constant :API_SUPPORTED_REQUIREMENTS
10+
11+
DeprecateDisableArgs = T.type_alias do
12+
{
13+
date: String,
14+
because: T.nilable(T.any(String, Symbol)),
15+
replacement_formula: T.nilable(String),
16+
replacement_cask: T.nilable(String),
17+
}
18+
end
19+
20+
DependencyArgs = T.type_alias do
21+
T.any(
22+
# Formula name: "foo"
23+
String,
24+
# Formula name and dependency type: { "foo" => :build }
25+
T::Hash[String, Symbol],
26+
)
27+
end
28+
29+
RequirementArgs = T.type_alias do
30+
T.any(
31+
# Requirement name: :macos
32+
Symbol,
33+
# Requirement name and other info: { macos: :build }
34+
T::Hash[Symbol, T::Array[T.anything]],
35+
)
36+
end
37+
38+
UsesFromMacOSArgs = T.type_alias do
39+
[
40+
T.any(String, T::Hash[T.any(String, Symbol), T.any(Symbol, T::Array[Symbol])]),
41+
T::Hash[Symbol, Symbol],
42+
]
43+
end
44+
45+
sig { params(hash: T::Hash[String, T.untyped]).returns(FormulaStruct) }
46+
def self.from_hash(hash)
47+
hash["license"] = SPDX.string_to_license_expression(hash["license"])
48+
49+
if (reason = hash["no_autobump_message"])
50+
reason = reason.to_sym if NO_AUTOBUMP_REASONS_LIST.key?(reason.to_sym)
51+
hash["no_autobump_args"] = { because: reason }
52+
end
53+
54+
if (caveats = hash["caveats"])
55+
hash["caveats"] = Formulary.replace_placeholders(caveats)
56+
end
57+
58+
if (condition = hash["pour_bottle_only_if"])
59+
hash["pour_bottle_args"] = { only_if: condition.to_sym }
60+
end
61+
62+
if (keg_only_hash = hash["keg_only_reason"])
63+
reason = Formulary.convert_to_string_or_symbol(keg_only_hash.fetch("reason"))
64+
explanation = keg_only_hash.fetch("explanation")
65+
hash["keg_only_args"] = [reason, explanation]
66+
end
67+
68+
if (deprecation_date = hash["deprecation_date"])
69+
hash["deprecate_args"] = {
70+
date: deprecation_date,
71+
because: DeprecateDisable.to_reason_string_or_symbol(hash["deprecation_reason"], type: :formula),
72+
replacement_formula: hash["deprecation_replacement_formula"],
73+
replacement_cask: hash["deprecation_replacement_cask"],
74+
}
75+
end
76+
77+
if (disable_date = hash["disable_date"])
78+
hash["disable_args"] = {
79+
date: disable_date,
80+
because: DeprecateDisable.to_reason_string_or_symbol(hash["disable_reason"], type: :formula),
81+
replacement_formula: hash["disable_replacement_formula"],
82+
replacement_cask: hash["disable_replacement_cask"],
83+
}
84+
end
85+
86+
hash["stable_dependency_hash"] = {
87+
"dependencies" => hash["dependencies"] || [],
88+
"build_dependencies" => hash["build_dependencies"] || [],
89+
"test_dependencies" => hash["test_dependencies"] || [],
90+
"recommended_dependencies" => hash["recommended_dependencies"] || [],
91+
"optional_dependencies" => hash["optional_dependencies"] || [],
92+
"uses_from_macos" => hash["uses_from_macos"] || [],
93+
"uses_from_macos_bounds" => hash["uses_from_macos_bounds"] || [],
94+
}
95+
96+
conflicts_with = hash["conflicts_with"] || []
97+
conflicts_with_reasons = hash["conflicts_with_reasons"] || []
98+
hash["conflicts"] = conflicts_with.zip(conflicts_with_reasons).map do |name, because|
99+
[name, { because: }]
100+
end
101+
102+
# Rename some entries for clarity
103+
hash["link_overwrite_paths"] = hash["link_overwrite"]
104+
hash["requirements_array"] = hash["requirements"]
105+
hash["head_dependency_hash"] = hash["head_dependencies"]
106+
107+
super
108+
end
109+
110+
const :stable_dependency_hash, T::Hash[String, T::Array[String]], default: {}
111+
const :head_dependency_hash, T::Hash[String, T::Array[String]], default: {}
14112
const :caveats, T.nilable(String)
15113
const :desc, String
16114
const :homepage, String
17-
const :license, String
115+
const :license, SPDX::LicenseExpression
18116
const :revision, Integer, default: 0
19117
const :version_scheme, Integer, default: 0
20-
const :no_autobump_msg, T.nilable(String)
118+
const :link_overwrite_paths, T::Array[String], default: []
119+
const :post_install_defined, T::Boolean, default: false
120+
const :service, T.nilable(T::Hash[String, T.untyped])
121+
const :tap_git_head, String
122+
const :oldnames, T.nilable(T::Array[String])
123+
const :ruby_source_path, String
124+
const :ruby_source_checksum, T::Hash[String, String]
125+
const :aliases, T::Array[String], default: []
126+
const :versioned_formulae, T::Array[String], default: []
127+
const :bottle, T::Hash[String, T.untyped], default: {}
128+
const :no_autobump_args, T.nilable({ because: T.any(String, Symbol) })
129+
const :pour_bottle_args, T.nilable({ only_if: Symbol })
130+
const :keg_only_args, T.nilable([T.any(String, Symbol), String])
131+
const :deprecate_args, T.nilable(DeprecateDisableArgs)
132+
const :disable_args, T.nilable(DeprecateDisableArgs)
133+
const :conflicts, T::Array[[String, { because: T.nilable(String) }]], default: []
134+
135+
sig { returns(String) }
136+
def ruby_source_sha256
137+
ruby_source_checksum.fetch("sha256")
138+
end
139+
140+
sig { returns(T::Boolean) }
141+
def head?
142+
urls["head"].present?
143+
end
144+
145+
sig { returns([String, T::Hash[Symbol, T.anything]]) }
146+
def head_url_args
147+
url = T.must(urls.dig("head", "url"))
148+
specs = {
149+
branch: urls.dig("head", "branch"),
150+
using: urls.dig("head", "using")&.to_sym,
151+
}.compact_blank
152+
[url, specs]
153+
end
154+
155+
sig { returns(T::Array[T.any(DependencyArgs, RequirementArgs)]) }
156+
def head_dependencies
157+
spec_dependencies(:head) + spec_requirements(:head)
158+
end
159+
160+
sig { returns(T::Array[UsesFromMacOSArgs]) }
161+
def head_uses_from_macos
162+
spec_uses_from_macos(:head)
163+
end
164+
165+
sig { returns(T::Boolean) }
166+
def stable?
167+
urls["stable"].present?
168+
end
169+
170+
sig { returns([String, T::Hash[Symbol, T.anything]]) }
171+
def stable_url_args
172+
url = T.must(urls.dig("stable", "url"))
173+
specs = {
174+
tag: urls.dig("stable", "tag"),
175+
revision: urls.dig("stable", "revision"),
176+
using: urls.dig("stable", "using")&.to_sym,
177+
}.compact_blank
178+
[url, specs]
179+
end
180+
181+
sig { returns(String) }
182+
def stable_version
183+
versions.fetch("stable")
184+
end
185+
186+
sig { returns(T.nilable(String)) }
187+
def stable_checksum
188+
urls.dig("stable", "checksum")
189+
end
190+
191+
sig { returns(T::Array[T.any(DependencyArgs, RequirementArgs)]) }
192+
def stable_dependencies
193+
spec_dependencies(:stable) + spec_requirements(:stable)
194+
end
195+
196+
sig { returns(T::Array[UsesFromMacOSArgs]) }
197+
def stable_uses_from_macos
198+
spec_uses_from_macos(:stable)
199+
end
200+
201+
sig { returns(T::Boolean) }
202+
def bottle?
203+
bottle["stable"].present?
204+
end
205+
206+
sig { returns(Integer) }
207+
def bottle_rebuild
208+
bottle.dig("stable", "rebuild") || 0
209+
end
210+
211+
sig { returns(T::Array[T::Hash[String, T.anything]]) }
212+
def bottle_checksums
213+
Array(bottle.dig("stable", "files")).map do |tag, bottle_spec|
214+
{
215+
cellar: Formulary.convert_to_string_or_symbol(bottle_spec.fetch("cellar")),
216+
tag.to_sym => bottle_spec.fetch("sha256"),
217+
}
218+
end
219+
end
220+
221+
# sig { returns(T::Boolean) }
222+
# def service?
223+
# service.present?
224+
# end
225+
226+
# def service_run_args
227+
# end
228+
229+
# def service_name_args
230+
# end
231+
232+
# def service_methods_and_args
233+
# end
234+
235+
private
236+
237+
const :versions, T::Hash[String, String]
238+
const :urls, T::Hash[String, T::Hash[String, T.nilable(String)]]
239+
const :requirements_array, T::Array[T::Hash[String, T.untyped]], default: []
240+
const :no_autobump_message, T.nilable(String)
21241
const :pour_bottle_only_if, T.nilable(String)
22242
const :keg_only_reason, T.nilable(T::Hash[String, String])
23243
const :deprecation_date, T.nilable(String)
@@ -30,33 +250,75 @@ class FormulaStruct < T::Struct
30250
const :disable_replacement_cask, T.nilable(String)
31251
const :conflicts_with, T::Array[String], default: []
32252
const :conflicts_with_reasons, T::Array[String], default: []
33-
const :link_overwrite, T::Array[String], default: []
34-
const :post_install_defined, T::Boolean, default: false
35-
const :service, T.nilable(T::Hash[String, T.untyped])
36-
const :tap_git_head, String
37-
const :oldnames, T.nilable(T::Array[String])
38-
const :oldname, T.nilable(String)
39-
const :ruby_source_path, String
40-
const :ruby_source_checksum, T::Hash[String, String]
41-
const :urls, T::Hash[String, T::Hash[String, T.nilable(String)]]
42-
const :ruby_source_sha256, T.nilable(String)
43-
const :aliases, T::Array[String], default: []
44-
const :versioned_formulae, T::Array[String], default: []
45-
const :versions, T::Hash[String, String]
46-
const :bottle, T::Hash[String, T.untyped], default: {}
47-
const :uses_from_macos_bounds, T.nilable(T::Array[T::Hash[String, String]])
48253

49-
sig { params(hash: T::Hash[String, T.untyped]).returns(FormulaStruct) }
50-
def self.from_hash(hash)
51-
hash["stable_dependencies"] ||= {
52-
"dependencies" => hash["dependencies"] || [],
53-
"build_dependencies" => hash["build_dependencies"] || [],
54-
"test_dependencies" => hash["test_dependencies"] || [],
55-
"recommended_dependencies" => hash["recommended_dependencies"] || [],
56-
"optional_dependencies" => hash["optional_dependencies"] || [],
57-
"uses_from_macos_bounds" => hash["uses_from_macos_bounds"] || [],
58-
}
254+
sig { params(spec: Symbol).returns(T::Array[DependencyArgs]) }
255+
def spec_dependencies(spec)
256+
deps_hash = send("#{spec}_dependency_hash")
257+
dependencies = deps_hash.fetch("dependencies", [])
258+
dependencies + [:build, :test, :recommended, :optional].filter_map do |type|
259+
deps_hash["#{type}_dependencies"]&.map do |dep|
260+
{ dep => type }
261+
end
262+
end.flatten(1)
263+
end
59264

60-
super
265+
sig { params(spec: Symbol).returns(T::Array[UsesFromMacOSArgs]) }
266+
def spec_uses_from_macos(spec)
267+
deps_hash = send("#{spec}_dependency_hash")
268+
zipped_array = deps_hash["uses_from_macos"]&.zip(deps_hash["uses_from_macos_bounds"])
269+
return [] unless zipped_array
270+
271+
zipped_array.map do |entry, bounds|
272+
bounds ||= {}
273+
bounds = bounds.transform_keys(&:to_sym).transform_values(&:to_sym)
274+
275+
if entry.is_a?(Hash)
276+
# The key is the dependency name, the value is the dep type. Only the type should be a symbol
277+
entry = entry.deep_transform_values(&:to_sym)
278+
# When passing both a dep type and bounds, uses_from_macos expects them both in the first argument
279+
entry = entry.merge(bounds)
280+
[entry, {}]
281+
else
282+
[entry, bounds]
283+
end
284+
end
285+
end
286+
287+
sig { params(spec: Symbol).returns(T::Array[RequirementArgs]) }
288+
def spec_requirements(spec)
289+
requirements_array.filter_map do |req|
290+
next unless req["specs"].include?(spec.to_s)
291+
292+
req_name = req["name"].to_sym
293+
next if API_SUPPORTED_REQUIREMENTS.exclude?(req_name)
294+
295+
req_version = case req_name
296+
when :arch
297+
req["version"]&.to_sym
298+
when :macos, :maximum_macos
299+
MacOSVersion::SYMBOLS.key(req["version"])
300+
else
301+
req["version"]
302+
end
303+
304+
req_tags = []
305+
req_tags << req_version if req_version.present?
306+
req_tags += req["contexts"]&.map do |tag|
307+
case tag
308+
when String
309+
tag.to_sym
310+
when Hash
311+
tag.deep_transform_keys(&:to_sym)
312+
else
313+
tag
314+
end
315+
end
316+
317+
if req_tags.empty?
318+
req_name
319+
else
320+
{ req_name => req_tags }
321+
end
322+
end
61323
end
62324
end

Library/Homebrew/dependency_collector.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def parse_string_spec(spec, tags)
160160

161161
def parse_symbol_spec(spec, tags)
162162
# When modifying this list of supported requirements, consider
163-
# whether `Formulary::API_SUPPORTED_REQUIREMENTS` should also be changed.
163+
# whether `FormulaStruct::API_SUPPORTED_REQUIREMENTS` should also be changed.
164164
case spec
165165
when :arch then ArchRequirement.new(tags)
166166
when :codesign then CodesignRequirement.new(tags)

Library/Homebrew/formula.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3605,14 +3605,14 @@ def desc(val = T.unsafe(nil))
36053605
# @see https://spdx.github.io/spdx-spec/latest/annexes/spdx-license-expressions/ SPDX license expression guide
36063606
# @api public
36073607
sig {
3608-
params(args: T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
3609-
.returns(T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
3608+
params(args: T.nilable(SPDX::LicenseExpression))
3609+
.returns(T.nilable(SPDX::LicenseExpression))
36103610
}
36113611
def license(args = nil)
36123612
if args.nil?
36133613
@licenses
36143614
else
3615-
@licenses = T.let(args, T.nilable(T.any(String, Symbol, T::Hash[T.any(String, Symbol), T.anything])))
3615+
@licenses = T.let(args, T.nilable(SPDX::LicenseExpression))
36163616
end
36173617
end
36183618

@@ -3939,7 +3939,7 @@ def build_flags
39393939
# ```
39403940
#
39413941
# @api public
3942-
sig { params(block: T.nilable(T.proc.returns(SoftwareSpec))).returns(T.untyped) }
3942+
sig { params(block: T.nilable(T.proc.void)).returns(T.untyped) }
39433943
def stable(&block)
39443944
return T.must(@stable) unless block
39453945

0 commit comments

Comments
 (0)