1 |
On 09/18/2015 04:32 AM, konsolebox wrote: |
2 |
> This is what an ideal and simple versioning spec should look like to |
3 |
> me. (Not the form, but the concept). I'm posting this here so it |
4 |
> could be used as an added reference to anyone that would consider |
5 |
> revising the current specification. |
6 |
> |
7 |
> Note: Assigning default values can be bypassed depending on the implementation. |
8 |
> |
9 |
> Comments are welcome. |
10 |
> ---------------------------------------- |
11 |
> 1 Version Specification |
12 |
> |
13 |
> A version is composed of four component sets: the base part, the |
14 |
> stage, the patch and the revision. |
15 |
> |
16 |
> Each component is composed mainly of version nodes to represent their |
17 |
> level values. |
18 |
> |
19 |
> When a component is not specified, it gets a default node value of {0}. |
20 |
> |
21 |
> 1.1 Version Nodes |
22 |
> |
23 |
> Nodes start basically with a number and then is optionally followed by |
24 |
> a set of letters. |
25 |
> |
26 |
> Numbers and letters can coexist alternatingly to represent a single |
27 |
> node. The number of consecutive letters is also not limited to 1. |
28 |
> For example: "1a4xy" is allowed (can be restricted). |
29 |
> |
30 |
> Each set of digits represents a single decimal number. Leading zeros |
31 |
> hold no meaning. |
32 |
> |
33 |
> The numerical equivalent of a set of letters is calculated in base of |
34 |
> 27 (0 exists along with it but is not included as a symbol). |
35 |
> |
36 |
> Version nodes after processing are basically just an array of signed |
37 |
> integers where each set of digits or letters are converted to their |
38 |
> numerical value. |
39 |
> |
40 |
> 1.2 Version Parts |
41 |
> |
42 |
> 1.2.1 The Base Part |
43 |
> |
44 |
> The base version is simply a set of version nodes that are separated |
45 |
> by dots. Examples: "4.1.2", "4.1.2aa" "4a", "4a.1", "4a.1.2". |
46 |
> |
47 |
> After processing, the base version is an array of version nodes. |
48 |
> |
49 |
> It is required. |
50 |
> |
51 |
> 1.2.2 The Stage Part |
52 |
> |
53 |
> The stage part starts with _alpha, _beta, _pre or _rc. Their |
54 |
> numerical values are -4, -3, -2 or -1 respectively. Each of them can |
55 |
> optionally be followed by a version node string. For example: |
56 |
> "_alpha01". |
57 |
> |
58 |
> The resulting value for the stage after processing is a single version |
59 |
> node where the first value in it is the numerical values of _alpha, |
60 |
> _beta, _pre or _rc, and the other values are based on the added node |
61 |
> string. |
62 |
> |
63 |
> A version without a stage has a default stage value of {0}. |
64 |
> |
65 |
> The stage part is optional and can be specified only once after the |
66 |
> base part. It can't be specified as a modifier for the patch, for the |
67 |
> revision, or for another stage. |
68 |
> |
69 |
> 1.2.3 The Patch Part |
70 |
> |
71 |
> The patch part begins with _p and is followed by a version node |
72 |
> string. For example: "_p20150105a". |
73 |
> |
74 |
> The patch part is optional and can be specified after the base part or |
75 |
> after the stage part, but not after the revision. It can only be |
76 |
> specified once. |
77 |
> |
78 |
> It is processed as a single version node based on its version node string. |
79 |
> |
80 |
> A version without a patch has a default patch value of {0}. |
81 |
> |
82 |
> 1.2.4 The Revision |
83 |
> |
84 |
> The revision starts with -r and is followed by a number. |
85 |
> |
86 |
> It is processed as a single version node based on its version node string. |
87 |
> |
88 |
> A version without a revision has a default revision value of {0}. |
89 |
> |
90 |
> 1.3 Comparing Versions |
91 |
> |
92 |
> Versions are compared as version nodes and the algorithm is simple: |
93 |
> each component and subcomponent is compared from left to right. |
94 |
> |
95 |
> Anything that gives a difference decides which version is greater or lesser. |
96 |
> |
97 |
> Any non-existing version-node has a default value of {0}. |
98 |
> |
99 |
> Any non-existing element of a version node has a default value of 0. |
100 |
> |
101 |
> If no difference is found during the process, it would mean that both |
102 |
> versions are equal. |
103 |
> |
104 |
> 1.3.1 Concept Code |
105 |
> |
106 |
> #!/usr/bin/ruby |
107 |
> |
108 |
> class ::Array |
109 |
> def adaptive_transpose |
110 |
> h_size = self.max_by{ |a| a.size }.size |
111 |
> v_size = self.size |
112 |
> |
113 |
> result = Array.new(h_size) |
114 |
> with_index = self.each_with_index.to_a.freeze |
115 |
> |
116 |
> 0.upto(h_size - 1) do |i| |
117 |
> result[i] = Array.new(v_size) |
118 |
> with_index.each{ |a, j| result[i][j] = a[i] } |
119 |
> end |
120 |
> |
121 |
> result |
122 |
> end |
123 |
> end |
124 |
> |
125 |
> module Portage |
126 |
> class PackageVersion |
127 |
> class Node < ::Array |
128 |
> def initialize(*values) |
129 |
> self.concat(values) |
130 |
> end |
131 |
> |
132 |
> def compare_with(another) |
133 |
> [self, another].adaptive_transpose.each do |a, b| |
134 |
> a ||= 0 |
135 |
> b ||= 0 |
136 |
> return -1 if a < b |
137 |
> return 1 if a > b |
138 |
> end |
139 |
> |
140 |
> return 0 |
141 |
> end |
142 |
> |
143 |
> def self.parse(*args) |
144 |
> result = new |
145 |
> |
146 |
> args.each do |a| |
147 |
> case a |
148 |
> when Integer |
149 |
> result << a |
150 |
> when /^[[:digit:]][[:alnum:]]*$/ |
151 |
> a.scan(/[[:digit:]]+|[[:alpha:]]+/).each_with_index.map do |b, i| |
152 |
> if i.even? |
153 |
> str = b.gsub(/^0+/, '') |
154 |
> result << (str.empty? ? 0 : Integer(str)) |
155 |
> else |
156 |
> value = 0 |
157 |
> |
158 |
> b.downcase.bytes.reverse.each_with_index do |c, i| |
159 |
> value += 27 ** i * (c - 96) ## a == 1, z == 26, |
160 |
> and 0 exists but is not used |
161 |
> end |
162 |
> |
163 |
> result << value |
164 |
> end |
165 |
> end |
166 |
> else |
167 |
> raise ArgumentError.new("Invalid node string: #{a.inspect}") |
168 |
> end |
169 |
> end |
170 |
> |
171 |
> result |
172 |
> end |
173 |
> |
174 |
> def self.zero |
175 |
> @zero ||= new(0) |
176 |
> end |
177 |
> |
178 |
> private_class_method :new, :allocate |
179 |
> end |
180 |
> |
181 |
> attr_accessor :base, :stage, :patch, :revision |
182 |
> |
183 |
> def initialize(base, stage, patch, revision) |
184 |
> @base, @stage, @patch, @revision = base, stage, patch, revision |
185 |
> end |
186 |
> |
187 |
> def compare_with(another) |
188 |
> [self.base, another.base].adaptive_transpose.each do |a, b| |
189 |
> a ||= Node.zero |
190 |
> b ||= Node.zero |
191 |
> r = a.compare_with(b) |
192 |
> return r unless r == 0 |
193 |
> end |
194 |
> |
195 |
> r = self.stage.compare_with(another.stage) |
196 |
> return r unless r == 0 |
197 |
> |
198 |
> r = self.patch.compare_with(another.patch) |
199 |
> return r unless r == 0 |
200 |
> |
201 |
> r = self.revision.compare_with(another.revision) |
202 |
> return r unless r == 0 |
203 |
> |
204 |
> return 0 |
205 |
> end |
206 |
> |
207 |
> STAGES = { 'alpha' => -4, 'beta' => -3, 'pre' => -2, 'rc' => -1 } |
208 |
> REGEX = /^([[:digit:]][[:alnum:]]*(?:[.][[:alnum:]]+)*)?(?:_(alpha|beta|pre|rc)([[:digit:]][[:alnum:]]*)?)?(?:_p([[:digit:]][[:alnum:]]*))?(?:-r([[:digit:]]+))?(.+)?$/m |
209 |
> |
210 |
> def self.parse(version_string) |
211 |
> __, base, stage, stage_ver, patch, revision, extra = |
212 |
> version_string.match(REGEX).to_a |
213 |
> raise_invalid_version_string(version_string) if extra |
214 |
> |
215 |
> begin |
216 |
> base = base.split('.').map{ |e| Node.parse(e) } |
217 |
> stage = stage ? stage_ver ? Node.parse(STAGES[stage], |
218 |
> stage_ver) : Node.parse(STAGES[stage]) : Node.zero |
219 |
> patch = patch ? Node.parse(patch) : Node.zero |
220 |
> revision = revision ? Node.parse(revision) : Node.zero |
221 |
> rescue ArgumentError => e |
222 |
> raise_invalid_version_string("#{version_string}: #{e}") |
223 |
> end |
224 |
> |
225 |
> new(base, stage, patch, revision) |
226 |
> end |
227 |
> |
228 |
> def self.raise_invalid_version_string(version_string) |
229 |
> raise ArgumentError.new("Invalid version string: #{version_string}") |
230 |
> end |
231 |
> |
232 |
> private_class_method :new, :allocate, :raise_invalid_version_string |
233 |
> end |
234 |
> end |
235 |
> |
236 |
> samples = [ |
237 |
> ["0", "0.01"], |
238 |
> ["0.01", "0.010"], |
239 |
> ["0.09", "0.090"], |
240 |
> ["0.10", "0.100"], |
241 |
> ["0.99", "0.990"], |
242 |
> ["0.100", "0.1000"], |
243 |
> ["0.100", "0.100"], |
244 |
> ["0.1", "0.1.1"], |
245 |
> ["0.1.1", "0.1a"], |
246 |
> ["0.1a", "0.2"], |
247 |
> ["0.2", "1"], |
248 |
> ["1", "1.0"], |
249 |
> ["1.0", "1.0_alpha"], |
250 |
> ["1.0_alpha", "1.0_alpha01"], |
251 |
> ["1.0_alpha01", "1.0_alpha01-r1"], |
252 |
> ["1.0_alpha01-r1", "1.0_alpha01_p20150105"], |
253 |
> ["1.0_alpha01_p20150105", "1.0_alpha01_p20150105-r1"], |
254 |
> ["1.0_alpha01", "1.0_beta"], |
255 |
> ["1.0_beta", "1.0_beta01"], |
256 |
> ["1.0_beta01", "1.0_pre01"], |
257 |
> ["1.0_pre01", "1.0_rc01"], |
258 |
> ["1.0_rc01", "1.0"], |
259 |
> ["1.0", "1.0-r1"], |
260 |
> ["1.0-r1", "1.0_p20150105"], |
261 |
> ["1.0_p20150105", "1.0_p20150105-r1"] |
262 |
> ] |
263 |
> |
264 |
> samples.each do |a, b| |
265 |
> x = Portage::PackageVersion.parse(a) |
266 |
> y = Portage::PackageVersion.parse(b) |
267 |
> r = x.compare_with(y) |
268 |
> r = r < 0 ? '<' : r > 0 ? '>' : '==' |
269 |
> puts "#{a} #{r} #{b}" |
270 |
> end |
271 |
> |
272 |
> 1.3.2 Concept Code Output |
273 |
> |
274 |
> 0 < 0.01 |
275 |
> 0.01 < 0.010 |
276 |
> 0.09 < 0.090 |
277 |
> 0.10 < 0.100 |
278 |
> 0.99 < 0.990 |
279 |
> 0.100 < 0.1000 |
280 |
> 0.100 == 0.100 |
281 |
> 0.1 < 0.1.1 |
282 |
> 0.1.1 < 0.1a |
283 |
> 0.1a < 0.2 |
284 |
> 0.2 < 1 |
285 |
> 1 == 1.0 |
286 |
> 1.0 > 1.0_alpha |
287 |
> 1.0_alpha < 1.0_alpha01 |
288 |
> 1.0_alpha01 < 1.0_alpha01-r1 |
289 |
> 1.0_alpha01-r1 < 1.0_alpha01_p20150105 |
290 |
> 1.0_alpha01_p20150105 < 1.0_alpha01_p20150105-r1 |
291 |
> 1.0_alpha01 < 1.0_beta |
292 |
> 1.0_beta < 1.0_beta01 |
293 |
> 1.0_beta01 < 1.0_pre01 |
294 |
> 1.0_pre01 < 1.0_rc01 |
295 |
> 1.0_rc01 < 1.0 |
296 |
> 1.0 < 1.0-r1 |
297 |
> 1.0-r1 < 1.0_p20150105 |
298 |
> 1.0_p20150105 < 1.0_p20150105-r1 |
299 |
> |
300 |
Are you stating this is for package epochs? |
301 |
|
302 |
-- |
303 |
-- Matthew Thode (prometheanfire) |