Module: Puppet::Util::RpmCompare
- Included in:
- Package::Version::Rpm, Package::Version::Rpm
- Defined in:
- lib/puppet/util/rpm_compare.rb
Constant Summary collapse
- ARCH_LIST =
%w[ noarch i386 i686 ppc ppc64 armv3l armv4b armv4l armv4tl armv5tel armv5tejl armv6l armv7l m68kmint s390 s390x ia64 x86_64 sh3 sh4 ].freeze
- ARCH_REGEX =
Regexp.new(ARCH_LIST.map { |arch| "\\.#{arch}" }.join('|'))
Instance Method Summary collapse
-
#compare_values(s1, s2) ⇒ Object
this method is a native implementation of the compare_values function in rpm’s python bindings, found in python/header-py.c, as used by rpm.
-
#rpm_compare_evr(should, is) ⇒ Object
how rpm compares two package versions: rpmUtils.miscutils.compareEVR(), which massages data types and then calls rpm.labelCompare(), found in rpm.git/python/header-py.c, which sets epoch to 0 if null, then compares epoch, then ver, then rel using compare_values() and returns the first non-0 result, else 0.
-
#rpm_parse_evr(full_version) ⇒ Object
parse a rpm “version” specification this re-implements rpm’s rpmUtils.miscutils.stringToVersion() in ruby.
-
#rpmvercmp(str1, str2) ⇒ Object
This is an attempt at implementing RPM’s lib/rpmvercmp.c rpmvercmp(a, b) in Ruby.
Instance Method Details
#compare_values(s1, s2) ⇒ Object
this method is a native implementation of the compare_values function in rpm’s python bindings, found in python/header-py.c, as used by rpm.
151 152 153 154 155 156 157 |
# File 'lib/puppet/util/rpm_compare.rb', line 151 def compare_values(s1, s2) return 0 if s1.nil? && s2.nil? return 1 if !s1.nil? && s2.nil? return -1 if s1.nil? && !s2.nil? rpmvercmp(s1, s2) end |
#rpm_compare_evr(should, is) ⇒ Object
how rpm compares two package versions: rpmUtils.miscutils.compareEVR(), which massages data types and then calls rpm.labelCompare(), found in rpm.git/python/header-py.c, which sets epoch to 0 if null, then compares epoch, then ver, then rel using compare_values() and returns the first non-0 result, else 0. This function combines the logic of compareEVR() and labelCompare().
“version_should” can be v, v-r, or e:v-r. “version_is” will always be at least v-r, can be e:v-r
return 1: a is newer than b
0: a and b are the same version
-1: b is newer than a
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/puppet/util/rpm_compare.rb', line 172 def rpm_compare_evr(should, is) # pass on to rpm labelCompare should_hash = rpm_parse_evr(should) is_hash = rpm_parse_evr(is) unless should_hash[:epoch].nil? rc = compare_values(should_hash[:epoch], is_hash[:epoch]) return rc unless rc == 0 end rc = compare_values(should_hash[:version], is_hash[:version]) return rc unless rc == 0 # here is our special case, PUP-1244. # if should_hash[:release] is nil (not specified by the user), # and comparisons up to here are equal, return equal. We need to # evaluate to whatever level of detail the user specified, so we # don't end up upgrading or *downgrading* when not intended. # # This should NOT be triggered if we're trying to ensure latest. return 0 if should_hash[:release].nil? compare_values(should_hash[:release], is_hash[:release]) end |
#rpm_parse_evr(full_version) ⇒ Object
parse a rpm “version” specification this re-implements rpm’s rpmUtils.miscutils.stringToVersion() in ruby
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/puppet/util/rpm_compare.rb', line 118 def rpm_parse_evr(full_version) epoch_index = full_version.index(':') if epoch_index epoch = full_version[0, epoch_index] full_version = full_version[epoch_index + 1, full_version.length] else epoch = nil end begin epoch = String(Integer(epoch)) rescue # If there are non-digits in the epoch field, default to nil epoch = nil end release_index = full_version.index('-') if release_index version = full_version[0, release_index] release = full_version[release_index + 1, full_version.length] arch = release.scan(ARCH_REGEX)[0] if arch architecture = arch.delete('.') release.gsub!(ARCH_REGEX, '') end else version = full_version release = nil end { :epoch => epoch, :version => version, :release => release, :arch => architecture } end |
#rpmvercmp(str1, str2) ⇒ Object
This is an attempt at implementing RPM’s lib/rpmvercmp.c rpmvercmp(a, b) in Ruby.
Some of the things in here look REALLY UGLY and/or arbitrary. Our goal is to match how RPM compares versions, quirks and all.
I’ve kept a lot of C-like string processing in an effort to keep this as identical to RPM as possible.
returns 1 if str1 is newer than str2,
0 if they are identical
-1 if str1 is older than str2
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/puppet/util/rpm_compare.rb', line 27 def rpmvercmp(str1, str2) return 0 if str1 == str2 front_strip_re = /^[^A-Za-z0-9~]+/ while str1.length > 0 or str2.length > 0 # trim anything that's in front_strip_re and != '~' off the beginning of each string str1 = str1.gsub(front_strip_re, '') str2 = str2.gsub(front_strip_re, '') # "handle the tilde separator, it sorts before everything else" if str1 =~ /^~/ && str2 =~ /^~/ # if they both have ~, strip it str1 = str1[1..] str2 = str2[1..] next elsif str1 =~ /^~/ return -1 elsif str2 =~ /^~/ return 1 end break if str1.length == 0 or str2.length == 0 # "grab first completely alpha or completely numeric segment" isnum = false # if the first char of str1 is a digit, grab the chunk of continuous digits from each string if str1 =~ /^[0-9]+/ if str1 =~ /^[0-9]+/ segment1 = $LAST_MATCH_INFO.to_s str1 = $LAST_MATCH_INFO.post_match else segment1 = '' end if str2 =~ /^[0-9]+/ segment2 = $LAST_MATCH_INFO.to_s str2 = $LAST_MATCH_INFO.post_match else segment2 = '' end isnum = true # else grab the chunk of continuous alphas from each string (which may be '') else if str1 =~ /^[A-Za-z]+/ segment1 = $LAST_MATCH_INFO.to_s str1 = $LAST_MATCH_INFO.post_match else segment1 = '' end if str2 =~ /^[A-Za-z]+/ segment2 = $LAST_MATCH_INFO.to_s str2 = $LAST_MATCH_INFO.post_match else segment2 = '' end end # if the segments we just grabbed from the strings are different types (i.e. one numeric one alpha), # where alpha also includes ''; "numeric segments are always newer than alpha segments" if segment2.length == 0 return 1 if isnum return -1 end if isnum # "throw away any leading zeros - it's a number, right?" segment1 = segment1.gsub(/^0+/, '') segment2 = segment2.gsub(/^0+/, '') # "whichever number has more digits wins" return 1 if segment1.length > segment2.length return -1 if segment1.length < segment2.length end # "strcmp will return which one is greater - even if the two segments are alpha # or if they are numeric. don't return if they are equal because there might # be more segments to compare" rc = segment1 <=> segment2 return rc if rc != 0 end # end while loop # if we haven't returned anything yet, "whichever version still has characters left over wins" return 1 if str1.length > str2.length return -1 if str1.length < str2.length 0 end |