pkg_noisrev 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +4 -0
- data/README.rdoc +102 -0
- data/Rakefile +58 -0
- data/bin/pkg_noisrev +70 -0
- data/doc/LICENSE +22 -0
- data/doc/NEWS.rdoc +5 -0
- data/doc/README.rdoc +102 -0
- data/doc/TODO +6 -0
- data/etc/pkg_noisrev.yaml +2 -0
- data/ext/extconf.rb +6 -0
- data/ext/rakefile.rb +24 -0
- data/ext/version.c +286 -0
- data/ext/version.h +13 -0
- data/lib/pkg_noisrev/fbsdpackage.rb +350 -0
- data/lib/pkg_noisrev/fbsdpackageversion.rb +18 -0
- data/lib/pkg_noisrev/meta.rb +9 -0
- data/lib/pkg_noisrev/threads.rb +99 -0
- data/lib/pkg_noisrev/trestle.rb +222 -0
- data/test/helper.rb +10 -0
- data/test/helper_trestle.rb +34 -0
- data/test/rake_git.rb +36 -0
- data/test/semis/package/invalid-1.0/+CONTENTS +0 -0
- data/test/semis/package/xmbdfed-4.7.1_2/+COMMENT +1 -0
- data/test/semis/package/xmbdfed-4.7.1_2/+CONTENTS +47 -0
- data/test/semis/package/zip-3.0/+COMMENT +1 -0
- data/test/semis/package/zip-3.0/+CONTENTS +35 -0
- data/test/semis/package/zip-3.0/+REQUIRED_BY +1 -0
- data/test/semis/ports/MOVED +23 -0
- data/test/semis/ports/archivers/zip/Makefile +35 -0
- data/test/semis/ports/x11-servers/xorg-server/Makefile +137 -0
- data/test/test_fbsdpackages.rb +70 -0
- data/test/test_fbsdports.rb +42 -0
- metadata +111 -0
data/ext/version.h
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#ifndef D31D4EAD_4F6F_A672_4D5B_BD8D946207BC
|
2
|
+
#define D31D4EAD_4F6F_A672_4D5B_BD8D946207BC
|
3
|
+
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <limits.h>
|
6
|
+
#include <ctype.h>
|
7
|
+
#include <sys/cdefs.h>
|
8
|
+
#include <string.h>
|
9
|
+
#include <err.h>
|
10
|
+
|
11
|
+
int version_cmp(const char *pkg1, const char *pkg2);
|
12
|
+
|
13
|
+
#endif // D31D4EAD_4F6F_A672_4D5B_BD8D946207BC
|
@@ -0,0 +1,350 @@
|
|
1
|
+
require_relative 'threads'
|
2
|
+
require_relative 'fbsdpackageversion'
|
3
|
+
|
4
|
+
module Pkg_noisrev
|
5
|
+
class FbsdPackage
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# A placeholder for package.
|
9
|
+
class OnePackage
|
10
|
+
include Comparable
|
11
|
+
|
12
|
+
CATEGORY = ['Root (no dependencies, not depended on)',
|
13
|
+
'Trunk (no dependencies, are depended on)',
|
14
|
+
'Branch (have dependencies, are depended on)',
|
15
|
+
'Leaf (have dependencies, not depended on)',
|
16
|
+
'Unknown category']
|
17
|
+
attr_accessor :name, :ver, :origin, :ports_ver, :category
|
18
|
+
|
19
|
+
def initialize(name, ver, origin, ports_ver, category)
|
20
|
+
@name = name
|
21
|
+
@ver = ver
|
22
|
+
@origin = origin
|
23
|
+
@ports_ver = ports_ver
|
24
|
+
@category = category
|
25
|
+
end
|
26
|
+
|
27
|
+
# by name
|
28
|
+
def <=>(other)
|
29
|
+
@name <=> other.name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :db_dir, :ports_dir
|
34
|
+
|
35
|
+
def initialize(db_dir, ports_dir)
|
36
|
+
@db_dir = db_dir
|
37
|
+
@ports_dir = ports_dir
|
38
|
+
|
39
|
+
@data = []
|
40
|
+
@data_massage = false
|
41
|
+
|
42
|
+
@queue = FbsdPackage.dir_collect(@db_dir)
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
@data.size
|
47
|
+
end
|
48
|
+
|
49
|
+
def each(&block)
|
50
|
+
fail "call analyze method first" unless @data_massage
|
51
|
+
@data.each{ |i| block.call i }
|
52
|
+
end
|
53
|
+
|
54
|
+
# by size
|
55
|
+
def <=>(other)
|
56
|
+
fail "call analyze method first" unless @data_massage
|
57
|
+
@data.size <=> other.size
|
58
|
+
end
|
59
|
+
|
60
|
+
def analyze(log = nil)
|
61
|
+
pkg_total = @queue.size
|
62
|
+
$stdout.puts "#{pkg_total} total: left% processed/okays/failures | thread number: ok/failed\n"
|
63
|
+
$stdout.flush
|
64
|
+
|
65
|
+
thread_pool = []
|
66
|
+
4.times {|i|
|
67
|
+
thread_pool[i] = MyThread.new(i, i) {
|
68
|
+
@queue.size.times {
|
69
|
+
item = @queue.pop(true) rescue break
|
70
|
+
r = FbsdPackage.parse_name item
|
71
|
+
origin = nil
|
72
|
+
category = nil
|
73
|
+
begin
|
74
|
+
origin, category = FbsdPackage.origin @db_dir, item
|
75
|
+
fail "cannot extract the origin for #{name}" unless origin
|
76
|
+
|
77
|
+
pver = FbsdPort.ver @ports_dir, origin
|
78
|
+
@data << OnePackage.new(r.first, r.last, origin, pver, category)
|
79
|
+
rescue
|
80
|
+
MyThread.current.stat.failed += 1
|
81
|
+
@data << OnePackage.new(r.first, r.last, origin, nil, category)
|
82
|
+
log.error "#{$!}" if log
|
83
|
+
else
|
84
|
+
MyThread.current.stat.ok += 1
|
85
|
+
end
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
stat = Spectator.new thread_pool, pkg_total, 1
|
91
|
+
stat.alarm # print the statistics every 1 second
|
92
|
+
|
93
|
+
thread_pool.each(&:join)
|
94
|
+
@data_massage = true
|
95
|
+
stat.alarm_finish
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.parse_name(name)
|
99
|
+
t = name.split '-'
|
100
|
+
return [name, '0'] if t.size < 2
|
101
|
+
[t[0..-2].join('-'), t.last]
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.dir_collect(d)
|
105
|
+
q = Queue.new
|
106
|
+
Dir.glob("#{d}/*").reject {|i| !File.directory?(i) }.map do |i|
|
107
|
+
q.push File.basename(i)
|
108
|
+
end
|
109
|
+
fail "no package records in #{d}" unless q.size > 0
|
110
|
+
q
|
111
|
+
end
|
112
|
+
|
113
|
+
# Return something like ['foo/bar', 3]
|
114
|
+
def self.origin(db_dir, name)
|
115
|
+
contents = File.read "#{db_dir}/#{name}/+CONTENTS"
|
116
|
+
db_required_by = "#{db_dir}/#{name}/+REQUIRED_BY"
|
117
|
+
|
118
|
+
category = nil
|
119
|
+
origin = nil
|
120
|
+
has_dep = contents.match(/^\s*@pkgdep /)
|
121
|
+
|
122
|
+
# Set a package category
|
123
|
+
#
|
124
|
+
# 0--Root (No dependencies, not depended on)
|
125
|
+
# 1--Trunk (No dependencies, are depended on)
|
126
|
+
# 2--Branch (Have dependencies, are depended on)
|
127
|
+
# 3--Leaf (Have dependencies, not depended on)
|
128
|
+
if File.size?(db_required_by)
|
129
|
+
category = 1
|
130
|
+
category = 2 if has_dep
|
131
|
+
else
|
132
|
+
category = 0
|
133
|
+
category = 3 if has_dep
|
134
|
+
end
|
135
|
+
|
136
|
+
origin = $1 if contents.match(/^\s*@comment\s+ORIGIN:(.+)$/)
|
137
|
+
[origin, category]
|
138
|
+
end
|
139
|
+
|
140
|
+
def print(mode, filter)
|
141
|
+
p = ->(item) {
|
142
|
+
cond = '='
|
143
|
+
if item.ports_ver
|
144
|
+
case FbsdPackageVersion.version_cmp(item.ver, item.ports_ver)
|
145
|
+
when -1
|
146
|
+
cond = '<'
|
147
|
+
when 1
|
148
|
+
cond = '>'
|
149
|
+
end
|
150
|
+
else
|
151
|
+
cond = '?'
|
152
|
+
end
|
153
|
+
puts "%21s %s %-21s %s" % [item.ver, cond, item.ports_ver, item.name]
|
154
|
+
}
|
155
|
+
|
156
|
+
packages = []
|
157
|
+
#
|
158
|
+
# filter packages
|
159
|
+
#
|
160
|
+
case filter
|
161
|
+
when ""
|
162
|
+
# all, no filter
|
163
|
+
packages = @data.sort
|
164
|
+
when 'outofsync'
|
165
|
+
packages = @data.reject {|i|
|
166
|
+
FbsdPackageVersion.version_cmp(i.ver,
|
167
|
+
(i.ports_ver ? i.ports_ver : "0")) == 0
|
168
|
+
}.sort
|
169
|
+
when 'missing'
|
170
|
+
packages = @data.reject {|i| i.ports_ver }.sort
|
171
|
+
else
|
172
|
+
fail "invalid filter: #{filter}"
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# print packages
|
177
|
+
#
|
178
|
+
if mode == 'likeportmaster'
|
179
|
+
FbsdPackage.print_like_portmaster @ports_dir, packages
|
180
|
+
else
|
181
|
+
packages.each {|idx| p.call(idx) }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.print_like_portmaster(ports_dir, packages)
|
186
|
+
moved = FbsdPort.moved(ports_dir)
|
187
|
+
|
188
|
+
root = []
|
189
|
+
trunk = []
|
190
|
+
branch = []
|
191
|
+
leaf = []
|
192
|
+
godknowswhat = []
|
193
|
+
packages.sort.each {|i|
|
194
|
+
case i.category
|
195
|
+
when 0
|
196
|
+
root << i
|
197
|
+
when 1
|
198
|
+
trunk << i
|
199
|
+
when 2
|
200
|
+
branch << i
|
201
|
+
when 3
|
202
|
+
leaf << i
|
203
|
+
else
|
204
|
+
godknowswhat << i
|
205
|
+
end
|
206
|
+
}
|
207
|
+
|
208
|
+
outofsync = 0
|
209
|
+
p = ->(category, data) {
|
210
|
+
return if data.size == 0
|
211
|
+
puts "* #{OnePackage::CATEGORY[category]}, #{data.size}"
|
212
|
+
data.each {|i|
|
213
|
+
puts i.name + '-' + i.ver
|
214
|
+
if i.ports_ver
|
215
|
+
if FbsdPackageVersion.version_cmp(i.ver, i.ports_ver) != 0
|
216
|
+
puts " => Ports have another version: #{i.ports_ver}"
|
217
|
+
outofsync += 1
|
218
|
+
end
|
219
|
+
else
|
220
|
+
outofsync += 1
|
221
|
+
# check missing package in MOVED db
|
222
|
+
if m = moved[i.origin]
|
223
|
+
if m.movedto == ""
|
224
|
+
puts " => Deleted at #{m.date}: #{m.why}"
|
225
|
+
else
|
226
|
+
puts " => Moved at #{m.date} to #{m.movedto}: #{m.why}"
|
227
|
+
end
|
228
|
+
else
|
229
|
+
# this is your hand made package or your ports are old
|
230
|
+
puts " => Not found in ports"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
}
|
234
|
+
puts ""
|
235
|
+
}
|
236
|
+
|
237
|
+
puts ""
|
238
|
+
p.call 0, root
|
239
|
+
p.call 1, trunk
|
240
|
+
p.call 2, branch
|
241
|
+
p.call 3, leaf
|
242
|
+
p.call 4, godknowswhat
|
243
|
+
|
244
|
+
puts "Total #{packages.size}, out of sync #{outofsync}."
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class FbsdPort
|
249
|
+
# The last resort method of getting a version number: execute 'make'
|
250
|
+
# command.
|
251
|
+
#
|
252
|
+
# Return a string that represents a port version.
|
253
|
+
def self.ver_slow(ports_dir, origin)
|
254
|
+
# cannot use just a block for Dir.chdir due to a annoing warning
|
255
|
+
# under the another thread
|
256
|
+
dirsave = Dir.pwd
|
257
|
+
|
258
|
+
Dir.chdir ports_dir + '/' + origin
|
259
|
+
r = Trestle.cmd_run('make -V PKGVERSION')
|
260
|
+
|
261
|
+
Dir.chdir dirsave
|
262
|
+
|
263
|
+
fail "even executing make didn't help #{origin}" if r[0] != 0
|
264
|
+
r[2].strip
|
265
|
+
end
|
266
|
+
|
267
|
+
# Return a string that represents a port version.
|
268
|
+
#
|
269
|
+
# Tries to do that in 2 ways: (a) by primitive regexp parsing of
|
270
|
+
# Makefile and if that fails (b) by executing external 'make'
|
271
|
+
# command.
|
272
|
+
def self.ver(ports_dir, origin, rlevel = 0)
|
273
|
+
fail "recursion level for #{origin} is too high" if rlevel >= 3
|
274
|
+
|
275
|
+
makefile = ports_dir + '/' + origin + '/' + 'Makefile'
|
276
|
+
ver = {}
|
277
|
+
|
278
|
+
begin
|
279
|
+
File.open(makefile) {|f|
|
280
|
+
f.each {|line|
|
281
|
+
['MASTERDIR', 'DISTVERSION', 'PORTVERSION', 'PORTREVISION', 'PORTEPOCH'].each {|idx|
|
282
|
+
ver[idx] = $1 if line.match(/^\s*#{idx}\s*[?:!]?=\s*(\S+)$/)
|
283
|
+
}
|
284
|
+
}
|
285
|
+
}
|
286
|
+
rescue
|
287
|
+
fail "(rlevel=#{rlevel}) #{$!}"
|
288
|
+
end
|
289
|
+
|
290
|
+
# Recursion! Some ports Makefiles don't contain version definitions
|
291
|
+
# but a link to a 'master' port.
|
292
|
+
if ver['MASTERDIR']
|
293
|
+
rlevel += 1
|
294
|
+
master_origin = ver['MASTERDIR'].sub(/\${.CURDIR}/, origin)
|
295
|
+
return ver(ports_dir, master_origin, rlevel)
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
# check if vars contain sane values
|
300
|
+
ok = true
|
301
|
+
ver.each {|k,v|
|
302
|
+
if v !~ /^[a-zA-Z0-9_,.-]+$/
|
303
|
+
ok = false
|
304
|
+
# puts makefile
|
305
|
+
break
|
306
|
+
end
|
307
|
+
}
|
308
|
+
ver['PORTVERSION'] = ver['DISTVERSION'] = nil if !ok
|
309
|
+
|
310
|
+
if ver['PORTVERSION'] || ver['DISTVERSION']
|
311
|
+
r = ver['PORTVERSION'] || ver['DISTVERSION']
|
312
|
+
r += "_#{ver['PORTREVISION']}" if ver['PORTREVISION']
|
313
|
+
r += ",#{ver['PORTEPOCH']}" if ver['PORTEPOCH']
|
314
|
+
return r
|
315
|
+
end
|
316
|
+
|
317
|
+
# try a dumb method
|
318
|
+
return FbsdPort.ver_slow(ports_dir, origin) rescue fail "(rlevel=#{rlevel}) #{$!}"
|
319
|
+
|
320
|
+
fail "(rlevel=#{rlevel}) cannot extract the version for #{makefile}"
|
321
|
+
end
|
322
|
+
|
323
|
+
# A placeholder for moved or removed single port.
|
324
|
+
#
|
325
|
+
# :movedto may be an empty string (indicates that the port was deleted)
|
326
|
+
Moved = Struct.new :movedto, :date, :why
|
327
|
+
|
328
|
+
# Parse /usr/ports/MOVED file into a hash.
|
329
|
+
# Return an empty hash on error.
|
330
|
+
def self.moved(ports_dir)
|
331
|
+
db_moved = ports_dir + '/MOVED'
|
332
|
+
r = {}
|
333
|
+
begin
|
334
|
+
File.open(db_moved) {|f|
|
335
|
+
f.each {|line|
|
336
|
+
next if line =~ /^\s*(#.*|\s*)$/ # comment or blank line
|
337
|
+
next if (entry = line.split '|').size != 4
|
338
|
+
r[entry.first] = Moved.new *entry[1..-1]
|
339
|
+
}
|
340
|
+
}
|
341
|
+
rescue
|
342
|
+
Trestle.warnx "parsing #{db_moved} failed: #{$!}"
|
343
|
+
return {}
|
344
|
+
end
|
345
|
+
|
346
|
+
r
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'dl/import'
|
2
|
+
|
3
|
+
module Pkg_noisrev
|
4
|
+
module FbsdPackageVersion
|
5
|
+
extend DL::Importer
|
6
|
+
|
7
|
+
DLL_PATH = [File.dirname(__FILE__),
|
8
|
+
File.absolute_path("#{File.dirname(__FILE__)}/../../ext")]
|
9
|
+
DLL_NAME = 'version.so'
|
10
|
+
|
11
|
+
begin
|
12
|
+
dlload(DLL_PATH[0] + '/' + DLL_NAME)
|
13
|
+
rescue
|
14
|
+
dlload(DLL_PATH[1] + '/' + DLL_NAME)
|
15
|
+
end
|
16
|
+
extern 'int version_cmp(const char *, const char *)'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Pkg_noisrev
|
2
|
+
|
3
|
+
class Spectator
|
4
|
+
def initialize(thread_pool, total, sec, stream = $stdout)
|
5
|
+
@thread_pool = thread_pool
|
6
|
+
@total = total
|
7
|
+
@sec = sec
|
8
|
+
|
9
|
+
@stream = stream
|
10
|
+
@tty = stream.tty? ? true : false
|
11
|
+
|
12
|
+
@alarm = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def flush_line
|
16
|
+
@tty ? @stream.print("\r") : @stream.print("\n")
|
17
|
+
@stream.flush
|
18
|
+
end
|
19
|
+
|
20
|
+
def draw(thread_pool, total)
|
21
|
+
stat = ''
|
22
|
+
processed = 0
|
23
|
+
okays = 0
|
24
|
+
failures = 0
|
25
|
+
|
26
|
+
thread_pool.each {|i|
|
27
|
+
stat += " | %d: %d/%d" % [i.stat.nthread, i.stat.ok, i.stat.failed]
|
28
|
+
okays += i.stat.ok
|
29
|
+
failures += i.stat.failed
|
30
|
+
processed = okays + failures
|
31
|
+
}
|
32
|
+
|
33
|
+
# left%/processed/okays/failures
|
34
|
+
@stream.print '%d%% %d/%d/%d' % [((processed.to_f/total)*100).round(2), processed, okays, failures] + stat
|
35
|
+
flush_line
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return a tread which will dump a statistics every @sec to a
|
39
|
+
# @stream about @thread_pool.
|
40
|
+
def alarm
|
41
|
+
@alarm = Thread.new(@thread_pool, @total, @sec) { |tp, total, s|
|
42
|
+
Thread.current.abort_on_exception = true
|
43
|
+
loop {
|
44
|
+
draw tp, total
|
45
|
+
break if alldone?
|
46
|
+
sleep s
|
47
|
+
}
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def alarm_finish
|
52
|
+
if @alarm
|
53
|
+
@alarm.join
|
54
|
+
draw @thread_pool, @total
|
55
|
+
@stream.print "\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def done_right?
|
60
|
+
return false if (@thread_pool.inject(0) {|sum, i| sum+i.stat.ok }) == 0
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def alldone?
|
65
|
+
# puts ""
|
66
|
+
# @thread_pool.each {|i| puts "#{i}=#{i.status}" }
|
67
|
+
@thread_pool.each {|i|
|
68
|
+
# thread status: false if terminated normally, nil if with exception
|
69
|
+
return false if (i.status != nil && i.status != false)
|
70
|
+
}
|
71
|
+
true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class MyThread < Thread
|
76
|
+
class Stat
|
77
|
+
attr_accessor :nthread, :ok, :failed
|
78
|
+
|
79
|
+
def initialize(n)
|
80
|
+
@nthread = n
|
81
|
+
@ok = 0
|
82
|
+
@failed = 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
"%d (o/f): %d/%d" % [@nthread, @ok, @failed]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_accessor :stat
|
91
|
+
|
92
|
+
# n -- thread number
|
93
|
+
def initialize(*var, n)
|
94
|
+
@stat = Stat.new(n)
|
95
|
+
super(*var)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|