2013-06-05 21:52:48 -05:00
|
|
|
class Version
|
2012-08-18 13:34:31 -05:00
|
|
|
include Comparable
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class Token
|
|
|
|
include Comparable
|
2012-08-18 13:34:31 -05:00
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
attr_reader :value
|
2013-04-15 15:00:57 -05:00
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
def initialize(value)
|
|
|
|
@value = value
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
2014-07-01 15:07:06 -05:00
|
|
|
"#<#{self.class.name} #{value.inspect}>"
|
2013-06-05 21:52:48 -05:00
|
|
|
end
|
2013-06-05 23:30:55 -05:00
|
|
|
|
|
|
|
def to_s
|
|
|
|
value.to_s
|
|
|
|
end
|
2015-03-17 21:37:03 -04:00
|
|
|
|
|
|
|
def numeric?
|
|
|
|
false
|
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class NullToken < Token
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(value = nil)
|
2013-06-05 21:52:48 -05:00
|
|
|
super
|
|
|
|
end
|
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
case other
|
2015-03-17 21:37:03 -04:00
|
|
|
when NullToken
|
|
|
|
0
|
2013-06-05 21:52:48 -05:00
|
|
|
when NumericToken
|
|
|
|
other.value == 0 ? 0 : -1
|
|
|
|
when AlphaToken, BetaToken, RCToken
|
|
|
|
1
|
|
|
|
else
|
|
|
|
-1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
2014-07-01 15:07:06 -05:00
|
|
|
"#<#{self.class.name}>"
|
2013-06-05 21:52:48 -05:00
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
NULL_TOKEN = NullToken.new
|
|
|
|
|
|
|
|
class StringToken < Token
|
2014-01-11 19:59:26 -06:00
|
|
|
PATTERN = /[a-z]+[0-9]*/i
|
2013-01-15 18:20:52 -06:00
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
def initialize(value)
|
|
|
|
@value = value.to_s
|
|
|
|
end
|
2013-01-15 18:20:52 -06:00
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
def <=>(other)
|
|
|
|
case other
|
|
|
|
when StringToken
|
|
|
|
value <=> other.value
|
|
|
|
when NumericToken, NullToken
|
|
|
|
-Integer(other <=> self)
|
|
|
|
end
|
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
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
|
2015-03-17 21:37:03 -04:00
|
|
|
|
|
|
|
def numeric?
|
|
|
|
true
|
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class CompositeToken < StringToken
|
|
|
|
def rev
|
2014-10-06 13:55:23 -05:00
|
|
|
value[/[0-9]+/].to_i
|
2013-06-05 21:52:48 -05:00
|
|
|
end
|
|
|
|
end
|
2012-07-09 22:51:10 -05:00
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class AlphaToken < CompositeToken
|
2013-07-24 19:44:27 -05:00
|
|
|
PATTERN = /a(?:lpha)?[0-9]*/i
|
2013-06-05 21:52:48 -05:00
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
case other
|
|
|
|
when AlphaToken
|
|
|
|
rev <=> other.rev
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
2012-07-10 16:10:16 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class BetaToken < CompositeToken
|
2013-07-24 19:44:27 -05:00
|
|
|
PATTERN = /b(?:eta)?[0-9]*/i
|
2013-06-05 21:52:48 -05:00
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
case other
|
|
|
|
when BetaToken
|
|
|
|
rev <=> other.rev
|
|
|
|
when AlphaToken
|
|
|
|
1
|
|
|
|
when RCToken, PatchToken
|
|
|
|
-1
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
2012-07-09 22:51:10 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class RCToken < CompositeToken
|
2013-07-24 19:44:27 -05:00
|
|
|
PATTERN = /rc[0-9]*/i
|
2013-06-05 21:52:48 -05:00
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
case other
|
|
|
|
when RCToken
|
|
|
|
rev <=> other.rev
|
|
|
|
when AlphaToken, BetaToken
|
|
|
|
1
|
|
|
|
when PatchToken
|
|
|
|
-1
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
2012-07-09 23:00:40 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
class PatchToken < CompositeToken
|
2013-07-24 19:44:27 -05:00
|
|
|
PATTERN = /p[0-9]*/i
|
2013-06-05 21:52:48 -05:00
|
|
|
|
|
|
|
def <=>(other)
|
|
|
|
case other
|
|
|
|
when PatchToken
|
|
|
|
rev <=> other.rev
|
|
|
|
when AlphaToken, BetaToken, RCToken
|
|
|
|
1
|
|
|
|
else
|
|
|
|
super
|
|
|
|
end
|
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2014-04-03 11:19:57 -05:00
|
|
|
SCAN_PATTERN = Regexp.union(
|
|
|
|
AlphaToken::PATTERN,
|
|
|
|
BetaToken::PATTERN,
|
|
|
|
RCToken::PATTERN,
|
|
|
|
PatchToken::PATTERN,
|
|
|
|
NumericToken::PATTERN,
|
|
|
|
StringToken::PATTERN
|
|
|
|
)
|
|
|
|
|
2015-01-07 15:21:20 -05:00
|
|
|
class FromURL < Version
|
|
|
|
def detected_from_url?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-03-14 01:58:17 -04:00
|
|
|
def self.detect(url, specs)
|
2015-08-03 13:09:07 +01:00
|
|
|
if specs.key?(:tag)
|
2015-01-07 15:21:20 -05:00
|
|
|
FromURL.new(specs[:tag][/((?:\d+\.)*\d+)/, 1])
|
2013-06-28 21:17:12 -05:00
|
|
|
else
|
2015-01-07 15:21:20 -05:00
|
|
|
FromURL.parse(url)
|
2013-06-28 21:17:12 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-01-07 15:21:20 -05:00
|
|
|
def initialize(val)
|
2013-10-25 17:29:36 -05:00
|
|
|
if val.respond_to?(:to_str)
|
|
|
|
@version = val.to_str
|
|
|
|
else
|
2016-05-03 08:22:28 -04:00
|
|
|
raise TypeError, "Version value must be a string; got a #{val.class} (#{val})"
|
2013-10-25 17:29:36 -05:00
|
|
|
end
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
def detected_from_url?
|
2015-01-07 15:21:20 -05:00
|
|
|
false
|
2012-08-18 13:34:31 -05:00
|
|
|
end
|
|
|
|
|
2013-06-05 21:52:48 -05:00
|
|
|
def head?
|
2015-03-17 21:37:03 -04:00
|
|
|
version == "HEAD"
|
2012-07-09 22:51:10 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def <=>(other)
|
2013-06-05 21:52:48 -05:00
|
|
|
return unless Version === other
|
2015-03-17 21:37:03 -04:00
|
|
|
return 0 if version == other.version
|
2013-06-05 21:52:48 -05:00
|
|
|
return 1 if head? && !other.head?
|
|
|
|
return -1 if !head? && other.head?
|
2012-07-09 23:00:40 -05:00
|
|
|
|
2015-03-27 20:07:50 -04:00
|
|
|
ltokens = tokens
|
|
|
|
rtokens = other.tokens
|
|
|
|
max = max(ltokens.length, rtokens.length)
|
|
|
|
l = r = 0
|
|
|
|
|
|
|
|
while l < max
|
|
|
|
a = ltokens[l] || NULL_TOKEN
|
|
|
|
b = rtokens[r] || NULL_TOKEN
|
|
|
|
|
|
|
|
if a == b
|
|
|
|
l += 1
|
|
|
|
r += 1
|
|
|
|
next
|
|
|
|
elsif a.numeric? && b.numeric?
|
|
|
|
return a <=> b
|
|
|
|
elsif a.numeric?
|
|
|
|
return 1 if a > NULL_TOKEN
|
|
|
|
l += 1
|
|
|
|
elsif b.numeric?
|
|
|
|
return -1 if b > NULL_TOKEN
|
|
|
|
r += 1
|
|
|
|
else
|
|
|
|
return a <=> b
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
0
|
2012-07-09 22:51:10 -05:00
|
|
|
end
|
2013-12-09 19:43:07 -06:00
|
|
|
alias_method :eql?, :==
|
|
|
|
|
|
|
|
def hash
|
2015-03-17 21:37:03 -04:00
|
|
|
version.hash
|
2013-12-09 19:43:07 -06:00
|
|
|
end
|
2012-07-09 22:51:10 -05:00
|
|
|
|
|
|
|
def to_s
|
2015-03-17 21:37:03 -04:00
|
|
|
version.dup
|
2012-07-09 22:51:10 -05:00
|
|
|
end
|
|
|
|
alias_method :to_str, :to_s
|
|
|
|
|
2013-01-07 11:59:33 -06:00
|
|
|
protected
|
|
|
|
|
2015-03-17 21:37:03 -04:00
|
|
|
attr_reader :version
|
|
|
|
|
2015-03-27 20:07:50 -04:00
|
|
|
def tokens
|
|
|
|
@tokens ||= tokenize
|
2013-07-24 19:44:27 -05:00
|
|
|
end
|
|
|
|
|
2015-03-27 20:07:50 -04:00
|
|
|
private
|
2013-06-05 21:52:48 -05:00
|
|
|
|
2015-03-27 20:07:50 -04:00
|
|
|
def max(a, b)
|
|
|
|
a > b ? a : b
|
2013-06-05 21:52:48 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def tokenize
|
2015-03-17 21:37:03 -04:00
|
|
|
version.scan(SCAN_PATTERN).map! do |token|
|
2013-06-05 21:52:48 -05:00
|
|
|
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
|
2013-01-07 11:59:33 -06:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def self.parse(spec)
|
2013-02-06 22:25:02 -06:00
|
|
|
version = _parse(spec)
|
2015-01-07 15:21:20 -05:00
|
|
|
new(version) unless version.nil?
|
2013-02-06 22:25:02 -06:00
|
|
|
end
|
2012-07-09 23:24:27 -05:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def self._parse(spec)
|
2012-07-09 23:24:27 -05:00
|
|
|
spec = Pathname.new(spec) unless spec.is_a? Pathname
|
|
|
|
|
2013-04-16 14:32:28 -05:00
|
|
|
spec_s = spec.to_s
|
|
|
|
|
2012-07-09 23:24:27 -05:00
|
|
|
stem = if spec.directory?
|
|
|
|
spec.basename.to_s
|
2015-08-03 13:09:07 +01:00
|
|
|
elsif %r{((?:sourceforge.net|sf.net)/.*)/download$}.match(spec_s)
|
2012-07-09 23:24:27 -05:00
|
|
|
Pathname.new(spec.dirname).stem
|
|
|
|
else
|
|
|
|
spec.stem
|
|
|
|
end
|
|
|
|
|
2013-04-27 15:21:05 -05:00
|
|
|
# GitHub tarballs
|
|
|
|
# e.g. https://github.com/foo/bar/tarball/v1.2.3
|
2012-07-09 23:24:27 -05:00
|
|
|
# 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
|
2015-08-03 13:09:07 +01:00
|
|
|
m = %r{github.com/.+/(?:zip|tar)ball/(?:v|\w+-)?((?:\d+[-._])+\d*)$}.match(spec_s)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2012-08-25 10:20:48 -07:00
|
|
|
# e.g. https://github.com/erlang/otp/tarball/OTP_R15B01 (erlang style)
|
2013-06-03 20:52:21 +01:00
|
|
|
m = /[-_]([Rr]\d+[AaBb]\d*(?:-\d+)?)/.match(spec_s)
|
2012-08-25 10:20:48 -07:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2012-07-09 23:24:27 -05:00
|
|
|
# e.g. boost_1_39_0
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /((?:\d+_)+\d+)$/.match(stem)
|
2015-08-06 15:45:52 +08:00
|
|
|
return m.captures.first.tr("_", ".") unless m.nil?
|
2012-07-09 23:24:27 -05:00
|
|
|
|
|
|
|
# e.g. foobar-4.5.1-1
|
2015-01-29 23:56:40 +01:00
|
|
|
# e.g. unrtf_0.20.4-1
|
2012-07-09 23:24:27 -05:00
|
|
|
# e.g. ruby-1.9.1-p243
|
2015-01-29 23:56:40 +01:00
|
|
|
m = /[-_]((?:\d+\.)*\d\.\d+-(?:p|rc|RC)?\d+)(?:[-._](?:bin|dist|stable|src|sources))?$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2016-02-19 11:11:14 -08:00
|
|
|
# URL with no extension
|
|
|
|
# e.g. https://waf.io/waf-1.8.12
|
|
|
|
# e.g. https://codeload.github.com/gsamokovarov/jump/tar.gz/v0.7.1
|
|
|
|
m = /[-v]((?:\d+\.)*\d+)$/.match(spec_s)
|
2015-12-26 19:55:19 +01:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2012-07-09 23:24:27 -05:00
|
|
|
# e.g. lame-398-1
|
2016-01-01 02:29:41 -08:00
|
|
|
m = /-((?:\d)+-\d+)/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
|
|
|
# e.g. foobar-4.5.1
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /-((?:\d+\.)*\d+)$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
|
|
|
# e.g. foobar-4.5.1b
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /-((?:\d+\.)*\d+(?:[abc]|rc|RC)\d*)$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
|
|
|
# e.g. foobar-4.5.0-beta1, or foobar-4.50-beta
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /-((?:\d+\.)*\d+-beta\d*)$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2015-01-28 09:28:20 +01:00
|
|
|
# e.g. http://ftpmirror.gnu.org/libidn/libidn-1.29-win64.zip
|
|
|
|
# e.g. http://ftpmirror.gnu.org/libmicrohttpd/libmicrohttpd-0.9.17-w32.zip
|
|
|
|
m = /-(\d+\.\d+(?:\.\d+)?)-w(?:in)?(?:32|64)$/.match(stem)
|
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2015-10-02 22:52:25 +02:00
|
|
|
# Opam packages
|
|
|
|
# e.g. https://opam.ocaml.org/archives/sha.1.9+opam.tar.gz
|
|
|
|
# e.g. https://opam.ocaml.org/archives/lablgtk.2.18.3+opam.tar.gz
|
|
|
|
# e.g. https://opam.ocaml.org/archives/easy-format.1.0.2+opam.tar.gz
|
|
|
|
m = /\.(\d+\.\d+(?:\.\d+)?)\+opam$/.match(stem)
|
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2015-01-28 09:28:20 +01:00
|
|
|
# e.g. http://ftpmirror.gnu.org/mtools/mtools-4.0.18-1.i686.rpm
|
2015-01-29 23:56:40 +01:00
|
|
|
# e.g. http://ftpmirror.gnu.org/autogen/autogen-5.5.7-5.i386.rpm
|
2015-01-28 09:28:20 +01:00
|
|
|
# e.g. http://ftpmirror.gnu.org/libtasn1/libtasn1-2.8-x86.zip
|
2015-01-29 23:56:40 +01:00
|
|
|
# e.g. http://ftpmirror.gnu.org/libtasn1/libtasn1-2.8-x64.zip
|
|
|
|
# e.g. http://ftpmirror.gnu.org/mtools/mtools_4.0.18_i386.deb
|
|
|
|
m = /[-_](\d+\.\d+(?:\.\d+)?(?:-\d+)?)[-_.](?:i[36]86|x86|x64(?:[-_](?:32|64))?)$/.match(stem)
|
2015-01-28 09:28:20 +01:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2012-07-09 23:24:27 -05:00
|
|
|
# e.g. foobar4.5.1
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /((?:\d+\.)*\d+)$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
|
|
|
# e.g. foobar-4.5.0-bin
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /-((?:\d+\.)+\d+[abc]?)[-._](?:bin|dist|stable|src|sources?)$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
|
|
|
# e.g. dash_0.5.5.1.orig.tar.gz (Debian style)
|
2013-04-27 15:21:06 -05:00
|
|
|
m = /_((?:\d+\.)+\d+[abc]?)[.]orig$/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
return m.captures.first unless m.nil?
|
|
|
|
|
2015-01-04 05:02:27 +01:00
|
|
|
# e.g. https://www.openssl.org/source/openssl-0.9.8s.tar.gz
|
2013-06-11 17:34:08 -05:00
|
|
|
m = /-v?([^-]+)/.match(stem)
|
2012-07-09 23:24:27 -05:00
|
|
|
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?
|
2012-10-16 10:31:03 +01:00
|
|
|
|
|
|
|
# e.g. http://mirrors.jenkins-ci.org/war/1.486/jenkins.war
|
2016-02-17 12:26:45 +01:00
|
|
|
# e.g. https://github.com/foo/bar/releases/download/0.10.11/bar.phar
|
|
|
|
m = /\/(\d\.\d+(\.\d+)?)\//.match(spec_s)
|
2012-10-16 10:31:03 +01:00
|
|
|
return m.captures.first unless m.nil?
|
2013-01-27 22:34:53 +00:00
|
|
|
|
|
|
|
# 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?
|
2012-07-09 22:51:10 -05:00
|
|
|
end
|
|
|
|
end
|