Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/portupgrade
Path: blob/master/lib/pkgtools/pkgtools.rb
102 views
1
# vim: set sts=2 sw=2 ts=8 et:
2
#
3
# Copyright (c) 2001-2004 Akinori MUSHA <[email protected]>
4
# Copyright (c) 2006-2008 Sergey Matveychuk <[email protected]>
5
# Copyright (c) 2009-2012 Stanislav Sedov <[email protected]>
6
# Copyright (c) 2012 Bryan Drewery <[email protected]>
7
#
8
# All rights reserved.
9
#
10
# Redistribution and use in source and binary forms, with or without
11
# modification, are permitted provided that the following conditions
12
# are met:
13
# 1. Redistributions of source code must retain the above copyright
14
# notice, this list of conditions and the following disclaimer.
15
# 2. Redistributions in binary form must reproduce the above copyright
16
# notice, this list of conditions and the following disclaimer in the
17
# documentation and/or other materials provided with the distribution.
18
#
19
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
# SUCH DAMAGE.
30
#
31
32
PREFIX = ENV["LOCALBASE"] || "/usr/local"
33
Version = "2.4.16"
34
35
require "pkgtools/pkg"
36
require "pkgtools/ports"
37
require "pkgtools/pkgmisc"
38
require "pkgtools/revision"
39
40
require "set"
41
require "time"
42
require "delegate"
43
require "tempfile"
44
45
autoload "Readline", "readline"
46
47
module PkgConfig
48
end
49
50
def load_config
51
file = ENV['PKGTOOLS_CONF'] || File.join(PREFIX, 'etc/pkgtools.conf')
52
53
File.exist?(file) or return false
54
55
begin
56
load file
57
rescue Exception => e
58
STDERR.puts "** Error occured reading #{file}:",
59
e.message.gsub(/^/, "\t")
60
exit 1
61
end
62
63
init_pkgtools_global
64
65
val = config_value(:SANITY_CHECK)
66
val.nil? or $sanity_check = val
67
68
if a = config_value(:PKG_SITES)
69
$pkg_sites.concat(a)
70
else
71
$pkg_sites << PkgConfig.pkg_site_mirror()
72
end
73
74
true
75
end
76
77
def setproctitle(fmt, *args)
78
$0 = sprintf('%s: ' << fmt, MYNAME, *args)
79
end
80
81
def config_value(name)
82
PkgConfig.const_defined?(name) ? PkgConfig.const_get(name) : nil
83
end
84
85
def compile_config_table(hash)
86
otable = {}
87
gtable = {}
88
89
hash.each do |pattern, value|
90
$portsdb.glob(pattern) do |portinfo|
91
(otable[portinfo.origin] ||= Set.new) << value
92
end
93
94
if !pattern.include?('/')
95
gtable[pattern] = value
96
end
97
end if hash
98
99
table = [otable, gtable]
100
end
101
102
def lookup_config_table(table, origin, pkgname = nil)
103
otable, gtable = *table
104
105
valueset = otable[origin] || Set.new
106
107
if pkgname
108
gtable.each do |pattern, value|
109
$pkgdb.glob(pattern, false) do |name|
110
if pkgname == name
111
valueset << value
112
break
113
end
114
end
115
end
116
end
117
118
return nil if valueset.empty?
119
120
valueset
121
end
122
123
def config_make_args(origin, pkgname = nil)
124
$make_args_table ||= compile_config_table(config_value(:MAKE_ARGS))
125
126
argset = lookup_config_table($make_args_table, origin, pkgname) or
127
return nil
128
129
argset.map { |args|
130
if args.is_a?(Proc)
131
String.new(args.call(origin)) rescue nil
132
else
133
args
134
end
135
}.join(' ')
136
end
137
138
def config_make_env(origin, pkgname = nil)
139
$make_env_table ||= compile_config_table(config_value(:MAKE_ENV))
140
141
envset = (lookup_config_table($make_env_table, origin, pkgname) or Array.new)
142
143
make_env = Array.new
144
145
envset.each do |envs|
146
if envs.is_a?(Proc)
147
make_env << String.new(envs.call(origin)) rescue nil
148
elsif envs.is_a?(Array)
149
envs.each do |entry|
150
make_env << entry
151
end
152
else
153
make_env << envs
154
end
155
end
156
make_env
157
end
158
159
def config_commandtable(key, origin)
160
$command_tables[key] ||= compile_config_table(config_value(key))
161
162
cmdset = lookup_config_table($command_tables[key], origin) or
163
return nil
164
165
cmdset.map { |command|
166
if command.is_a?(Proc)
167
String.new(command.call(origin)) rescue nil
168
else
169
command.dup
170
end
171
}.compact
172
end
173
174
def config_beforebuild(origin)
175
config_commandtable(:BEFOREBUILD, origin)
176
end
177
178
def config_beforedeinstall(origin)
179
config_commandtable(:BEFOREDEINSTALL, origin)
180
end
181
182
def config_afterinstall(origin)
183
config_commandtable(:AFTERINSTALL, origin)
184
end
185
186
def config_use_packages_only?(p)
187
config_include?(:USE_PKGS_ONLY, p)
188
end
189
190
def config_use_packages?(p)
191
config_include?(:USE_PKGS, p)
192
end
193
194
def config_use_ports_only?(p)
195
config_include?(:USE_PORTS_ONLY, p)
196
end
197
198
def config_held?(p)
199
config_include?(:HOLD_PKGS, p)
200
end
201
202
def config_ignore_moved?(p)
203
config_include?(:IGNORE_MOVED, p)
204
end
205
206
def config_include?(key, p)
207
if $config_include_table.key?(key)
208
set = $config_include_table[key]
209
else
210
set = $config_include_table[key] = Set.new
211
212
if a = config_value(key)
213
a.each do |pattern|
214
$portsdb.glob(pattern) do |portinfo|
215
set << portinfo.origin
216
end
217
218
if pkgnames = $pkgdb.deorigin_glob(pattern)
219
pkgnames.each do |pkgname|
220
set << pkgname
221
end
222
end
223
224
set.merge($pkgdb.glob(pattern, false))
225
end
226
end
227
end
228
229
case p
230
when PortInfo
231
set.include?(p.origin)
232
when PkgInfo
233
(o = p.origin and set.include?(o)) ||
234
set.include?(p.fullname)
235
else
236
set.include?(p)
237
end
238
end
239
240
def init_pkgtools_global
241
# initialize pkgdb first - PortsDB uses PkgDB.instance.db_dir.
242
$portsdb = PortsDB.instance.setup
243
$pkgdb = PkgDB.instance.setup
244
$pkgdb_dir = $pkgdb.db_dir
245
$ports_dir = $portsdb.ports_dir
246
$packages_base = ENV['PACKAGES'] || File.join($ports_dir, 'packages')
247
$packages_dir = File.join($packages_base, 'All')
248
init_tmpdir
249
$pkg_path = ENV['PKG_PATH'] || $packages_dir
250
251
$pkg_sites = (ENV['PKG_SITES'] || '').split
252
253
$verbose = false
254
$sudo_args = ['sudo']
255
$sudo = false
256
257
$timer = {}
258
259
$make_args_table = nil
260
$command_tables = {}
261
262
$config_include_table = {}
263
264
$portsdb.ignore_categories = config_value(:IGNORE_CATEGORIES) || []
265
$portsdb.extra_categories = config_value(:EXTRA_CATEGORIES) || []
266
alt_moved = config_value(:ALT_MOVED) || []
267
unless alt_moved.empty?
268
alt_moved.each do |f|
269
$portsdb.moved.fill(f)
270
end
271
end
272
end
273
274
def parse_pattern(str, regex = false)
275
if str[0] == ?:
276
regex = true
277
str = str[1..-1]
278
end
279
280
if regex
281
Regexp.new(str)
282
else
283
str
284
end
285
end
286
287
def stty_sane
288
system '/bin/stty', 'sane' if STDIN.tty?
289
end
290
291
def progress_message(message, io = STDOUT)
292
io.puts "---> " + message
293
end
294
295
def information_message(message, io = STDERR)
296
io.puts "++ " + message
297
end
298
299
def warning_message(message, io = STDERR)
300
io.puts "** " + message
301
end
302
303
def all?(str)
304
/^a/i =~ str
305
end
306
307
def yes?(str)
308
/^y/i =~ str
309
end
310
311
def no?(str)
312
/^n/i =~ str
313
end
314
315
def yesno_str(yes)
316
if yes then 'yes' else 'no' end
317
end
318
319
def prompt_yesno(message = "OK?", yes_by_default = false)
320
if $automatic
321
input = yesno_str(yes_by_default)
322
323
if $verbose
324
print "#{message} [#{yesno_str(yes_by_default)}] "
325
puts input
326
end
327
else
328
print "#{message} [#{yesno_str(yes_by_default)}] "
329
330
STDOUT.flush
331
input = (STDIN.gets || '').strip
332
end
333
334
if yes_by_default
335
!no?(input)
336
else
337
yes?(input)
338
end
339
end
340
341
def prompt_yesnoall(message = "OK?", yes_by_default = false)
342
print "#{message} ([y]es/[n]o/[a]ll) [#{yesno_str(yes_by_default)}] "
343
344
if $automatic
345
input = yesno_str(yes_by_default)
346
puts input if $verbose
347
else
348
STDOUT.flush
349
input = (STDIN.gets || '').strip
350
end
351
352
if all?(input)
353
:all
354
elsif yes_by_default
355
!no?(input)
356
else
357
yes?(input)
358
end
359
end
360
361
def matchlen(a, b)
362
i = 0
363
0.upto(a.size) { |i| a[i] != b[i] and break }
364
i
365
end
366
367
def input_line(prompt, add_history = nil, completion_proc = nil)
368
prev_proc = Readline.completion_proc
369
370
Readline.completion_append_character = nil if Readline.respond_to?(:completion_append_character=)
371
Readline.completion_proc = completion_proc if completion_proc.respond_to?(:call)
372
373
Readline.readline(prompt, add_history)
374
ensure
375
Readline.completion_proc = prev_proc if prev_proc.respond_to?(:call)
376
end
377
378
def input_file(prompt, dir, add_history = nil)
379
Dir.chdir(dir) {
380
return input_line(prompt, add_history, Readline::FILENAME_COMPLETION_PROC)
381
}
382
end
383
384
OPTIONS_NONE = 0x00
385
OPTIONS_SKIP = 0x01
386
OPTIONS_DELETE = 0x02
387
OPTIONS_ALL = 0x04
388
OPTIONS_HISTORY = 0x08
389
390
def choose_from_options(message = 'Input?', options = nil, flags = OPTIONS_NONE)
391
skip = (flags & OPTIONS_SKIP).nonzero?
392
delete = (flags & OPTIONS_DELETE).nonzero?
393
all = (flags & OPTIONS_ALL).nonzero?
394
history = (flags & OPTIONS_HISTORY).nonzero?
395
396
completion_proc = nil
397
398
unless options.nil?
399
case options.size
400
when 0
401
return :skip
402
else
403
completion_proc = proc { |head|
404
len = head.size
405
options.select { |option| head == option[0, len] }
406
}
407
end
408
end
409
410
loop do
411
input = input_line(message + ' (? to help): ', history, completion_proc)
412
413
if input.nil?
414
print "\n"
415
416
next if not delete
417
418
if all
419
ans = prompt_yesnoall("Delete this?", true)
420
else
421
ans = prompt_yesno("Delete this?", true)
422
end
423
424
if ans == :all
425
return :delete_all
426
elsif ans
427
return :delete
428
end
429
430
next
431
end
432
433
input.strip!
434
435
case input
436
when '.'
437
return :abort
438
when '?'
439
print ' [Enter] to skip,' if skip
440
print ' [Ctrl]+[D] to delete,' if delete
441
print ' [.][Enter] to abort, [Tab] to complete'
442
print "\n"
443
next
444
when ''
445
if skip
446
if all
447
ans = prompt_yesnoall("Skip this?", true)
448
else
449
ans = prompt_yesno("Skip this?", true)
450
end
451
452
if ans == :all
453
return :skip_all
454
elsif ans
455
return :skip
456
end
457
end
458
459
next
460
else
461
if options.include?(input)
462
return input
463
end
464
465
print "Please choose one of these:\n"
466
467
if options.size <= 20
468
puts options.join(' ')
469
else
470
puts options[0, 20].join(' ') + " ..."
471
end
472
end
473
end
474
475
# not reached
476
end
477
478
class CommandFailedError < StandardError
479
end
480
481
# xsystem
482
def __system(x, *args)
483
progress_message "[Executing: #{args.join(" ")}]" if ENV['PORTUPGRADE_DEBUG']
484
system(*args) and return true
485
486
if x
487
raise CommandFailedError, format('Command failed [exit code %d]: %s', $? >> 8, shelljoin(*args))
488
end
489
490
false
491
end
492
def xsystem(*args)
493
__system(true, *args)
494
end
495
496
# sudo, xsudo
497
def __sudo(x, *args)
498
if $sudo && Process.euid != 0
499
if $sudo_args.grep(/%s/).empty?
500
args = $sudo_args + args
501
else
502
args = $sudo_args.map { |arg|
503
format(arg, shelljoin(*args)) rescue arg
504
}
505
end
506
507
progress_message "[Executing a command as root: " + shelljoin(*args) + "]"
508
end
509
510
__system(x, *args)
511
end
512
def sudo(*args)
513
__sudo(false, *args)
514
end
515
def xsudo(*args)
516
__sudo(true, *args)
517
end
518
519
# system!, xsystem!
520
alias system! sudo
521
alias xsystem! xsudo
522
523
def script_path
524
# If a fixed/custom script(1) is installed by the port, use that version.
525
# See #8
526
custom_script = "#{PREFIX}/libexec/pkgtools/script"
527
if File.exists?(custom_script)
528
$script_path = custom_script
529
else
530
$script_path = '/usr/bin/script'
531
end
532
$script_path
533
end
534
535
def logged_command(file, args)
536
if !file
537
args
538
else
539
[script_path(), '-qa', file, *args]
540
end
541
end
542
543
# script, xscript
544
def __script(x, file, *args)
545
__system(x, *logged_command(file, args))
546
end
547
def script(file, *args)
548
__script(false, file, *args)
549
end
550
def xscript(file, *args)
551
__script(true, file, *args)
552
end
553
554
# script!, xscript!
555
def __script!(x, file, *args)
556
__sudo(x, *logged_command(file, args))
557
end
558
def script!(file, *args)
559
__script!(false, file, *args)
560
end
561
def xscript!(file, *args)
562
__script!(true, file, *args)
563
end
564
565
# raises CommandFailedError
566
def install_data(src, dst, backup = false)
567
cmd = ['/usr/bin/install']
568
cmd.push('-b') if backup
569
cmd.push('-m', '644', src, dst)
570
571
begin
572
xsystem(*cmd)
573
rescue CommandFailedError => e
574
if $sudo && Process.euid != 0
575
information_message "Retrying install as root"
576
xsystem!(*cmd)
577
else
578
raise e
579
end
580
end
581
end
582
583
# raises CommandFailedError and Errno::*
584
def unlink_file(file)
585
File.exist?(file) or return
586
587
begin
588
File.unlink(file)
589
rescue => e
590
raise e if e.class == PkgDB::NeedsPkgNGSupport
591
if $sudo && Process.euid != 0
592
xsystem!('/bin/rm', '-f', file)
593
else
594
raise e
595
end
596
end
597
end
598
599
# backquote
600
def __backquote(x, sudo, *args)
601
if sudo && Process.euid != 0
602
if $sudo_args.grep(/%s/).empty?
603
args = $sudo_args + args
604
else
605
args = $sudo_args.map { |arg|
606
format(arg, shelljoin(*args)) rescue arg
607
}
608
end
609
610
cmdline = shelljoin(*args)
611
612
progress_message "[Executing a command as root: " + cmdline + "]"
613
else
614
cmdline = shelljoin(*args)
615
progress_message "[Executing: #{cmdline}]" if ENV['PORTUPGRADE_DEBUG']
616
end
617
618
str = `#{cmdline}` and return str
619
620
if x
621
raise CommandFailedError, format('Command failed [exit code %d]: %s', $? >> 8, cmdline)
622
end
623
624
false
625
end
626
def backquote(*args)
627
__backquote(false, false, *args)
628
end
629
def xbackquote(*args)
630
__backquote(true, false, *args)
631
end
632
def backquote!(*args)
633
__backquote(false, $sudo, *args)
634
end
635
def xbackquote!(*args)
636
__backquote(true, $sudo, *args)
637
end
638
639
def grep_q_file(re, file)
640
case re
641
when Regexp
642
pat = re.source
643
else
644
pat = re.to_s
645
end
646
647
system '/usr/bin/egrep', '-q', pat, file
648
end
649
650
def alt_dep(dep, origin = nil)
651
hash = config_value(:ALT_PKGDEP) or return nil
652
653
if dep == ''
654
dep = $pkgdb.deorigin(origin).to_s
655
end
656
657
hash.each do |pat, alt|
658
begin
659
pat = parse_pattern(pat)
660
rescue RegexpError => e
661
warning_message e.message.capitalize
662
next
663
end
664
665
# pattern allowed both in origin and pkgname
666
if pat.index('/')
667
next if !origin || !File.fnmatch?(pat, origin)
668
elsif !File.fnmatch?(pat, dep)
669
next
670
end
671
672
case alt
673
when :delete, :skip
674
return [alt]
675
else
676
begin
677
alt = parse_pattern(alt)
678
rescue RegexpError => e
679
warning_message e.message.capitalize
680
next
681
end
682
683
pkgnames = $pkgdb.glob(alt, false)
684
685
if pkgnames.empty?
686
return nil
687
else
688
return pkgnames
689
end
690
end
691
end
692
693
nil
694
end
695
696
# raises StandardError
697
def modify_pkgdep(pkgname, dep, newdep, neworigin = nil)
698
return if $pkgdb.with_pkgng? # PKGNG doesn't need this.
699
pkgdir = $pkgdb.pkgdir(pkgname)
700
return if pkgdir.nil? || !File.directory?(pkgdir)
701
changed = false
702
703
pkgver_re = %r{-\d\S*$}
704
file = $pkgdb.pkg_contents(pkgname)
705
706
if ! newdep == :add
707
grep_q_file(/^@pkgdep[[:space:]]+#{Regexp.quote(dep)}$/, file) or return
708
end
709
710
case newdep
711
when :delete
712
neworigin = nil
713
else
714
neworigin ||= $pkgdb.origin(newdep)
715
end
716
717
content = File.open(file)
718
719
pkgdeps = Set.new
720
721
deporigin = nil # what to do with the next DEPORIGIN
722
723
head_lines = []
724
depends_lines = []
725
tail_lines = []
726
727
pkgdep_undeleted = false
728
deporigin_undeleted = false
729
last_correct = false
730
731
content.each do |line|
732
case line
733
when /^@pkgdep\s+(\S+)/
734
deporigin = :keep
735
736
pkgdep = $1
737
738
if pkgdeps.include?(pkgdep) # remove duplicates
739
deporigin = :delete
740
changed = true
741
next
742
end
743
744
pkgdeps << pkgdep
745
746
if $1 == dep
747
if newdep == :delete
748
depends_lines << "@comment DELETED:pkgdep #{pkgdep}\n"
749
deporigin = :commentout
750
else
751
depends_lines << "@pkgdep #{newdep}\n"
752
753
if neworigin
754
depends_lines << "@comment DEPORIGIN:#{neworigin}\n"
755
end
756
757
deporigin = :delete
758
759
pkgdeps << newdep
760
end
761
changed = true
762
else
763
depends_lines << line
764
end
765
when /^@comment\s+DEPORIGIN:(\S+)/
766
case deporigin
767
when :commentout
768
depends_lines << "@comment DELETED:DEPORIGIN:#{$1}\n"
769
changed = true
770
when :keep
771
depends_lines << line
772
else # :delete, nil
773
# no output
774
changed = true
775
end
776
777
deporigin = nil
778
when /^@comment\s+DELETED:(pkgdep |DEPORIGIN:)(\S+)/
779
# Undelete it if requested
780
if newdep == :add
781
keyword = $1
782
data = $2
783
if keyword == "pkgdep " &&
784
data.sub(pkgver_re,'') == dep.sub(pkgver_re,'')
785
depends_lines << "@pkgdep #{dep}\n"
786
pkgdep_undeleted = true
787
last_correct = true
788
changed = true
789
next
790
elsif keyword == "DEPORIGIN:" && data == neworigin
791
# Undelete DEPORIGIN only if we sure the last line is correct
792
if last_correct
793
depends_lines << "@comment DEPORIGIN:#{neworigin}\n"
794
deporigin_undeleted = true
795
changed = true
796
next
797
end
798
end
799
depends_lines << line
800
else
801
depends_lines << line
802
end
803
else
804
if depends_lines.empty?
805
head_lines << line
806
else
807
tail_lines << line
808
end
809
810
deporigin = nil
811
last_correct = false
812
end
813
end
814
content.close
815
816
if newdep == :add && (!pkgdep_undeleted || !deporigin_undeleted)
817
# Remove partly undeleted entry
818
if pkgdep_undeleted
819
depends_lines.delete_if { |line| line == "@pkgdep #{dep}\n" }
820
end
821
# and just add correct lines
822
depends_lines << "@pkgdep #{dep}\n"
823
depends_lines << "@comment DEPORIGIN:#{neworigin}\n"
824
changed = true
825
end
826
827
if changed
828
lines = head_lines + depends_lines + tail_lines
829
w = Tempfile.new(File.basename(file))
830
w.print(lines.join())
831
w.close
832
tmpfile = w.path
833
834
progress_message "Modifying #{file}" if $verbose
835
836
install_data(tmpfile, file)
837
end
838
rescue => e
839
raise e if e.class == PkgDB::NeedsPkgNGSupport
840
raise "Failed to rewrite #{file}: " + e.message
841
end
842
843
# raises CommandFailedError
844
def update_pkgdep(oldpkgname, newpkgname, neworigin = nil)
845
return if oldpkgname == newpkgname
846
847
progress_message "Updating dependency info" if $verbose
848
849
$pkgdb.installed_pkgs.each do |pkgname|
850
modify_pkgdep(pkgname, oldpkgname, newpkgname, neworigin)
851
end
852
end
853
854
def modify_origin(pkgname, origin)
855
if $pkgdb.with_pkgng?
856
oldorigin = $pkgdb.origin(pkgname)
857
str = backquote!(PkgDB::command(:pkg), 'set', '-yo', "#{oldorigin}:#{origin}")
858
else
859
contents_file = $pkgdb.pkg_contents(pkgname)
860
861
if grep_q_file(/^@comment[ \t]+ORIGIN:/, contents_file)
862
command = shelljoin('sed',
863
"s|^\\(@comment[ \t][ \t]*ORIGIN:\\).*$|\\1#{origin}|")
864
else
865
command = "(cat; echo '@comment ORIGIN:#{origin}')"
866
end
867
868
filter_file(command, contents_file)
869
end
870
871
$pkgdb.set_origin(pkgname, origin)
872
rescue => e
873
raise e if e.class == PkgDB::NeedsPkgNGSupport
874
raise "Failed to rewrite #{contents_file}: " + e.message
875
end
876
877
def identify_pkg(path)
878
dir, file = File.split(path)
879
880
pkgname = nil
881
origin = nil
882
pkgdep = []
883
884
if $pkgdb.with_pkgng?
885
origin = backquote!(PkgDB::command(:pkg), 'query', '-F', "#{dir}/#{file}",
886
'%o').chomp
887
pkgname = backquote!(PkgDB::command(:pkg), 'query', '-F', "#{dir}/#{file}",
888
'%n-%v').chomp
889
pkgdep = backquote!(PkgDB::command(:pkg), 'query', '-F', "#{dir}/#{file}",
890
'%dn-%dv').split("\n")
891
else
892
IO.popen("cd #{dir} && #{PkgDB::command(:pkg_info)} -qfo #{file}") do |r|
893
r.each do |line|
894
case line
895
when /^@name\s+(\S*)/
896
pkgname = $1
897
when /^@pkgdep\s+(\S*)/
898
pkgdep << $1
899
when /^(\S+\/\S+)$/ # /
900
origin = $1
901
end
902
end
903
end
904
end
905
906
return pkgname, origin, pkgdep
907
rescue => e
908
raise e if e.class == PkgDB::NeedsPkgNGSupport
909
warning_message e.message
910
return nil
911
end
912
913
# raises CommandFailedError
914
def filter_file(command, file, backup = false)
915
w = Tempfile.new(File.basename(file))
916
w.close
917
tmpfile = w.path
918
919
xsystem("#{command} < #{file} > #{tmpfile}")
920
921
progress_message "Filtering #{file}" if $verbose
922
923
install_data(tmpfile, file, backup)
924
end
925
926
def search_paths(command)
927
ENV['PATH'].split(':').each do |dir|
928
path = File.join(dir, command)
929
stat = File.stat(path)
930
return path if stat.file? && stat.executable?(path)
931
end
932
933
nil
934
end
935
936
def timer_start(name, verbose = $verbose)
937
$timer[name] = start_time = Time.now
938
939
if verbose
940
progress_message "#{name} started at: #{start_time.rfc2822}"
941
end
942
end
943
944
def timer_end(name, verbose = $verbose)
945
return if $timer.nil?
946
$timer.key?(name) or return
947
948
end_time = Time.now
949
950
start_time = $timer[name]
951
952
time = end_time - start_time
953
days = time/86400
954
str_time = ""
955
if days.to_i > 0
956
str_time = "#{days.to_i} day"
957
str_time += "s" if days.to_i > 1
958
str_time += " and "
959
end
960
str_time += Time.at(time).utc.strftime("%T")
961
962
if verbose
963
progress_message "#{name} ended at: #{end_time.rfc2822} (consumed #{str_time})"
964
end
965
966
$timer.delete(name)
967
end
968
969
class PkgResult
970
attr_accessor :item, :result, :info
971
972
def initialize(item, result, info = nil)
973
if item.nil? then
974
raise ArgumentError
975
end
976
@item = item
977
@result = result
978
@info = info
979
end
980
981
def done?
982
@result == :done
983
end
984
985
def ignored?
986
@result == :ignored
987
end
988
989
def skipped?
990
@result == :skipped
991
end
992
993
def error?
994
!@result.is_a?(Symbol)
995
end
996
997
def ok?
998
done?
999
end
1000
1001
def failed?
1002
!ok?
1003
end
1004
1005
def self.phrase(result, long = false)
1006
case result
1007
when :done
1008
"done"
1009
when :ignored
1010
long ? "been ignored" : "ignored"
1011
when :skipped
1012
long ? "been skipped" : "skipped"
1013
else
1014
"failed"
1015
end
1016
end
1017
1018
def phrase(long = false)
1019
PkgResult.phrase(@result, long)
1020
end
1021
1022
def self.sign(result, long = false)
1023
case result
1024
when :done
1025
sign = "+"
1026
when :ignored
1027
sign = "-"
1028
when :skipped
1029
sign = "*"
1030
else
1031
sign = "!"
1032
end
1033
1034
if long
1035
sign << ":" << phrase(result)
1036
else
1037
sign
1038
end
1039
end
1040
1041
def sign(long = false)
1042
PkgResult.sign(@result, long)
1043
end
1044
1045
def message
1046
case @result
1047
when Symbol
1048
nil
1049
when Exception
1050
@result.message
1051
else
1052
@result
1053
end
1054
end
1055
1056
def write(io = STDOUT, prefix = "\t")
1057
if @info
1058
str = "#{@item} (#{@info})"
1059
else
1060
str = @item
1061
end
1062
1063
line = prefix.dup << sign << " " << str
1064
1065
if str = message()
1066
line << "\t(" << str << ")"
1067
end
1068
1069
io.puts line
1070
end
1071
1072
def self.legend(long = false)
1073
if long
1074
[:done, :ignored, :skipped, :error]
1075
else
1076
[:ignored, :skipped, :error]
1077
end.map { |r| sign(r, true) }.join(" / ")
1078
end
1079
end
1080
1081
class PkgResultSet < SimpleDelegator
1082
def initialize
1083
@array = []
1084
super(@array)
1085
end
1086
1087
def [](item)
1088
@array.find { |r| r.item == item }
1089
end
1090
1091
def progress_message(message, io = STDOUT)
1092
io.puts "---> " + message
1093
end
1094
1095
def warning_message(message, io = STDERR)
1096
io.puts "** " + message
1097
end
1098
1099
def include?(item)
1100
@array.any? { |r| r.item == item }
1101
end
1102
1103
def summary
1104
count = Hash.new(0)
1105
1106
each do |result|
1107
if result.done?
1108
count[:done] += 1
1109
elsif result.error?
1110
count[:error] += 1
1111
elsif result.ignored?
1112
count[:ignored] += 1
1113
elsif result.skipped?
1114
count[:skipped] += 1
1115
end
1116
end
1117
1118
[:done, :ignored, :skipped, :error].map { |e|
1119
"#{count[e]} #{PkgResult.phrase(e)}"
1120
}.join(', ').sub(/, ([^,]+)$/, " and \\1")
1121
end
1122
1123
def write(io = STDOUT, prefix = "\t", verbose = $verbose)
1124
errors = 0
1125
1126
each do |result|
1127
next if !verbose && result.ok?
1128
1129
errors += 1 if result.error?
1130
1131
result.write(io, prefix)
1132
end
1133
1134
errors
1135
end
1136
1137
def show(done_service = 'done', verbose = $verbose)
1138
if verbose
1139
if empty?
1140
warning_message "None has been #{done_service}."
1141
return 0
1142
end
1143
1144
progress_message "Listing the results (" <<
1145
PkgResult.legend(true) << ")"
1146
else
1147
if find { |r| r.ignored? || r.failed? }
1148
warning_message "Listing the failed packages (" <<
1149
PkgResult.legend() << ")"
1150
end
1151
end
1152
1153
errors = write(STDOUT, "\t", verbose)
1154
1155
progress_message "Packages processed: " << summary() if verbose
1156
1157
errors
1158
end
1159
end
1160
1161
def set_signal_handlers
1162
for sig in [:SIGINT, :SIGQUIT, :SIGTERM]
1163
trap(sig) do
1164
puts "\nInterrupted."
1165
$interrupt_proc.call if $interrupt_proc
1166
stty_sane
1167
exit
1168
end
1169
end
1170
1171
# trap(:SIGCHLD) do
1172
# begin
1173
# true while Process.waitpid(-1, Process::WNOHANG)
1174
# rescue Errno::ECHILD
1175
# end
1176
# end
1177
end
1178
1179
module PkgConfig
1180
uname = `uname -rm`.chomp
1181
1182
if m = /^(((\d+)(?:\.\d+[^.\-]*?)+)-(\w+)(-\S+)?) (\w+)$/.match(uname)
1183
OS_RELEASE, OS_REVISION, OS_MAJOR,
1184
OS_BRANCH, os_patchlevel, OS_PLATFORM = m[1..-1]
1185
OS_PATCHLEVEL = os_patchlevel || ""
1186
1187
case OS_BRANCH
1188
when /^CURRENT$/ # <n>-current
1189
OS_PKGBRANCH = sprintf('%s-%s', OS_MAJOR, OS_BRANCH.downcase)
1190
when /^RELEASE$/ # <n>.<m>-release
1191
OS_PKGBRANCH = sprintf('%s-%s', OS_REVISION, OS_BRANCH.downcase)
1192
else # <n>-stable
1193
# when /^(PRERELEASE|RC\d*|ALPHA|BETA)$/
1194
OS_PKGBRANCH = sprintf('%s-%s', OS_MAJOR, 'stable')
1195
end
1196
else
1197
STDERR.puts "uname(1) could be broken - cannot parse the output: #{uname}"
1198
end
1199
1200
def pkg_site_mirror(root = ENV['PACKAGEROOT'] || 'ftp://ftp.FreeBSD.org/')
1201
sprintf('%s/pub/FreeBSD/ports/%s/packages-%s/',
1202
root, OS_PLATFORM, OS_PKGBRANCH)
1203
end
1204
1205
def pkg_site_primary()
1206
pkg_site_mirror('ftp://ftp.FreeBSD.org')
1207
end
1208
1209
def pkg_site_builder(latest = false)
1210
run = latest ? 'latest' : 'full'
1211
1212
case OS_PLATFORM
1213
when 'i386', 'sparc64', 'amd64', 'ia64'
1214
sprintf('http://pointyhat.FreeBSD.org/errorlogs/%s-%s-packages-%s/',
1215
OS_PLATFORM, OS_MAJOR, run)
1216
else
1217
raise sprintf('There is no official package builder site yet for the %s platform.',
1218
OS_PLATFORM)
1219
end
1220
end
1221
1222
module_function :pkg_site_mirror, :pkg_site_primary, :pkg_site_builder
1223
1224
def localbase()
1225
$portsdb.localbase
1226
end
1227
1228
def x11base()
1229
$portsdb.x11base
1230
end
1231
1232
module_function :localbase, :x11base
1233
1234
def deorigin(origin)
1235
if ret = $pkgdb.deorigin(origin)
1236
ret.first
1237
else
1238
raise "No package is found to be installed from '#{origin}'!"
1239
end
1240
end
1241
1242
def enabled_rc_scripts(origin_or_pkgname)
1243
if origin_or_pkgname.include?('/')
1244
pkgname = deorigin(origin_or_pkgname)
1245
else
1246
pkgname = origin_or_pkgname
1247
end
1248
1249
pkg = PkgInfo.new(pkgname)
1250
1251
re = %r"^((?:#{Regexp.quote(localbase())}|#{Regexp.quote(x11base())})/etc/rc\.d/[^/]+(\.sh)?)(\.\w+)?$"
1252
1253
ret = []
1254
pkg.files.each { |file|
1255
ret << $1 if re =~ file && File.executable?($1)
1256
}
1257
ret
1258
end
1259
1260
def disabled_rc_scripts(origin_or_pkgname)
1261
if origin_or_pkgname.include?('/')
1262
pkgname = deorigin(origin_or_pkgname)
1263
else
1264
pkgname = origin_or_pkgname
1265
end
1266
1267
pkg = PkgInfo.new(pkgname)
1268
1269
re = %r"^((?:#{Regexp.quote(localbase())}|#{Regexp.quote(x11base())})/etc/rc\.d/[^/]+(\.sh)?)(\.\w+)$"
1270
1271
pkg.files.select { |file|
1272
re =~ file && File.executable?(file)
1273
}
1274
end
1275
1276
def cmd_start_rc(origin)
1277
STDERR.puts("cmd_start_rc is deprecated. pkg(8) supports HANDLE_RC_SCRIPTS: true in /usr/local/etc/pkg.conf")
1278
enabled_rc_scripts(origin).map { |file| "/usr/sbin/service #{File.basename(file)} start" }.join("; ")
1279
end
1280
1281
def cmd_stop_rc(origin)
1282
STDERR.puts("cmd_stop_rc is deprecated. pkg(8) supports HANDLE_RC_SCRIPTS: true in /usr/local/etc/pkg.conf")
1283
enabled_rc_scripts(origin).map { |file| "/usr/sbin/service #{File.basename(file)} stop" }.join("; ")
1284
end
1285
1286
def cmd_restart_rc(origin)
1287
STDERR.puts("cmd_restart_rc is deprecated. pkg(8) supports HANDLE_RC_SCRIPTS: true in /usr/local/etc/pkg.conf")
1288
enabled_rc_scripts(origin).map { |file| "/usr/sbin/service #{File.basename(file)} stop; sleep 3; /usr/sbin/service #{File.basename(file)} start" }.join("; ")
1289
end
1290
1291
def cmd_disable_rc(origin)
1292
enabled_rc_scripts(origin).map { |file| "mv -f #{file} #{file}.disable" }.join("; ")
1293
end
1294
1295
def cmd_enable_rc(origin)
1296
disabled_rc_scripts(origin).map { |file| "cp -p #{file} #{file.sub(/\.\w+$/, '')}" }.join("; ")
1297
end
1298
1299
def include_eval(file)
1300
file = File.join(PREFIX, file)
1301
1302
File.exist?(file) or return false
1303
1304
begin
1305
load file
1306
rescue Exception => e
1307
STDERR.puts "** Error occured reading #{file}:",
1308
e.message.gsub(/^/, "\t")
1309
exit 1
1310
end
1311
end
1312
1313
def include_hash(glob)
1314
hash = Hash.new
1315
Dir.glob(File.join(PREFIX, glob)) do |f|
1316
if FileTest.file?(f)
1317
File.open(f) do |file|
1318
file.each_line do |line|
1319
next if /^#/ =~ line
1320
if /=>/ =~ line
1321
key, val = line.split('=>')
1322
key.strip!.gsub!(/['"]/, '')
1323
val.strip!.gsub!(/['",]/, '')
1324
hash[key] = val
1325
else
1326
unless line.empty?
1327
raise "File #{f}: syntax error in line: #{line}"
1328
end
1329
end
1330
end
1331
end
1332
end
1333
end
1334
1335
hash
1336
end
1337
1338
module_function :deorigin,
1339
:enabled_rc_scripts, :disabled_rc_scripts,
1340
:cmd_start_rc, :cmd_stop_rc, :cmd_restart_rc,
1341
:cmd_disable_rc, :cmd_enable_rc,
1342
:include_eval, :include_hash
1343
end
1344
1345