Merge HeadVersion and NullVersion into Version.

This commit is contained in:
Markus Reiter 2023-04-29 23:39:26 +02:00
parent b05de929c6
commit a1efaf1864
No known key found for this signature in database
GPG Key ID: 245293B51702655B
9 changed files with 151 additions and 194 deletions

View File

@ -7,10 +7,10 @@ module Language
# @api public
module Python
def self.major_minor_version(python)
version = /\d\.\d+/.match `#{python} --version 2>&1`
version = `#{python} --version 2>&1`.chomp[/(\d\.\d+)/, 1]
return unless version
Version.create(version.to_s)
Version.new(version)
end
def self.homebrew_site_packages(python = "python3.7")

View File

@ -1,6 +1,7 @@
# typed: true
# frozen_string_literal: true
require "os/mac/version"
require "utils"
module OS
@ -53,11 +54,11 @@ module OS
raise "Loaded OS::Linux on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"]
def self.version
::Version::NULL
Version::NULL
end
def self.full_version
::Version::NULL
Version::NULL
end
def self.languages

View File

@ -30,18 +30,29 @@ module OS
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) }
def <=>(other)
@comparison_cache.fetch(other) do
return @comparison_cache[other] if @comparison_cache.key?(other)
result = case other
when Symbol
if MacOSVersions::SYMBOLS.key?(other) && to_sym == other
0
else
v = MacOSVersions::SYMBOLS.fetch(other) { other.to_s }
@comparison_cache[other] = super(::Version.new(v))
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)
@ -52,12 +63,24 @@ module OS
sig { returns(Symbol) }
def to_sym
@to_sym ||= MacOSVersions::SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
return @sym if defined?(@sym)
sym = MacOSVersions::SYMBOLS.invert.fetch(strip_patch.to_s, :dunno)
@sym = sym unless frozen?
sym
end
sig { returns(String) }
def pretty_name
@pretty_name ||= to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
return @pretty_name if defined?(@pretty_name)
pretty_name = to_sym.to_s.split("_").map(&:capitalize).join(" ").freeze
@pretty_name = pretty_name unless frozen?
pretty_name
end
sig { returns(T::Boolean) }
@ -78,6 +101,8 @@ module OS
# For {OS::Mac::Version} compatibility.
sig { returns(T::Boolean) }
def requires_nehalem_cpu?
return false if null?
unless Hardware::CPU.intel?
raise "Unexpected architecture: #{Hardware::CPU.arch}. This only works with Intel architecture."
end
@ -90,6 +115,9 @@ module OS
alias requires_sse41? requires_nehalem_cpu?
alias requires_sse42? requires_nehalem_cpu?
alias requires_popcnt? requires_nehalem_cpu?
# Represents the absence of a version.
NULL = Version.new("10.0").tap { |v| v.instance_variable_set(:@version, nil) }.freeze
end
end
end

View File

@ -190,6 +190,7 @@ class Resource < Downloadable
@download_strategy = @url.download_strategy
end
sig { params(val: T.nilable(T.any(String, Version))).returns(T.nilable(Version)) }
def version(val = nil)
return super() if val.nil?

View File

@ -315,11 +315,11 @@ class Tab
end
def stable_version
Version.create(versions["stable"]) if versions["stable"]
versions["stable"]&.then(&Version.method(:new))
end
def head_version
Version.create(versions["head"]) if versions["head"]
versions["head"]&.then(&Version.method(:new))
end
def version_scheme

View File

@ -15,16 +15,11 @@ describe OS::Mac::Version do
expect(version).to be < :catalina
end
specify "comparison with Fixnum" do
specify "comparison with Integer" do
expect(version).to be > 10
expect(version).to be < 11
end
specify "comparison with Float" do
expect(version).to be > 10.13
expect(version).to be < 10.15
end
specify "comparison with String" do
expect(version).to be > "10.3"
expect(version).to be == "10.14"

View File

@ -2,8 +2,6 @@
# frozen_string_literal: true
require "pkg_version"
require "version/head"
require "version/null"
require "version/parser"
# A formula's version.
@ -343,37 +341,25 @@ class Version
parse(specs.fetch(:tag, url), detected_from_url: true)
end
# TODO: `odeprecate` this and just use `Version.new`.
sig { params(val: String).returns(Version) }
def self.create(val)
raise TypeError, "Version value must be a string; got a #{val.class} (#{val})" unless val.respond_to?(:to_str)
if val.to_str.start_with?("HEAD")
HeadVersion.new(val)
else
Version.new(val)
end
new(val)
end
sig { params(spec: T.any(String, Pathname), detected_from_url: T::Boolean).returns(Version) }
sig { params(spec: T.any(String, Pathname), detected_from_url: T::Boolean).returns(T.attached_class) }
def self.parse(spec, detected_from_url: false)
version = _parse(spec, detected_from_url: detected_from_url)
version.nil? ? NULL : new(version, detected_from_url: detected_from_url)
end
sig { params(spec: T.any(String, Pathname), detected_from_url: T::Boolean).returns(T.nilable(String)) }
def self._parse(spec, detected_from_url:)
spec = CGI.unescape(spec.to_s) if detected_from_url
spec = Pathname.new(spec) unless spec.is_a? Pathname
spec = Pathname(spec)
VERSION_PARSERS.each do |parser|
version = parser.parse(spec)
return version if version.present?
return new(version, detected_from_url: detected_from_url) if version.present?
end
nil
NULL
end
private_class_method :_parse
NUMERIC_WITH_OPTIONAL_DOTS = /(?:\d+(?:\.\d+)*)/.source.freeze
private_constant :NUMERIC_WITH_OPTIONAL_DOTS
@ -517,14 +503,36 @@ class Version
@detected_from_url
end
HEAD_VERSION_REGEX = /\AHEAD(?:-(?<commit>.*))?\Z/.freeze
private_constant :HEAD_VERSION_REGEX
# Check if this is a HEAD version.
sig { returns(T::Boolean) }
def head?
false
version&.match?(HEAD_VERSION_REGEX) || false
end
# Return the commit for a HEAD version.
sig { returns(T.nilable(String)) }
def commit
version&.match(HEAD_VERSION_REGEX)&.[](:commit)
end
# Update the commit for a HEAD version.
sig { params(commit: T.nilable(String)).void }
def update_commit(commit)
raise ArgumentError, "Cannot update commit for non-HEAD version." unless head?
@version = if commit
"HEAD-#{commit}"
else
"HEAD"
end
end
sig { returns(T::Boolean) }
def null?
false
@version.nil?
end
sig { params(comparator: String, other: Version).returns(T::Boolean) }
@ -542,16 +550,47 @@ class Version
sig { params(other: T.untyped).returns(T.nilable(Integer)) }
def <=>(other)
# Needed to retain API compatibility with older string comparisons
# for compiler versions, etc.
other = Version.new(other) if other.is_a? String
# Used by the *_build_version comparisons, which formerly returned Fixnum
other = Version.new(other.to_s) if other.is_a? Integer
return 1 if other.nil?
return 1 if other.respond_to?(:null?) && other.null?
other = case other
when String
if other.blank?
# Cannot compare `NULL` to empty string.
return if null?
return 1
end
# Needed to retain API compatibility with older string comparisons for compiler versions, etc.
Version.new(other)
when Integer
# Used by the `*_build_version` comparisons, which formerly returned an integer.
Version.new(other.to_s)
when Token
if other.null?
# Cannot compare `NULL` to `NULL`.
return if null?
return 1
end
Version.new(other.to_s)
when Version
if other.null?
# Cannot compare `NULL` to `NULL`.
return if null?
return 1
end
other
when nil
return 1
else
return
end
# All `other.null?` cases are handled at this point.
return -1 if null?
other = Version.new(other.to_s) if other.is_a? Token
return unless other.is_a?(Version)
return 0 if version == other.version
return 1 if head? && !other.head?
return -1 if !head? && other.head?
@ -585,41 +624,64 @@ class Version
0
end
sig { override.params(other: T.untyped).returns(T::Boolean) }
def ==(other)
# Makes sure that the same instance of Version::NULL
# will never equal itself; normally Comparable#==
# will return true for this regardless of the return
# value of #<=>
return false if null?
super
end
alias eql? ==
# @api public
sig { returns(T.nilable(Token)) }
def major
return NULL_TOKEN if null?
tokens.first
end
# @api public
sig { returns(T.nilable(Token)) }
def minor
return NULL_TOKEN if null?
tokens.second
end
# @api public
sig { returns(T.nilable(Token)) }
def patch
return NULL_TOKEN if null?
tokens.third
end
# @api public
sig { returns(T.self_type) }
def major_minor
self.class.new([major, minor].compact.join("."))
return self if null?
major_minor = T.must(tokens[0..1])
major_minor.empty? ? NULL : self.class.new(major_minor.join("."))
end
# @api public
sig { returns(T.self_type) }
def major_minor_patch
self.class.new([major, minor, patch].compact.join("."))
return self if null?
major_minor_patch = T.must(tokens[0..2])
major_minor_patch.empty? ? NULL : self.class.new(major_minor_patch.join("."))
end
sig { returns(T::Boolean) }
def empty?
version.empty?
version&.empty? || false
end
sig { returns(Integer) }
@ -629,6 +691,8 @@ class Version
sig { returns(Float) }
def to_f
return Float::NAN if null?
version.to_f
end
@ -639,10 +703,17 @@ class Version
sig { returns(String) }
def to_s
version.dup
version.to_s
end
alias to_str to_s
sig { returns(String) }
def inspect
return "#<Version::NULL>" if null?
super
end
sig { returns(T.self_type) }
def freeze
tokens # Determine and store tokens before freezing
@ -651,12 +722,12 @@ class Version
protected
sig { returns(String) }
sig { returns(T.nilable(String)) }
attr_reader :version
sig { returns(T::Array[Token]) }
def tokens
@tokens ||= tokenize
@tokens ||= version&.scan(SCAN_PATTERN)&.map { |token| Token.create(T.cast(token, String)) } || []
end
private
@ -666,8 +737,6 @@ class Version
(first > second) ? first : second
end
sig { returns(T::Array[Token]) }
def tokenize
version.scan(SCAN_PATTERN).map { |token| Token.create(T.cast(token, String)) }
end
# Represents the absence of a version.
NULL = Version.new("NULL").tap { |v| v.instance_variable_set(:@version, nil) }.freeze
end

View File

@ -1,33 +0,0 @@
# typed: true
# frozen_string_literal: true
class Version
# A formula's HEAD version.
# @see https://docs.brew.sh/Formula-Cookbook#unstable-versions-head Unstable versions (head)
#
# @api private
class HeadVersion < Version
sig { returns(T.nilable(String)) }
attr_reader :commit
def initialize(*)
super
@commit = @version[/^HEAD-(.+)$/, 1]
end
sig { params(commit: T.nilable(String)).void }
def update_commit(commit)
@commit = commit
@version = if commit
"HEAD-#{commit}"
else
"HEAD"
end
end
sig { returns(T::Boolean) }
def head?
true
end
end
end

View File

@ -1,104 +0,0 @@
# typed: true
# frozen_string_literal: true
require "singleton"
class Version
# A pseudo-version representing the absence of a version.
#
# @api private
class NullVersion < Version
include Comparable
include Singleton
sig { override.params(_other: T.untyped).returns(Integer) }
def <=>(_other)
-1
end
sig { override.params(_other: T.untyped).returns(T::Boolean) }
def eql?(_other)
# Makes sure that the same instance of Version::NULL
# will never equal itself; normally Comparable#==
# will return true for this regardless of the return
# value of #<=>
false
end
sig { override.returns(T::Boolean) }
def detected_from_url?
false
end
sig { override.returns(T::Boolean) }
def head?
false
end
sig { override.returns(T::Boolean) }
def null?
true
end
# For {OS::Mac::Version} compatibility.
sig { returns(T::Boolean) }
def requires_nehalem_cpu?
false
end
alias requires_sse4? requires_nehalem_cpu?
alias requires_sse41? requires_nehalem_cpu?
alias requires_sse42? requires_nehalem_cpu?
alias requires_popcnt? requires_nehalem_cpu?
alias outdated_release? requires_nehalem_cpu?
sig { override.returns(Token) }
def major
NULL_TOKEN
end
sig { override.returns(Token) }
def minor
NULL_TOKEN
end
sig { override.returns(Token) }
def patch
NULL_TOKEN
end
sig { override.returns(T.self_type) }
def major_minor
self
end
sig { override.returns(T.self_type) }
def major_minor_patch
self
end
sig { override.returns(Float) }
def to_f
Float::NAN
end
sig { override.returns(Integer) }
def to_i
0
end
sig { override.returns(String) }
def to_s
""
end
alias to_str to_s
sig { override.returns(String) }
def inspect
"#<Version::NULL>"
end
end
private_constant :NullVersion
# Represents the absence of a version.
NULL = NullVersion.instance.freeze
end