-
Jack Nagel authored
Closes Homebrew/homebrew#18300. Closes Homebrew/homebrew#20891.
Jack Nagel authoredCloses Homebrew/homebrew#18300. Closes Homebrew/homebrew#20891.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
version.rb 6.77 KiB
class Version
include Comparable
class Token
include Comparable
attr_reader :value
def initialize(value)
@value = value
end
def inspect
"#<#{self.class} #{value.inspect}>"
end
def to_s
value.to_s
end
end
class NullToken < Token
def initialize(value=nil)
super
end
def <=>(other)
case other
when NumericToken
other.value == 0 ? 0 : -1
when AlphaToken, BetaToken, RCToken
1
else
-1
end
end
def inspect
"#<#{self.class}>"
end
end
NULL_TOKEN = NullToken.new
class StringToken < Token
PATTERN = /[a-z]+[0-9]+/i
def initialize(value)
@value = value.to_s
end
def <=>(other)
case other
when StringToken
value <=> other.value
when NumericToken, NullToken
-Integer(other <=> self)
end
end
end
class NumericToken < Token
PATTERN = /[0-9]+/i
def initialize(value)
@value = value.to_i
end
def <=>(other)
case other
when NumericToken
value <=> other.value
when StringToken
1
when NullToken
-Integer(other <=> self)
end
end
end
class CompositeToken < StringToken
def rev
value[/([0-9]+)/, 1]
end
end
class AlphaToken < CompositeToken
PATTERN = /a(?:lpha)?[0-9]+/i
def <=>(other)
case other
when AlphaToken
rev <=> other.rev
else
super
end
end
end
class BetaToken < CompositeToken
PATTERN = /b(?:eta)?[0-9]+/i
def <=>(other)
case other
when BetaToken
rev <=> other.rev
when AlphaToken
1
when RCToken, PatchToken
-1
else
super
end
end
end
class RCToken < CompositeToken
PATTERN = /rc[0-9]+/i
def <=>(other)
case other
when RCToken
rev <=> other.rev
when AlphaToken, BetaToken
1
when PatchToken
-1
else
super
end
end
end
class PatchToken < CompositeToken
PATTERN = /p[0-9]+/i
def <=>(other)
case other
when PatchToken
rev <=> other.rev
when AlphaToken, BetaToken, RCToken
1
else
super
end
end
end
def self.new_with_scheme(value, scheme)
if Class === scheme && scheme.ancestors.include?(Version)
scheme.new(value)
else
raise TypeError, "Unknown version scheme #{scheme.inspect}"
end
end
def self.detect(url, specs={})
if specs.has_key?(:tag)
new(specs[:tag][/((?:\d+\.)*\d+)/, 1], true)
else
parse(url)
end
end
def initialize(val, detected=false)
@version = val.to_s
@detected_from_url = detected
end
def detected_from_url?
@detected_from_url
end
def head?
@version == 'HEAD'
end
def <=>(other)
return unless Version === other
return 0 if head? && other.head?
return 1 if head? && !other.head?
return -1 if !head? && other.head?
max = [tokens.length, other.tokens.length].max
pad_to(max) <=> other.pad_to(max)
end
def to_s
@version.dup
end
alias_method :to_str, :to_s
protected
def pad_to(length)
nums, rest = tokens.partition { |t| NumericToken === t }
nums.concat([NULL_TOKEN]*(length - tokens.length)).concat(rest)
end
def tokens
@tokens ||= tokenize
end
def tokenize
@version.scan(
Regexp.union(
AlphaToken::PATTERN,
BetaToken::PATTERN,
RCToken::PATTERN,
PatchToken::PATTERN,
NumericToken::PATTERN,
StringToken::PATTERN
)
).map! do |token|
case token
when /\A#{AlphaToken::PATTERN}\z/o then AlphaToken
when /\A#{BetaToken::PATTERN}\z/o then BetaToken
when /\A#{RCToken::PATTERN}\z/o then RCToken
when /\A#{PatchToken::PATTERN}\z/o then PatchToken
when /\A#{NumericToken::PATTERN}\z/o then NumericToken
when /\A#{StringToken::PATTERN}\z/o then StringToken
end.new(token)
end
end
def self.parse spec
version = _parse(spec)
Version.new(version, true) unless version.nil?
end
def self._parse spec
spec = Pathname.new(spec) unless spec.is_a? Pathname
spec_s = spec.to_s
stem = if spec.directory?
spec.basename.to_s
elsif %r[((?:sourceforge.net|sf.net)/.*)/download$].match(spec_s)
Pathname.new(spec.dirname).stem
else
spec.stem
end
# GitHub tarballs
# e.g. https://github.com/foo/bar/tarball/v1.2.3
# e.g. https://github.com/sam-github/libnet/tarball/libnet-1.1.4
# e.g. https://github.com/isaacs/npm/tarball/v0.2.5-1
# e.g. https://github.com/petdance/ack/tarball/1.93_02
m = %r[github.com/.+/(?:zip|tar)ball/(?:v|\w+-)?((?:\d+[-._])+\d*)$].match(spec_s)
return m.captures.first unless m.nil?
# e.g. https://github.com/erlang/otp/tarball/OTP_R15B01 (erlang style)
m = /[-_]([Rr]\d+[AaBb]\d*(?:-\d+)?)/.match(spec_s)
return m.captures.first unless m.nil?
# e.g. perforce-2013.1.610569-x86_64
m = /-([\d\.]+-x86(_64)?)/.match(stem)
return m.captures.first unless m.nil?
# e.g. boost_1_39_0
m = /((?:\d+_)+\d+)$/.match(stem)
return m.captures.first.gsub('_', '.') unless m.nil?
# e.g. foobar-4.5.1-1
# e.g. ruby-1.9.1-p243
m = /-((?:\d+\.)*\d\.\d+-(?:p|rc|RC)?\d+)(?:[-._](?:bin|dist|stable|src|sources))?$/.match(stem)
return m.captures.first unless m.nil?
# e.g. lame-398-1
m = /-((?:\d)+-\d)/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.1
m = /-((?:\d+\.)*\d+)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.1b
m = /-((?:\d+\.)*\d+(?:[abc]|rc|RC)\d*)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.0-beta1, or foobar-4.50-beta
m = /-((?:\d+\.)*\d+-beta\d*)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar4.5.1
m = /((?:\d+\.)*\d+)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. foobar-4.5.0-bin
m = /-((?:\d+\.)+\d+[abc]?)[-._](?:bin|dist|stable|src|sources?)$/.match(stem)
return m.captures.first unless m.nil?
# e.g. dash_0.5.5.1.orig.tar.gz (Debian style)
m = /_((?:\d+\.)+\d+[abc]?)[.]orig$/.match(stem)
return m.captures.first unless m.nil?
# e.g. http://www.openssl.org/source/openssl-0.9.8s.tar.gz
m = /-v?([^-]+)/.match(stem)
return m.captures.first unless m.nil?
# e.g. astyle_1.23_macosx.tar.gz
m = /_([^_]+)/.match(stem)
return m.captures.first unless m.nil?
# e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
m = /\/(\d\.\d+)\//.match(spec_s)
return m.captures.first unless m.nil?
# e.g. http://www.ijg.org/files/jpegsrc.v8d.tar.gz
m = /\.v(\d+[a-z]?)/.match(stem)
return m.captures.first unless m.nil?
end
end