UsesOnSystem: Collect macOS requirements

When determining macOS requirements for a cask, we may need to
reference requirements from related on_system blocks (e.g.,
`on_monterey :or_older`, `on_ventura`, `on_sonoma :or_newer`) when
`depends_on macos` isn't adequate.

Sometimes casks specify different `depends_on macos` values in macOS
on_system blocks but that value is only set when the cask is loaded
in an environment that satisfies the on_system block's requirements.
There are other casks that contain macOS on_system blocks and use
`depends_on macos` outside of the on_system blocks but it may only
use the macOS version of the lowest on_system block (e.g.,
`>= :monterey`), which isn't sufficient if the cask's values vary
based on macOS version.

To be able to simulate macOS versions that meet the requirements of
all the on_system blocks in a cask, we need to collect the macOS
requirements in a way that doesn't require OS simulation. This is also
something that's easy to do in on_system methods, so this adds a
`macos_requirements` array to `UsesOnSystem`, containing
`MacOSRequirement` objects created from the cask's macOS on_system
block conditions.
This commit is contained in:
Sam Ford 2025-04-11 16:42:31 -04:00
parent 3df8f70511
commit 01825acabd
No known key found for this signature in database
GPG Key ID: 7AF5CBEE1DD6F76D
4 changed files with 69 additions and 1 deletions

View File

@ -1,6 +1,7 @@
# typed: strict
# frozen_string_literal: true
require "requirements/macos_requirement"
require "simulate_system"
module OnSystem
@ -27,6 +28,7 @@ module OnSystem
prop :intel, T::Boolean, default: false
prop :linux, T::Boolean, default: false
prop :macos, T::Boolean, default: false
prop :macos_requirements, T::Set[MacOSRequirement], default: Set[]
alias arm? arm
alias intel? intel
@ -36,7 +38,7 @@ module OnSystem
# Whether the object has only default values.
sig { returns(T::Boolean) }
def empty?
!@arm && !@intel && !@linux && !@macos
!@arm && !@intel && !@linux && !@macos && @macos_requirements.empty?
end
# Whether the object has any non-default values.
@ -44,6 +46,20 @@ module OnSystem
def present? = !empty?
end
# Converts an `or_condition` value to a suitable `MacOSRequirements`
# `comparator` string, defaulting to `==` if the provided argument is `nil`.
sig { params(symbol: T.nilable(Symbol)).returns(String) }
def self.comparator_from_or_condition(symbol)
case symbol
when :or_newer
">="
when :or_older
"<="
else
"=="
end
end
sig { params(arch: Symbol).returns(T::Boolean) }
def self.arch_condition_met?(arch)
raise ArgumentError, "Invalid arch condition: #{arch.inspect}" if ARCH_OPTIONS.exclude?(arch)
@ -142,6 +158,9 @@ module OnSystem
else
[macos.to_sym, nil]
end
comparator = OnSystem.comparator_from_or_condition(or_condition)
@uses_on_system.macos_requirements << MacOSRequirement.new([os_version], comparator:)
return if !OnSystem.os_condition_met?(os_version, or_condition) && !OnSystem.os_condition_met?(:linux)
@called_in_on_system_block = true
@ -172,6 +191,9 @@ module OnSystem
@uses_on_system.macos = true
os_condition = OnSystem.condition_from_method_name T.must(__method__)
comparator = OnSystem.comparator_from_or_condition(or_condition)
@uses_on_system.macos_requirements << MacOSRequirement.new([os_condition], comparator:)
return unless OnSystem.os_condition_met? os_condition, or_condition
@on_system_block_min_os = T.let(

View File

@ -855,6 +855,12 @@ RSpec.describe Cask::DSL, :cask do
it "sets @uses_on_system.macos to true" do
expect(cask.uses_on_system.macos?).to be true
end
it "sets @uses_on_system.macos_requirements using macos argument" do
expect(cask.uses_on_system.macos_requirements).to eq(Set[
MacOSRequirement.new([:sequoia], comparator: ">="),
])
end
end
context "when cask uses on_system_conditional" do
@ -890,6 +896,14 @@ RSpec.describe Cask::DSL, :cask do
it "sets @uses_on_system.macos to true" do
expect(cask.uses_on_system.macos?).to be true
end
it "sets @uses_on_system.macos_requirements using on_* macos version conditions" do
expect(cask.uses_on_system.macos_requirements).to eq(Set[
MacOSRequirement.new([:big_sur], comparator: "<="),
MacOSRequirement.new([:monterey], comparator: "=="),
MacOSRequirement.new([:ventura], comparator: ">="),
])
end
end
end
end

View File

@ -52,6 +52,24 @@ RSpec.describe OnSystem do
end
end
describe "::comparator_from_or_condition" do
it 'returns ">=" for `:or_newer` symbol' do
expect(described_class.comparator_from_or_condition(:or_newer)).to eq(">=")
end
it 'returns "<=" for `:or_older` symbol' do
expect(described_class.comparator_from_or_condition(:or_older)).to eq("<=")
end
it 'returns "==" for an unsupported symbol' do
expect(described_class.comparator_from_or_condition(:nonexistent)).to eq("==")
end
it 'returns "==" for `nil`' do
expect(described_class.comparator_from_or_condition(nil)).to eq("==")
end
end
describe "::arch_condition_met?" do
it "returns true if current arch equals provided arch" do
Homebrew::SimulateSystem.with(arch: :arm) do

View File

@ -2073,6 +2073,12 @@ RSpec.describe Formula do
it "sets @uses_on_system.macos to true" do
expect(f_on_system.uses_on_system.macos?).to be true
end
it "sets @uses_on_system.macos_requirements using macos argument" do
expect(f_on_system.uses_on_system.macos_requirements).to eq(Set[
MacOSRequirement.new([:sequoia], comparator: ">="),
])
end
end
context "when formula uses on_system_conditional" do
@ -2131,6 +2137,14 @@ RSpec.describe Formula do
it "sets @uses_on_system.macos to true" do
expect(f_on_macos_versions.uses_on_system.macos?).to be true
end
it "sets @uses_on_system.macos_requirements using on_* macos version conditions" do
expect(f_on_macos_versions.uses_on_system.macos_requirements).to eq(Set[
MacOSRequirement.new([:big_sur], comparator: "<="),
MacOSRequirement.new([:monterey], comparator: "=="),
MacOSRequirement.new([:ventura], comparator: ">="),
])
end
end
end