Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ def audit_min_os
cask_min_os = [on_system_block_min_os, cask.depends_on.macos&.minimum_version].compact.max
odebug "Declared minimum OS version: #{cask_min_os&.to_sym}"
return if cask_min_os&.to_sym == min_os.to_sym
return if cask.on_system_blocks_exist? &&
return if cask.uses_on_system.present? &&
OnSystem.arch_condition_met?(:arm) &&
cask_min_os.present? &&
cask_min_os < MacOSVersion.new("11")
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/cask/cask.rb
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ def to_hash_with_variations
hash = to_h
variations = {}

if @dsl.on_system_blocks_exist?
if @dsl.uses_on_system.present?
begin
OnSystem::VALID_OS_ARCH_TAGS.each do |bottle_tag|
next if bottle_tag.linux? && @dsl.os.nil?
Expand Down
31 changes: 21 additions & 10 deletions Library/Homebrew/cask/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ class DSL
:no_autobump!,
:autobump?,
:no_autobump_message,
:on_system_blocks_exist?,
:on_system_block_min_os,
:depends_on_set_in_block?,
:on_system_block_min_os,
:uses_on_system,
*ORDINARY_ARTIFACT_CLASSES.map(&:dsl_key),
*ACTIVATABLE_ARTIFACT_CLASSES.map(&:dsl_key),
*ARTIFACT_BLOCK_CLASSES.flat_map { |klass| [klass.dsl_key, klass.uninstall_dsl_key] },
Expand All @@ -121,6 +121,11 @@ class DSL
:disable_date, :disable_reason, :disable_replacement_cask,
:disable_replacement_formula, :on_system_block_min_os

# A `UsesOnSystem` object that contains boolean instance variables
# indicating whether the cask uses specific on_system methods.
sig { returns(OnSystem::UsesOnSystem) }
attr_reader :uses_on_system

sig { params(cask: Cask).void }
def initialize(cask)
# NOTE: Variables set by `set_unique_stanza` must be initialized to `nil`.
Expand Down Expand Up @@ -153,13 +158,13 @@ def initialize(cask)
@name = T.let([], T::Array[String])
@autobump = T.let(true, T::Boolean)
@no_autobump_defined = T.let(false, T::Boolean)
@on_system_blocks_exist = T.let(false, T::Boolean)
@os = T.let(nil, T.nilable(String))
@on_system_block_min_os = T.let(nil, T.nilable(MacOSVersion))
@sha256 = T.let(nil, T.nilable(T.any(Checksum, Symbol)))
@staged_path = T.let(nil, T.nilable(Pathname))
@token = T.let(cask.token, String)
@url = T.let(nil, T.nilable(URL))
@uses_on_system = T.let(OnSystem::UsesOnSystem.new, OnSystem::UsesOnSystem)
@version = T.let(nil, T.nilable(DSL::Version))
end

Expand All @@ -175,9 +180,6 @@ def disabled? = @disabled
sig { returns(T::Boolean) }
def livecheck_defined? = @livecheck_defined

sig { returns(T::Boolean) }
def on_system_blocks_exist? = @on_system_blocks_exist

# Specifies the cask's name.
#
# NOTE: Multiple names can be specified.
Expand Down Expand Up @@ -392,8 +394,15 @@ def sha256(arg = nil, arm: nil, intel: nil, x86_64: nil, x86_64_linux: nil, arm6

x86_64 ||= intel if intel.present? && x86_64.nil?
set_unique_stanza(:sha256, should_return) do
if arm.present? || x86_64.present? || x86_64_linux.present? || arm64_linux.present?
@on_system_blocks_exist = true
@uses_on_system.arm = true if arm.present?
@uses_on_system.intel = true if x86_64.present?
if x86_64_linux.present?
@uses_on_system.intel = true
@uses_on_system.linux = true
end
if arm64_linux.present?
@uses_on_system.arm = true
@uses_on_system.linux = true
end

val = arg || on_system_conditional(
Expand Down Expand Up @@ -424,7 +433,8 @@ def arch(arm: nil, intel: nil)
should_return = arm.nil? && intel.nil?

set_unique_stanza(:arch, should_return) do
@on_system_blocks_exist = true
@uses_on_system.arm = true if arm
@uses_on_system.intel = true if intel

on_arch_conditional(arm:, intel:)
end
Expand All @@ -449,7 +459,8 @@ def os(macos: nil, linux: nil)
should_return = macos.nil? && linux.nil?

set_unique_stanza(:os, should_return) do
@on_system_blocks_exist = true
@uses_on_system.macos = true if macos
@uses_on_system.linux = true if linux

on_system_conditional(macos:, linux:)
end
Expand Down
4 changes: 2 additions & 2 deletions Library/Homebrew/cask/dsl/depends_on.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ def macos=(*args)
elsif MacOSVersion::SYMBOLS.key?(args.first)
MacOSRequirement.new([args.first], comparator: "==")
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*:(?<version>\S+)\s*$/.match(first_arg))
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: md[:comparator])
MacOSRequirement.new([T.must(md[:version]).to_sym], comparator: T.must(md[:comparator]))
elsif (md = /^\s*(?<comparator><|>|[=<>]=)\s*(?<version>\S+)\s*$/.match(first_arg))
MacOSRequirement.new([md[:version]], comparator: md[:comparator])
MacOSRequirement.new([md[:version]], comparator: T.must(md[:comparator]))
# This is not duplicate of the first case: see `args.first` and a different comparator.
else # rubocop:disable Lint/DuplicateBranch
MacOSRequirement.new([args.first], comparator: "==")
Expand Down
6 changes: 3 additions & 3 deletions Library/Homebrew/dev-cmd/bump-cask-pr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def generate_system_options(cask)
# to on_system blocks referencing macOS versions.
os_values = []
arch_values = depends_on_archs.presence || []
if cask.on_system_blocks_exist?
if cask.uses_on_system.present?
OnSystem::BASE_OS_OPTIONS.each do |os|
os_values << if os == :macos
(current_os_is_macos ? current_os : newest_macos)
Expand Down Expand Up @@ -235,7 +235,7 @@ def replace_version_and_checksum(cask, new_hash, new_version, replacement_pairs)
old_cask = begin
Cask::CaskLoader.load(cask.sourcefile_path)
rescue Cask::CaskInvalidError, Cask::CaskUnreadableError
raise unless cask.on_system_blocks_exist?
raise unless cask.uses_on_system.present?
end
next if old_cask.nil?

Expand All @@ -262,7 +262,7 @@ def replace_version_and_checksum(cask, new_hash, new_version, replacement_pairs)
replacement_pairs << [/"#{old_hash}"/, ":no_check"] if old_hash != :no_check
elsif old_hash == :no_check && new_hash != :no_check
replacement_pairs << [":no_check", "\"#{new_hash}\""] if new_hash.is_a?(String)
elsif new_hash && !cask.on_system_blocks_exist? && cask.languages.empty?
elsif new_hash && cask.uses_on_system.blank? && cask.languages.empty?
replacement_pairs << [old_hash.to_s, new_hash.to_s]
elsif old_hash != :no_check
opoo "Multiple checksum replacements required; ignoring specified `--sha256` argument." if new_hash
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/dev-cmd/bump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
).returns(VersionBumpInfo)
}
def retrieve_versions_by_arch(formula_or_cask:, repositories:, name:)
is_cask_with_blocks = formula_or_cask.is_a?(Cask::Cask) && formula_or_cask.on_system_blocks_exist?
is_cask_with_blocks = formula_or_cask.is_a?(Cask::Cask) && formula_or_cask.uses_on_system.present?

Check warning on line 308 in Library/Homebrew/dev-cmd/bump.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/dev-cmd/bump.rb#L308

Added line #L308 was not covered by tests
type, version_name = if formula_or_cask.is_a?(Formula)
[:formula, "formula version:"]
else
Expand Down
133 changes: 98 additions & 35 deletions Library/Homebrew/extend/on_system.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,64 @@
# typed: true # rubocop:todo Sorbet/StrictSigil
# typed: strict
# frozen_string_literal: true

require "requirements/macos_requirement"
require "simulate_system"

module OnSystem
ARCH_OPTIONS = [:intel, :arm].freeze
BASE_OS_OPTIONS = [:macos, :linux].freeze
ALL_OS_OPTIONS = [*MacOSVersion::SYMBOLS.keys, :linux].freeze
ALL_OS_ARCH_COMBINATIONS = ALL_OS_OPTIONS.product(ARCH_OPTIONS).freeze
ARCH_OPTIONS = T.let([:intel, :arm].freeze, T::Array[Symbol])
BASE_OS_OPTIONS = T.let([:macos, :linux].freeze, T::Array[Symbol])
ALL_OS_OPTIONS = T.let([*MacOSVersion::SYMBOLS.keys, :linux].freeze, T::Array[Symbol])
ALL_OS_ARCH_COMBINATIONS = T.let(
ALL_OS_OPTIONS.product(ARCH_OPTIONS).freeze,
T::Array[[Symbol, Symbol]],
)

VALID_OS_ARCH_TAGS = T.let(
ALL_OS_ARCH_COMBINATIONS.filter_map do |os, arch|
tag = Utils::Bottles::Tag.new(system: os, arch:)
next unless tag.valid_combination?

tag
end.freeze,
T::Array[Utils::Bottles::Tag],
)

class UsesOnSystem < T::Struct
prop :arm, T::Boolean, default: false
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
alias linux? linux
alias macos? macos

# Whether the object has only default values.
sig { returns(T::Boolean) }
def empty?
!@arm && !@intel && !@linux && !@macos && @macos_requirements.empty?
end

VALID_OS_ARCH_TAGS = ALL_OS_ARCH_COMBINATIONS.filter_map do |os, arch|
tag = Utils::Bottles::Tag.new(system: os, arch:)
next unless tag.valid_combination?
# Whether the object has any non-default values.
sig { returns(T::Boolean) }
def present? = !empty?
end

tag
end.freeze
# 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)
Expand All @@ -33,16 +77,17 @@ def self.os_condition_met?(os_name, or_condition = nil)
raise ArgumentError, "Invalid OS `or_*` condition: #{or_condition.inspect}"
end

return false if Homebrew::SimulateSystem.simulating_or_running_on_linux?

base_os = MacOSVersion.from_symbol(os_name)
current_os = if Homebrew::SimulateSystem.current_os == :macos
current_os_symbol = Homebrew::SimulateSystem.current_os
current_os = if current_os_symbol == :macos
# Assume the oldest macOS version when simulating a generic macOS version
# Version::NULL is always treated as less than any other version.
Version::NULL
elsif MacOSVersion::SYMBOLS.key?(current_os_symbol)
MacOSVersion.from_symbol(current_os_symbol)
else
MacOSVersion.from_symbol(Homebrew::SimulateSystem.current_os)
return false
end
base_os = MacOSVersion.from_symbol(os_name)

return current_os >= base_os if or_condition == :or_newer
return current_os <= base_os if or_condition == :or_older
Expand All @@ -55,15 +100,16 @@ def self.condition_from_method_name(method_name)
method_name.to_s.sub(/^on_/, "").to_sym
end

sig { params(base: Class).void }
sig { params(base: T::Class[T.anything]).void }
def self.setup_arch_methods(base)
ARCH_OPTIONS.each do |arch|
base.define_method(:"on_#{arch}") do |&block|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.send(:"#{arch}=", true)

return unless OnSystem.arch_condition_met? OnSystem.condition_from_method_name(T.must(__method__))

@called_in_on_system_block = true
@called_in_on_system_block = T.let(true, T.nilable(T::Boolean))
result = block.call
@called_in_on_system_block = false

Expand All @@ -72,7 +118,9 @@ def self.setup_arch_methods(base)
end

base.define_method(:on_arch_conditional) do |arm: nil, intel: nil|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.arm = true if arm
@uses_on_system.intel = true if intel

if OnSystem.arch_condition_met? :arm
arm
Expand All @@ -82,11 +130,12 @@ def self.setup_arch_methods(base)
end
end

sig { params(base: Class).void }
sig { params(base: T::Class[T.anything]).void }
def self.setup_base_os_methods(base)
BASE_OS_OPTIONS.each do |base_os|
base.define_method(:"on_#{base_os}") do |&block|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.send(:"#{base_os}=", true)

return unless OnSystem.os_condition_met? OnSystem.condition_from_method_name(T.must(__method__))

Expand All @@ -99,7 +148,9 @@ def self.setup_base_os_methods(base)
end

base.define_method(:on_system) do |linux, macos:, &block|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.linux = true
@uses_on_system.macos = true

raise ArgumentError, "The first argument to `on_system` must be `:linux`" if linux != :linux

Expand All @@ -108,6 +159,9 @@ def self.setup_base_os_methods(base)
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
Expand All @@ -118,7 +172,9 @@ def self.setup_base_os_methods(base)
end

base.define_method(:on_system_conditional) do |macos: nil, linux: nil|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.macos = true if macos
@uses_on_system.linux = true if linux

if OnSystem.os_condition_met?(:macos) && macos.present?
macos
Expand All @@ -128,20 +184,27 @@ def self.setup_base_os_methods(base)
end
end

sig { params(base: Class).void }
sig { params(base: T::Class[T.anything]).void }
def self.setup_macos_methods(base)
MacOSVersion::SYMBOLS.each_key do |os_name|
base.define_method(:"on_#{os_name}") do |or_condition = nil, &block|
@on_system_blocks_exist = true
@uses_on_system ||= T.let(OnSystem::UsesOnSystem.new, T.nilable(OnSystem::UsesOnSystem))
@uses_on_system.macos = true

os_condition = OnSystem.condition_from_method_name T.must(__method__)
return unless OnSystem.os_condition_met? os_condition, or_condition

@on_system_block_min_os = if or_condition == :or_older
@called_in_on_system_block ? @on_system_block_min_os : MacOSVersion.new(HOMEBREW_MACOS_OLDEST_ALLOWED)
else
MacOSVersion.from_symbol(os_condition)
end
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(
if or_condition == :or_older
@called_in_on_system_block ? @on_system_block_min_os : MacOSVersion.new(HOMEBREW_MACOS_OLDEST_ALLOWED)
else
MacOSVersion.from_symbol(os_condition)
end,
T.nilable(MacOSVersion),
)
@called_in_on_system_block = true
result = block.call
@called_in_on_system_block = false
Expand All @@ -151,13 +214,13 @@ def self.setup_macos_methods(base)
end
end

sig { params(_base: Class).void }
sig { params(_base: T::Class[T.anything]).void }
def self.included(_base)
raise "Do not include `OnSystem` directly. Instead, include `OnSystem::MacOSAndLinux` or `OnSystem::MacOSOnly`"
end

module MacOSAndLinux
sig { params(base: Class).void }
sig { params(base: T::Class[T.anything]).void }
def self.included(base)
OnSystem.setup_arch_methods(base)
OnSystem.setup_base_os_methods(base)
Expand All @@ -166,7 +229,7 @@ def self.included(base)
end

module MacOSOnly
sig { params(base: Class).void }
sig { params(base: T::Class[T.anything]).void }
def self.included(base)
OnSystem.setup_arch_methods(base)
OnSystem.setup_macos_methods(base)
Expand Down
Loading
Loading