From 039eb1adffdff15fccb461d9e3aa4b8e450bc1e1 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Sun, 17 Mar 2024 13:55:57 -0700 Subject: [PATCH 1/4] Update formula internal json v3 to improve dependencies readability This improves the readability of dependencies in the json we produce for this internally. --- Library/Homebrew/formula.rb | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 24cc6f75a2..c70f79e923 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2361,13 +2361,6 @@ class Formula "ruby_source_sha256" => ruby_source_checksum&.hexdigest, } - dep_hash = dependencies_hash - .except("recommended_dependencies", "optional_dependencies") - .transform_values(&:presence) - .compact - - api_hash.merge!(dep_hash) - # Exclude default values. api_hash["revision"] = revision unless revision.zero? api_hash["version_scheme"] = version_scheme unless version_scheme.zero? @@ -2389,6 +2382,14 @@ class Formula api_hash["versioned_formulae"] = versioned_formulae_list.map(&:name) end + if (dependencies = dependencies_list(:stable).presence) + api_hash["dependencies"] = dependencies + end + + if (head_dependencies = dependencies_list(:head).presence) + api_hash["head_dependencies"] = head_dependencies + end + if (requirements_array = serialized_requirements.presence) api_hash["requirements"] = requirements_array end @@ -2608,6 +2609,19 @@ class Formula hash end + def dependencies_list(spec_symbol) + return if spec_symbol != :stable && spec_symbol != :head + + send(spec_symbol)&.declared_deps&.each_with_object({}) do |dep, dep_hash| + next if dep.implicit? # Remove all implicit deps + + dep_hash[dep.name] = {}.tap do |info| + info[:tags] = dep.tags if dep.tags.present? + info[:uses_from_macos] = dep.bounds.presence if dep.uses_from_macos? + end.presence + end + end + def on_system_blocks_exist? self.class.on_system_blocks_exist? || @on_system_blocks_exist end From ff0c24786b023e5d0941f6732f6dc041b44cd088 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Thu, 25 Apr 2024 21:41:13 -0700 Subject: [PATCH 2/4] Unpack new internal json v3 dep format --- Library/Homebrew/formulary.rb | 85 ++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 53df1d8374..86ca772cd8 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -211,38 +211,71 @@ module Formulary end end - add_deps = lambda do |spec| - T.bind(self, SoftwareSpec) + add_deps = if Homebrew::API.internal_json_v3? + lambda do |deps| + T.bind(self, SoftwareSpec) - dep_json = json_formula.fetch("#{spec}_dependencies", json_formula) + deps&.each do |name, info| + tags = case info["tags"] + in Array => tag_list + tag_list.map(&:to_sym) + in String => tag + tag.to_sym + else + nil + end - dep_json["dependencies"]&.each do |dep| - # Backwards compatibility check - uses_from_macos used to be a part of dependencies on Linux - next if !json_formula.key?("uses_from_macos_bounds") && uses_from_macos_names.include?(dep) && - !Homebrew::SimulateSystem.simulating_or_running_on_macos? + if info.key?("uses_from_macos") + bounds = info["uses_from_macos"] || {} + bounds.deep_transform_keys!(&:to_sym) + bounds.deep_transform_values!(&:to_sym) - depends_on dep + if tags + uses_from_macos name => tags, **bounds + else + uses_from_macos name, **bounds + end + elsif tags + depends_on name => tags + else + depends_on name + end + end end + else + lambda do |spec| + T.bind(self, SoftwareSpec) - [:build, :test, :recommended, :optional].each do |type| - dep_json["#{type}_dependencies"]&.each do |dep| + dep_json = json_formula.fetch("#{spec}_dependencies", json_formula) + + dep_json["dependencies"]&.each do |dep| # Backwards compatibility check - uses_from_macos used to be a part of dependencies on Linux next if !json_formula.key?("uses_from_macos_bounds") && uses_from_macos_names.include?(dep) && !Homebrew::SimulateSystem.simulating_or_running_on_macos? - depends_on dep => type + depends_on dep end - end - dep_json["uses_from_macos"]&.each_with_index do |dep, index| - bounds = dep_json.fetch("uses_from_macos_bounds", [])[index].dup || {} - bounds.deep_transform_keys!(&:to_sym) - bounds.deep_transform_values!(&:to_sym) + [:build, :test, :recommended, :optional].each do |type| + dep_json["#{type}_dependencies"]&.each do |dep| + # Backwards compatibility check - uses_from_macos used to be a part of dependencies on Linux + next if !json_formula.key?("uses_from_macos_bounds") && uses_from_macos_names.include?(dep) && + !Homebrew::SimulateSystem.simulating_or_running_on_macos? - if dep.is_a?(Hash) - uses_from_macos dep.deep_transform_values(&:to_sym).merge(bounds) - else - uses_from_macos dep, bounds + depends_on dep => type + end + end + + dep_json["uses_from_macos"]&.each_with_index do |dep, index| + bounds = dep_json.fetch("uses_from_macos_bounds", [])[index].dup || {} + bounds.deep_transform_keys!(&:to_sym) + bounds.deep_transform_values!(&:to_sym) + + if dep.is_a?(Hash) + uses_from_macos dep.deep_transform_values(&:to_sym).merge(bounds) + else + uses_from_macos dep, bounds + end end end end @@ -267,7 +300,11 @@ module Formulary version Homebrew::API.internal_json_v3? ? json_formula["version"] : json_formula["versions"]["stable"] sha256 urls_stable["checksum"] if urls_stable["checksum"].present? - instance_exec(:stable, &add_deps) + if Homebrew::API.internal_json_v3? + instance_exec(json_formula["dependencies"], &add_deps) + else + instance_exec(:stable, &add_deps) + end requirements[:stable]&.each do |req| depends_on req @@ -283,7 +320,11 @@ module Formulary }.compact url urls_head["url"], **url_spec - instance_exec(:head, &add_deps) + if Homebrew::API.internal_json_v3? + instance_exec(json_formula["head_dependencies"], &add_deps) + else + instance_exec(:head, &add_deps) + end requirements[:head]&.each do |req| depends_on req From 541305ae98a5b79cb93f14d64aea250205ccde6e Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Fri, 26 Apr 2024 23:49:39 -0700 Subject: [PATCH 3/4] Update internal json v3 formula tests --- Library/Homebrew/formulary.rb | 4 +- .../internal_tap_json/homebrew-core.json | 49 ++++++++++--------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 86ca772cd8..a11220a062 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -216,7 +216,7 @@ module Formulary T.bind(self, SoftwareSpec) deps&.each do |name, info| - tags = case info["tags"] + tags = case info&.dig("tags") in Array => tag_list tag_list.map(&:to_sym) in String => tag @@ -225,7 +225,7 @@ module Formulary nil end - if info.key?("uses_from_macos") + if info&.key?("uses_from_macos") bounds = info["uses_from_macos"] || {} bounds.deep_transform_keys!(&:to_sym) bounds.deep_transform_values!(&:to_sym) diff --git a/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json index c823e3973f..9074219a04 100644 --- a/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json +++ b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json @@ -32,9 +32,6 @@ "post_install_defined": false, "ruby_source_path": "Formula/f/fennel.rb", "ruby_source_sha256": "5856e655fd1cea11496d67bc27fb14fee5cfbdea63c697c3773c7f247581197d", - "dependencies": [ - "lua" - ], "version": "1.4.0", "bottle": { "files": { @@ -43,6 +40,9 @@ "sha256": "f46028597883cbc38864c61bd3fa402da9cb90ce97415d51a7b5279bc17f7bd0" } } + }, + "dependencies": { + "lua": null } }, "ponyc": { @@ -59,25 +59,6 @@ "post_install_defined": false, "ruby_source_path": "Formula/p/ponyc.rb", "ruby_source_sha256": "81d51c25d18710191beb62f9f380bae3d878aad815a65ec1ee2a3b132c1fadb3", - "build_dependencies": [ - "cmake", - "python@3.12" - ], - "uses_from_macos": [ - { - "llvm": [ - "build", - "test" - ] - }, - "zlib" - ], - "uses_from_macos_bounds": [ - { - }, - { - } - ], "version": "0.58.1", "bottle": { "files": { @@ -110,7 +91,29 @@ "sha256": "ab49318d75eed3ee932c8e5add22f252ec0c852aad94945022877f926e93899f" } } + }, + "dependencies": { + "cmake": { + "tags": [ + "build" + ] + }, + "python@3.12": { + "tags": [ + "build" + ] + }, + "llvm": { + "tags": [ + "build", + "test" + ], + "uses_from_macos": null + }, + "zlib": { + "uses_from_macos": null + } } } } -} \ No newline at end of file +} From 6ad02b8e340e163ce90beccb33d0f297d96e7b03 Mon Sep 17 00:00:00 2001 From: apainintheneck Date: Tue, 30 Apr 2024 23:12:07 -0700 Subject: [PATCH 4/4] formula: internal json v3 dependencies: address feedback - rename #dependencies_list to #internal_dependencies_hash - the initial implementation returned an array but now it doesn't - simplify usage of #tap in #internal_dependencies_hash - remove safe navigation operator usages in #internal_dependencies_hash - better document why implicit dependencies are not included in the API JSON - add new test fixture formula to better test generation of uses from macos bounds with the new internal json format --- Library/Homebrew/formula.rb | 31 +++--- .../api/internal_tap_json/formula_spec.rb | 26 ++++- .../internal_tap_json/homebrew-core.json | 99 +++++++++++++++++++ .../homebrew-core/Formula/i/inko.rb | 45 +++++++++ 4 files changed, 189 insertions(+), 12 deletions(-) create mode 100644 Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core/Formula/i/inko.rb diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index c70f79e923..e7b519aa27 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -2382,11 +2382,11 @@ class Formula api_hash["versioned_formulae"] = versioned_formulae_list.map(&:name) end - if (dependencies = dependencies_list(:stable).presence) + if (dependencies = internal_dependencies_hash(:stable).presence) api_hash["dependencies"] = dependencies end - if (head_dependencies = dependencies_list(:head).presence) + if (head_dependencies = internal_dependencies_hash(:head).presence) api_hash["head_dependencies"] = head_dependencies end @@ -2554,7 +2554,11 @@ class Formula dependencies = self.class.spec_syms.to_h do |sym| [sym, send(sym)&.declared_deps] end - dependencies.transform_values! { |deps| deps&.reject(&:implicit?) } # Remove all implicit deps from all lists + + # Implicit dependencies are only needed when installing from source + # since they are only used to download and unpack source files. + # @see DependencyCollector + dependencies.transform_values! { |deps| deps&.reject(&:implicit?) } hash = {} @@ -2609,16 +2613,21 @@ class Formula hash end - def dependencies_list(spec_symbol) - return if spec_symbol != :stable && spec_symbol != :head + def internal_dependencies_hash(spec_symbol) + raise ArgumentError, "Unsupported spec: #{spec_symbol}" unless [:stable, :head].include?(spec_symbol) + return unless (spec = public_send(spec_symbol)) - send(spec_symbol)&.declared_deps&.each_with_object({}) do |dep, dep_hash| - next if dep.implicit? # Remove all implicit deps + spec.declared_deps.each_with_object({}) do |dep, dep_hash| + # Implicit dependencies are only needed when installing from source + # since they are only used to download and unpack source files. + # @see DependencyCollector + next if dep.implicit? - dep_hash[dep.name] = {}.tap do |info| - info[:tags] = dep.tags if dep.tags.present? - info[:uses_from_macos] = dep.bounds.presence if dep.uses_from_macos? - end.presence + metadata_hash = {} + metadata_hash[:tags] = dep.tags if dep.tags.present? + metadata_hash[:uses_from_macos] = dep.bounds.presence if dep.uses_from_macos? + + dep_hash[dep.name] = metadata_hash.presence end end diff --git a/Library/Homebrew/test/api/internal_tap_json/formula_spec.rb b/Library/Homebrew/test/api/internal_tap_json/formula_spec.rb index b6a495922c..2cfe78426d 100644 --- a/Library/Homebrew/test/api/internal_tap_json/formula_spec.rb +++ b/Library/Homebrew/test/api/internal_tap_json/formula_spec.rb @@ -107,7 +107,6 @@ RSpec.describe "Internal Tap JSON -- Formula", type: :system do "ruby_source_path" => "Formula/p/ponyc.rb", "tap" => "homebrew/core", "tap_git_head" => tap_git_head, - # TODO: improve this API before we ship internal API v3 to users "uses_from_macos" => [{ "llvm"=>[:build, :test] }, "zlib"], "uses_from_macos_bounds" => [{}, {}], "versions" => { "bottle"=>true, "head"=>nil, "stable"=>"0.58.1" }, @@ -117,6 +116,26 @@ RSpec.describe "Internal Tap JSON -- Formula", type: :system do } end + let(:inko_metadata) do + { + "desc" => "Safe and concurrent object-oriented programming language", + "full_name" => "inko", + "homepage" => "https://inko-lang.org/", + "license" => "MPL-2.0", + "name" => "inko", + "ruby_source_path" => "Formula/i/inko.rb", + "tap" => "homebrew/core", + "tap_git_head" => tap_git_head, + "dependencies" => ["llvm@15", "zstd"], + "uses_from_macos" => ["libffi", "ruby"], + "uses_from_macos_bounds" => [{ since: :catalina }, { since: :sierra }], + "versions" => { "bottle"=>true, "head"=>"HEAD", "stable"=>"0.14.0" }, + "ruby_source_checksum" => { + "sha256" => "843f6b5652483b971c83876201d68c95d5f32e67e55a75ac7c95d68c4350aa1c", + }, + } + end + it "loads fennel" do fennel = Formulary.factory("fennel") expect(fennel.to_hash).to include(**fennel_metadata) @@ -141,6 +160,11 @@ RSpec.describe "Internal Tap JSON -- Formula", type: :system do ponyc = Formulary.factory("ponyc-lang") expect(ponyc.to_hash).to include(**ponyc_metadata) end + + it "loads ink" do + inko = Formulary.factory("inko") + expect(inko.to_hash).to include(**inko_metadata) + end end end end diff --git a/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json index 9074219a04..b704184417 100644 --- a/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json +++ b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core.json @@ -45,6 +45,105 @@ "lua": null } }, + "inko": { + "desc": "Safe and concurrent object-oriented programming language", + "license": "MPL-2.0", + "homepage": "https://inko-lang.org/", + "urls": { + "stable": { + "url": "https://releases.inko-lang.org/0.14.0.tar.gz", + "checksum": "4e2c82911d6026f76c42ccc164dc45b1b5e331db2e9557460d9319d682668e65" + }, + "head": { + "url": "https://github.com/inko-lang/inko.git", + "branch": "main" + } + }, + "post_install_defined": false, + "ruby_source_path": "Formula/i/inko.rb", + "ruby_source_sha256": "843f6b5652483b971c83876201d68c95d5f32e67e55a75ac7c95d68c4350aa1c", + "version": "0.14.0", + "bottle": { + "files": { + "arm64_sonoma": { + "cellar": ":any", + "sha256": "f6ff66fdfb3aac69263c32a8a29d13e9d28a80ae33807f34460e55d8c1b228c6" + }, + "arm64_ventura": { + "cellar": ":any", + "sha256": "be59d916d29d85bb8bc4474eb1c7d42a56236835c3c21b0e36fb9e9df0a25e6e" + }, + "arm64_monterey": { + "cellar": ":any", + "sha256": "9522c1f89b997dedaa3167ce4dbfa4a2d8c660acddecd32a99a515922e851b52" + }, + "sonoma": { + "cellar": ":any", + "sha256": "8e32d823ce9712ae2d5a2b9cbe0c9b727223098b3e66b003da087032be9f6818" + }, + "ventura": { + "cellar": ":any", + "sha256": "178865db1e2b60b4085a2465e8a3879794030a6d22c99b58c95e4bdf5418ef1b" + }, + "monterey": { + "cellar": ":any", + "sha256": "6ef924939c42d7bb2ec4e0d65cf293147a593f829619928d2580b419ec19b26c" + }, + "x86_64_linux": { + "cellar": ":any_skip_relocation", + "sha256": "14a02c119990d6a17062290439ac74e6667b41dcb90b18cd90b36d2a09715e10" + } + } + }, + "dependencies": { + "coreutils": { + "tags": [ + "build" + ] + }, + "rust": { + "tags": [ + "build" + ] + }, + "llvm@15": null, + "zstd": null, + "libffi": { + "uses_from_macos": { + "since": "catalina" + } + }, + "ruby": { + "uses_from_macos": { + "since": "sierra" + } + } + }, + "head_dependencies": { + "coreutils": { + "tags": [ + "build" + ] + }, + "rust": { + "tags": [ + "build" + ] + }, + "llvm@15": null, + "zstd": null, + "libffi": { + "uses_from_macos": { + "since": "catalina" + } + }, + "ruby": { + "uses_from_macos": { + "since": "sierra" + } + } + } + }, "ponyc": { "desc": "Object-oriented, actor-model, capabilities-secure programming language", "license": "BSD-2-Clause", diff --git a/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core/Formula/i/inko.rb b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core/Formula/i/inko.rb new file mode 100644 index 0000000000..9f56b2d0f4 --- /dev/null +++ b/Library/Homebrew/test/support/fixtures/internal_tap_json/homebrew-core/Formula/i/inko.rb @@ -0,0 +1,45 @@ +class Inko < Formula + desc "Safe and concurrent object-oriented programming language" + homepage "https://inko-lang.org/" + url "https://releases.inko-lang.org/0.14.0.tar.gz" + sha256 "4e2c82911d6026f76c42ccc164dc45b1b5e331db2e9557460d9319d682668e65" + license "MPL-2.0" + head "https://github.com/inko-lang/inko.git", branch: "main" + + bottle do + sha256 cellar: :any, arm64_sonoma: "f6ff66fdfb3aac69263c32a8a29d13e9d28a80ae33807f34460e55d8c1b228c6" + sha256 cellar: :any, arm64_ventura: "be59d916d29d85bb8bc4474eb1c7d42a56236835c3c21b0e36fb9e9df0a25e6e" + sha256 cellar: :any, arm64_monterey: "9522c1f89b997dedaa3167ce4dbfa4a2d8c660acddecd32a99a515922e851b52" + sha256 cellar: :any, sonoma: "8e32d823ce9712ae2d5a2b9cbe0c9b727223098b3e66b003da087032be9f6818" + sha256 cellar: :any, ventura: "178865db1e2b60b4085a2465e8a3879794030a6d22c99b58c95e4bdf5418ef1b" + sha256 cellar: :any, monterey: "6ef924939c42d7bb2ec4e0d65cf293147a593f829619928d2580b419ec19b26c" + sha256 cellar: :any_skip_relocation, x86_64_linux: "14a02c119990d6a17062290439ac74e6667b41dcb90b18cd90b36d2a09715e10" + end + + depends_on "coreutils" => :build + depends_on "rust" => :build + depends_on "llvm@15" + depends_on "zstd" + + uses_from_macos "libffi", since: :catalina + uses_from_macos "ruby", since: :sierra + + def install + ENV.prepend_path "PATH", Formula["coreutils"].opt_libexec/"gnubin" + system "make", "build", "PREFIX=#{prefix}" + system "make", "install", "PREFIX=#{prefix}" + end + + test do + (testpath/"hello.inko").write <<~EOS + import std.stdio.STDOUT + + class async Main { + fn async main { + STDOUT.new.print('Hello, world!') + } + } + EOS + assert_equal "Hello, world!\n", shell_output("#{bin}/inko run hello.inko") + end +end