brew/Library/Homebrew/macos_version.rb
Mike McQuaid 435158884d
Move formula analytics pretty name logic to MacOSVersion.
This makes sense to centralize these so when we support new macOS
versions we can just add them to the
`VERSIONS_TO_ANALYTICS_PRETTY_NAMES` hash.
2025-08-18 16:57:20 +01:00

190 lines
5.0 KiB
Ruby

# typed: strong
# frozen_string_literal: true
require "version"
# A macOS version.
class MacOSVersion < Version
# Raised when a macOS version is unsupported.
class Error < RuntimeError
sig { returns(T.nilable(T.any(String, Symbol))) }
attr_reader :version
sig { params(version: T.nilable(T.any(String, Symbol))).void }
def initialize(version)
@version = version
super "unknown or unsupported macOS version: #{version.inspect}"
end
end
# NOTE: When removing symbols here, ensure that they are added
# to `DEPRECATED_MACOS_VERSIONS` in `MacOSRequirement`.
SYMBOLS = T.let({
tahoe: "26",
sequoia: "15",
sonoma: "14",
ventura: "13",
monterey: "12",
big_sur: "11",
catalina: "10.15",
mojave: "10.14",
high_sierra: "10.13",
sierra: "10.12",
el_capitan: "10.11",
}.freeze, T::Hash[Symbol, String])
# TODO: can be replaced with a call to `#pretty_name` once we remove support
# for El Capitan.
VERSIONS_TO_ANALYTICS_PRETTY_NAMES = T.let({
"26" => "macOS Tahoe (26)",
"15" => "macOS Sequoia (15)",
"14" => "macOS Sonoma (14)",
"13" => "macOS Ventura (13)",
"12" => "macOS Monterey (12)",
"11" => "macOS Big Sur (11)",
"10.16" => "macOS Big Sur (11)",
"10.15" => "macOS Catalina (10.15)",
"10.14" => "macOS Mojave (10.14)",
"10.13" => "macOS High Sierra (10.13)",
"10.12" => "macOS Sierra (10.12)",
"10.11" => "OS X El Capitan (10.11)",
}.freeze, T::Hash[String, String])
sig { params(version: String).returns(T.nilable(String)) }
def self.analytics_pretty_name(version)
VERSIONS_TO_ANALYTICS_PRETTY_NAMES.fetch(version) do
VERSIONS_TO_ANALYTICS_PRETTY_NAMES.find do |v, _|
version.start_with?(v)
end&.last
end
end
sig { params(macos_version: MacOSVersion).returns(Version) }
def self.kernel_major_version(macos_version)
version_major = macos_version.major.to_i
if version_major >= 26
Version.new((version_major - 1).to_s)
elsif version_major > 10
Version.new((version_major + 9).to_s)
else
version_minor = macos_version.minor.to_i
Version.new((version_minor + 4).to_s)
end
end
sig { params(version: Symbol).returns(T.attached_class) }
def self.from_symbol(version)
str = SYMBOLS.fetch(version) { raise MacOSVersion::Error, version }
new(str)
end
sig { params(version: T.nilable(String)).void }
def initialize(version)
raise MacOSVersion::Error, version unless /\A\d{2,}(?:\.\d+){0,2}\z/.match?(version)
super(T.must(version))
@comparison_cache = T.let({}, T::Hash[T.untyped, T.nilable(Integer)])
@pretty_name = T.let(nil, T.nilable(String))
@sym = T.let(nil, T.nilable(Symbol))
end
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
def <=>(other)
return @comparison_cache[other] if @comparison_cache.key?(other)
result = case other
when Symbol
if SYMBOLS.key?(other) && to_sym == other
0
else
v = SYMBOLS.fetch(other) { other.to_s }
super(v)
end
else
super
end
@comparison_cache[other] = result unless frozen?
result
end
sig { returns(T.self_type) }
def strip_patch
return self if null?
# Big Sur is 11.x but Catalina is 10.15.x.
if T.must(major) >= 11
self.class.new(major.to_s)
else
major_minor
end
end
sig { returns(Symbol) }
def to_sym
return @sym if @sym
sym = SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
@sym = sym unless frozen?
sym
end
sig { returns(String) }
def pretty_name
return @pretty_name if @pretty_name
pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
@pretty_name = pretty_name unless frozen?
pretty_name
end
sig { returns(String) }
def inspect
"#<#{self.class.name}: #{to_s.inspect}>"
end
sig { returns(T::Boolean) }
def outdated_release?
self < HOMEBREW_MACOS_OLDEST_SUPPORTED
end
sig { returns(T::Boolean) }
def prerelease?
self >= HOMEBREW_MACOS_NEWEST_UNSUPPORTED
end
sig { returns(T::Boolean) }
def unsupported_release?
outdated_release? || prerelease?
end
sig { returns(T::Boolean) }
def requires_nehalem_cpu?
return false if null?
require "hardware"
return Hardware.oldest_cpu(self) == :nehalem if Hardware::CPU.intel?
raise ArgumentError, "Unexpected architecture: #{Hardware::CPU.arch}. This only works with Intel architecture."
end
# https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
alias requires_sse4? requires_nehalem_cpu?
alias requires_sse41? requires_nehalem_cpu?
alias requires_sse42? requires_nehalem_cpu?
alias requires_popcnt? requires_nehalem_cpu?
# Represents the absence of a version.
#
# NOTE: Constructor needs to called with an arbitrary macOS-like version which is then set to `nil`.
NULL = T.let(MacOSVersion.new("10.0").tap do |v|
T.let(v, MacOSVersion).instance_variable_set(:@version, nil)
end.freeze, MacOSVersion)
end