reap 0.4.0

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.
Files changed (48) hide show
  1. data/ChangeLog +2 -0
  2. data/LICENSE +11 -0
  3. data/README +59 -0
  4. data/Reapfile +85 -0
  5. data/bin/reap +103 -0
  6. data/doc/classes/Reap/AnnounceTask.html +331 -0
  7. data/doc/classes/Reap/ChmodTask.html +278 -0
  8. data/doc/classes/Reap/PackageTask.html +372 -0
  9. data/doc/classes/Reap/PublishTask.html +231 -0
  10. data/doc/classes/Reap/RDocTask.html +254 -0
  11. data/doc/classes/Reap/Runner.html +307 -0
  12. data/doc/classes/Reap/Task.html +390 -0
  13. data/doc/classes/Reap/TemplateTask.html +233 -0
  14. data/doc/classes/Reap/TestTask.html +242 -0
  15. data/doc/classes/Reap.html +184 -0
  16. data/doc/created.rid +1 -0
  17. data/doc/files/LICENSE.html +119 -0
  18. data/doc/files/README.html +194 -0
  19. data/doc/files/lib/reap/reap_rb.html +110 -0
  20. data/doc/files/lib/reap/runner_rb.html +113 -0
  21. data/doc/files/lib/reap/task_rb.html +108 -0
  22. data/doc/files/lib/reap/tasks/announce-task_rb.html +113 -0
  23. data/doc/files/lib/reap/tasks/chmod-task_rb.html +101 -0
  24. data/doc/files/lib/reap/tasks/package-task_rb.html +108 -0
  25. data/doc/files/lib/reap/tasks/publish-task_rb.html +101 -0
  26. data/doc/files/lib/reap/tasks/rdoc-task_rb.html +101 -0
  27. data/doc/files/lib/reap/tasks/template-task_rb.html +108 -0
  28. data/doc/files/lib/reap/tasks/test-task_rb.html +101 -0
  29. data/doc/files/lib/reap/template_rb.html +101 -0
  30. data/doc/fr_class_index.html +36 -0
  31. data/doc/fr_file_index.html +39 -0
  32. data/doc/fr_method_index.html +69 -0
  33. data/doc/index.html +24 -0
  34. data/doc/rdoc-style.css +208 -0
  35. data/lib/reap/reap.rb +11 -0
  36. data/lib/reap/runner.rb +95 -0
  37. data/lib/reap/task.rb +103 -0
  38. data/lib/reap/tasks/announce-task.rb +140 -0
  39. data/lib/reap/tasks/chmod-task.rb +72 -0
  40. data/lib/reap/tasks/package-task.rb +153 -0
  41. data/lib/reap/tasks/publish-task.rb +33 -0
  42. data/lib/reap/tasks/rdoc-task.rb +57 -0
  43. data/lib/reap/tasks/template-task.rb +38 -0
  44. data/lib/reap/tasks/test-task.rb +45 -0
  45. data/lib/reap/template.rb +72 -0
  46. data/setup.rb +1361 -0
  47. data/test/test_require.rb +11 -0
  48. metadata +112 -0
data/setup.rb ADDED
@@ -0,0 +1,1361 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # setup.rb
4
+ #
5
+ # Copyright (c) 2000-2004 Minero Aoki
6
+ #
7
+ # This program is free software.
8
+ # You can distribute/modify this program under the terms of
9
+ # the GNU LGPL, Lesser General Public License version 2.1.
10
+ #
11
+
12
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
13
+ module Enumerable
14
+ alias map collect
15
+ end
16
+ end
17
+
18
+ unless File.respond_to?(:read) # Ruby 1.6
19
+ def File.read(fname)
20
+ open(fname) {|f|
21
+ return f.read
22
+ }
23
+ end
24
+ end
25
+
26
+ def File.binread(fname)
27
+ open(fname, 'rb') {|f|
28
+ return f.read
29
+ }
30
+ end
31
+
32
+ # for corrupted windows stat(2)
33
+ def File.dir?(path)
34
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
35
+ end
36
+
37
+
38
+ class SetupError < StandardError; end
39
+
40
+ def setup_rb_error(msg)
41
+ raise SetupError, msg
42
+ end
43
+
44
+ #
45
+ # Config
46
+ #
47
+
48
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
49
+ ARGV.delete(arg)
50
+ require arg.split(/=/, 2)[1]
51
+ $".push 'rbconfig.rb'
52
+ else
53
+ require 'rbconfig'
54
+ end
55
+
56
+ def multipackage_install?
57
+ FileTest.directory?(File.dirname($0) + '/packages')
58
+ end
59
+
60
+
61
+ class ConfigItem
62
+ def initialize(name, template, default, desc)
63
+ @name = name.freeze
64
+ @template = template
65
+ @value = default
66
+ @default = default.dup.freeze
67
+ @description = desc
68
+ end
69
+
70
+ attr_reader :name
71
+ attr_reader :description
72
+
73
+ attr_accessor :default
74
+ alias help_default default
75
+
76
+ def help_opt
77
+ "--#{@name}=#{@template}"
78
+ end
79
+
80
+ def value
81
+ @value
82
+ end
83
+
84
+ def eval(table)
85
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
86
+ end
87
+
88
+ def set(val)
89
+ @value = check(val)
90
+ end
91
+
92
+ private
93
+
94
+ def check(val)
95
+ setup_rb_error "config: --#{name} requires argument" unless val
96
+ val
97
+ end
98
+ end
99
+
100
+ class BoolItem < ConfigItem
101
+ def config_type
102
+ 'bool'
103
+ end
104
+
105
+ def help_opt
106
+ "--#{@name}"
107
+ end
108
+
109
+ private
110
+
111
+ def check(val)
112
+ return 'yes' unless val
113
+ unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
114
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
115
+ end
116
+ (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
117
+ end
118
+ end
119
+
120
+ class PathItem < ConfigItem
121
+ def config_type
122
+ 'path'
123
+ end
124
+
125
+ private
126
+
127
+ def check(path)
128
+ setup_rb_error "config: --#{@name} requires argument" unless path
129
+ path[0,1] == '$' ? path : File.expand_path(path)
130
+ end
131
+ end
132
+
133
+ class ProgramItem < ConfigItem
134
+ def config_type
135
+ 'program'
136
+ end
137
+ end
138
+
139
+ class SelectItem < ConfigItem
140
+ def initialize(name, template, default, desc)
141
+ super
142
+ @ok = template.split('/')
143
+ end
144
+
145
+ def config_type
146
+ 'select'
147
+ end
148
+
149
+ private
150
+
151
+ def check(val)
152
+ unless @ok.include?(val.strip)
153
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
154
+ end
155
+ val.strip
156
+ end
157
+ end
158
+
159
+ class PackageSelectionItem < ConfigItem
160
+ def initialize(name, template, default, help_default, desc)
161
+ super name, template, default, desc
162
+ @help_default = help_default
163
+ end
164
+
165
+ attr_reader :help_default
166
+
167
+ def config_type
168
+ 'package'
169
+ end
170
+
171
+ private
172
+
173
+ def check(val)
174
+ unless File.dir?("packages/#{val}")
175
+ setup_rb_error "config: no such package: #{val}"
176
+ end
177
+ val
178
+ end
179
+ end
180
+
181
+ class ConfigTable_class
182
+
183
+ def initialize(items)
184
+ @items = items
185
+ @table = {}
186
+ items.each do |i|
187
+ @table[i.name] = i
188
+ end
189
+ ALIASES.each do |ali, name|
190
+ @table[ali] = @table[name]
191
+ end
192
+ end
193
+
194
+ include Enumerable
195
+
196
+ def each(&block)
197
+ @items.each(&block)
198
+ end
199
+
200
+ def key?(name)
201
+ @table.key?(name)
202
+ end
203
+
204
+ def lookup(name)
205
+ @table[name] or raise ArgumentError, "no such config item: #{name}"
206
+ end
207
+
208
+ def add(item)
209
+ @items.push item
210
+ @table[item.name] = item
211
+ end
212
+
213
+ def remove(name)
214
+ item = lookup(name)
215
+ @items.delete_if {|i| i.name == name }
216
+ @table.delete_if {|name, i| i.name == name }
217
+ item
218
+ end
219
+
220
+ def new
221
+ dup()
222
+ end
223
+
224
+ def savefile
225
+ '.config'
226
+ end
227
+
228
+ def load
229
+ begin
230
+ t = dup()
231
+ File.foreach(savefile()) do |line|
232
+ k, v = *line.split(/=/, 2)
233
+ t[k] = v.strip
234
+ end
235
+ t
236
+ rescue Errno::ENOENT
237
+ setup_rb_error $!.message + "#{File.basename($0)} config first"
238
+ end
239
+ end
240
+
241
+ def save
242
+ @items.each {|i| i.value }
243
+ File.open(savefile(), 'w') {|f|
244
+ @items.each do |i|
245
+ f.printf "%s=%s\n", i.name, i.value if i.value
246
+ end
247
+ }
248
+ end
249
+
250
+ def [](key)
251
+ lookup(key).eval(self)
252
+ end
253
+
254
+ def []=(key, val)
255
+ lookup(key).set val
256
+ end
257
+
258
+ end
259
+
260
+ c = ::Config::CONFIG
261
+
262
+ rubypath = c['bindir'] + '/' + c['ruby_install_name']
263
+
264
+ major = c['MAJOR'].to_i
265
+ minor = c['MINOR'].to_i
266
+ teeny = c['TEENY'].to_i
267
+ version = "#{major}.#{minor}"
268
+
269
+ # ruby ver. >= 1.4.4?
270
+ newpath_p = ((major >= 2) or
271
+ ((major == 1) and
272
+ ((minor >= 5) or
273
+ ((minor == 4) and (teeny >= 4)))))
274
+
275
+ if c['rubylibdir']
276
+ # V < 1.6.3
277
+ _stdruby = c['rubylibdir']
278
+ _siteruby = c['sitedir']
279
+ _siterubyver = c['sitelibdir']
280
+ _siterubyverarch = c['sitearchdir']
281
+ elsif newpath_p
282
+ # 1.4.4 <= V <= 1.6.3
283
+ _stdruby = "$prefix/lib/ruby/#{version}"
284
+ _siteruby = c['sitedir']
285
+ _siterubyver = "$siteruby/#{version}"
286
+ _siterubyverarch = "$siterubyver/#{c['arch']}"
287
+ else
288
+ # V < 1.4.4
289
+ _stdruby = "$prefix/lib/ruby/#{version}"
290
+ _siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
291
+ _siterubyver = _siteruby
292
+ _siterubyverarch = "$siterubyver/#{c['arch']}"
293
+ end
294
+ libdir = '-* dummy libdir *-'
295
+ stdruby = '-* dummy rubylibdir *-'
296
+ siteruby = '-* dummy site_ruby *-'
297
+ siterubyver = '-* dummy site_ruby version *-'
298
+ parameterize = lambda {|path|
299
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
300
+ .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\
301
+ .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\
302
+ .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\
303
+ .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
304
+ }
305
+ libdir = parameterize.call(c['libdir'])
306
+ stdruby = parameterize.call(_stdruby)
307
+ siteruby = parameterize.call(_siteruby)
308
+ siterubyver = parameterize.call(_siterubyver)
309
+ siterubyverarch = parameterize.call(_siterubyverarch)
310
+
311
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
312
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
313
+ else
314
+ makeprog = 'make'
315
+ end
316
+
317
+ common_conf = [
318
+ PathItem.new('prefix', 'path', c['prefix'],
319
+ 'path prefix of target environment'),
320
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
321
+ 'the directory for commands'),
322
+ PathItem.new('libdir', 'path', libdir,
323
+ 'the directory for libraries'),
324
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
325
+ 'the directory for shared data'),
326
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
327
+ 'the directory for man pages'),
328
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
329
+ 'the directory for man pages'),
330
+ PathItem.new('stdruby', 'path', stdruby,
331
+ 'the directory for standard ruby libraries'),
332
+ PathItem.new('siteruby', 'path', siteruby,
333
+ 'the directory for version-independent aux ruby libraries'),
334
+ PathItem.new('siterubyver', 'path', siterubyver,
335
+ 'the directory for aux ruby libraries'),
336
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
337
+ 'the directory for aux ruby binaries'),
338
+ PathItem.new('rbdir', 'path', '$siterubyver',
339
+ 'the directory for ruby scripts'),
340
+ PathItem.new('sodir', 'path', '$siterubyverarch',
341
+ 'the directory for ruby extentions'),
342
+ PathItem.new('rubypath', 'path', rubypath,
343
+ 'the path to set to #! line'),
344
+ ProgramItem.new('rubyprog', 'name', rubypath,
345
+ 'the ruby program using for installation'),
346
+ ProgramItem.new('makeprog', 'name', makeprog,
347
+ 'the make program to compile ruby extentions'),
348
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
349
+ 'shebang line (#!) editing mode'),
350
+ BoolItem.new('without-ext', 'yes/no', 'no',
351
+ 'does not compile/install ruby extentions')
352
+ ]
353
+ class ConfigTable_class # open again
354
+ ALIASES = {
355
+ 'std-ruby' => 'stdruby',
356
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
357
+ 'site-ruby' => 'siterubyver', # For backward compatibility
358
+ 'bin-dir' => 'bindir',
359
+ 'bin-dir' => 'bindir',
360
+ 'rb-dir' => 'rbdir',
361
+ 'so-dir' => 'sodir',
362
+ 'data-dir' => 'datadir',
363
+ 'ruby-path' => 'rubypath',
364
+ 'ruby-prog' => 'rubyprog',
365
+ 'ruby' => 'rubyprog',
366
+ 'make-prog' => 'makeprog',
367
+ 'make' => 'makeprog'
368
+ }
369
+ end
370
+ multipackage_conf = [
371
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
372
+ 'package names that you want to install'),
373
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
374
+ 'package names that you do not want to install')
375
+ ]
376
+ if multipackage_install?
377
+ ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
378
+ else
379
+ ConfigTable = ConfigTable_class.new(common_conf)
380
+ end
381
+
382
+
383
+ module MetaConfigAPI
384
+
385
+ def eval_file_ifexist(fname)
386
+ instance_eval File.read(fname), fname, 1 if File.file?(fname)
387
+ end
388
+
389
+ def config_names
390
+ ConfigTable.map {|i| i.name }
391
+ end
392
+
393
+ def config?(name)
394
+ ConfigTable.key?(name)
395
+ end
396
+
397
+ def bool_config?(name)
398
+ ConfigTable.lookup(name).config_type == 'bool'
399
+ end
400
+
401
+ def path_config?(name)
402
+ ConfigTable.lookup(name).config_type == 'path'
403
+ end
404
+
405
+ def value_config?(name)
406
+ case ConfigTable.lookup(name).config_type
407
+ when 'bool', 'path'
408
+ true
409
+ else
410
+ false
411
+ end
412
+ end
413
+
414
+ def add_config(item)
415
+ ConfigTable.add item
416
+ end
417
+
418
+ def add_bool_config(name, default, desc)
419
+ ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
420
+ end
421
+
422
+ def add_path_config(name, default, desc)
423
+ ConfigTable.add PathItem.new(name, 'path', default, desc)
424
+ end
425
+
426
+ def set_config_default(name, default)
427
+ ConfigTable.lookup(name).default = default
428
+ end
429
+
430
+ def remove_config(name)
431
+ ConfigTable.remove(name)
432
+ end
433
+
434
+ end
435
+
436
+
437
+ #
438
+ # File Operations
439
+ #
440
+
441
+ module FileOperations
442
+
443
+ def mkdir_p(dirname, prefix = nil)
444
+ dirname = prefix + File.expand_path(dirname) if prefix
445
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
446
+ return if no_harm?
447
+
448
+ # does not check '/'... it's too abnormal case
449
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
450
+ if /\A[a-z]:\z/i =~ dirs[0]
451
+ disk = dirs.shift
452
+ dirs[0] = disk + dirs[0]
453
+ end
454
+ dirs.each_index do |idx|
455
+ path = dirs[0..idx].join('')
456
+ Dir.mkdir path unless File.dir?(path)
457
+ end
458
+ end
459
+
460
+ def rm_f(fname)
461
+ $stderr.puts "rm -f #{fname}" if verbose?
462
+ return if no_harm?
463
+
464
+ if File.exist?(fname) or File.symlink?(fname)
465
+ File.chmod 0777, fname
466
+ File.unlink fname
467
+ end
468
+ end
469
+
470
+ def rm_rf(dn)
471
+ $stderr.puts "rm -rf #{dn}" if verbose?
472
+ return if no_harm?
473
+
474
+ Dir.chdir dn
475
+ Dir.foreach('.') do |fn|
476
+ next if fn == '.'
477
+ next if fn == '..'
478
+ if File.dir?(fn)
479
+ verbose_off {
480
+ rm_rf fn
481
+ }
482
+ else
483
+ verbose_off {
484
+ rm_f fn
485
+ }
486
+ end
487
+ end
488
+ Dir.chdir '..'
489
+ Dir.rmdir dn
490
+ end
491
+
492
+ def move_file(src, dest)
493
+ File.unlink dest if File.exist?(dest)
494
+ begin
495
+ File.rename src, dest
496
+ rescue
497
+ File.open(dest, 'wb') {|f| f.write File.binread(src) }
498
+ File.chmod File.stat(src).mode, dest
499
+ File.unlink src
500
+ end
501
+ end
502
+
503
+ def install(from, dest, mode, prefix = nil)
504
+ $stderr.puts "install #{from} #{dest}" if verbose?
505
+ return if no_harm?
506
+
507
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
508
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
509
+ str = File.binread(from)
510
+ if diff?(str, realdest)
511
+ verbose_off {
512
+ rm_f realdest if File.exist?(realdest)
513
+ }
514
+ File.open(realdest, 'wb') {|f|
515
+ f.write str
516
+ }
517
+ File.chmod mode, realdest
518
+
519
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
520
+ if prefix
521
+ f.puts realdest.sub(prefix, '')
522
+ else
523
+ f.puts realdest
524
+ end
525
+ }
526
+ end
527
+ end
528
+
529
+ def diff?(new_content, path)
530
+ return true unless File.exist?(path)
531
+ new_content != File.binread(path)
532
+ end
533
+
534
+ def command(str)
535
+ $stderr.puts str if verbose?
536
+ system str or raise RuntimeError, "'system #{str}' failed"
537
+ end
538
+
539
+ def ruby(str)
540
+ command config('rubyprog') + ' ' + str
541
+ end
542
+
543
+ def make(task = '')
544
+ command config('makeprog') + ' ' + task
545
+ end
546
+
547
+ def extdir?(dir)
548
+ File.exist?(dir + '/MANIFEST')
549
+ end
550
+
551
+ def all_files_in(dirname)
552
+ Dir.open(dirname) {|d|
553
+ return d.select {|ent| File.file?("#{dirname}/#{ent}") }
554
+ }
555
+ end
556
+
557
+ REJECT_DIRS = %w(
558
+ CVS SCCS RCS CVS.adm .svn
559
+ )
560
+
561
+ def all_dirs_in(dirname)
562
+ Dir.open(dirname) {|d|
563
+ return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
564
+ }
565
+ end
566
+
567
+ end
568
+
569
+
570
+ #
571
+ # Main Installer
572
+ #
573
+
574
+ module HookUtils
575
+
576
+ def run_hook(name)
577
+ try_run_hook "#{curr_srcdir()}/#{name}" or
578
+ try_run_hook "#{curr_srcdir()}/#{name}.rb"
579
+ end
580
+
581
+ def try_run_hook(fname)
582
+ return false unless File.file?(fname)
583
+ begin
584
+ instance_eval File.read(fname), fname, 1
585
+ rescue
586
+ setup_rb_error "hook #{fname} failed:\n" + $!.message
587
+ end
588
+ true
589
+ end
590
+
591
+ end
592
+
593
+
594
+ module HookScriptAPI
595
+
596
+ def get_config(key)
597
+ @config[key]
598
+ end
599
+
600
+ alias config get_config
601
+
602
+ def set_config(key, val)
603
+ @config[key] = val
604
+ end
605
+
606
+ #
607
+ # srcdir/objdir (works only in the package directory)
608
+ #
609
+
610
+ #abstract srcdir_root
611
+ #abstract objdir_root
612
+ #abstract relpath
613
+
614
+ def curr_srcdir
615
+ "#{srcdir_root()}/#{relpath()}"
616
+ end
617
+
618
+ def curr_objdir
619
+ "#{objdir_root()}/#{relpath()}"
620
+ end
621
+
622
+ def srcfile(path)
623
+ "#{curr_srcdir()}/#{path}"
624
+ end
625
+
626
+ def srcexist?(path)
627
+ File.exist?(srcfile(path))
628
+ end
629
+
630
+ def srcdirectory?(path)
631
+ File.dir?(srcfile(path))
632
+ end
633
+
634
+ def srcfile?(path)
635
+ File.file? srcfile(path)
636
+ end
637
+
638
+ def srcentries(path = '.')
639
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
640
+ return d.to_a - %w(. ..)
641
+ }
642
+ end
643
+
644
+ def srcfiles(path = '.')
645
+ srcentries(path).select {|fname|
646
+ File.file?(File.join(curr_srcdir(), path, fname))
647
+ }
648
+ end
649
+
650
+ def srcdirectories(path = '.')
651
+ srcentries(path).select {|fname|
652
+ File.dir?(File.join(curr_srcdir(), path, fname))
653
+ }
654
+ end
655
+
656
+ end
657
+
658
+
659
+ class ToplevelInstaller
660
+
661
+ Version = '3.3.1'
662
+ Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
663
+
664
+ TASKS = [
665
+ [ 'all', 'do config, setup, then install' ],
666
+ [ 'config', 'saves your configurations' ],
667
+ [ 'show', 'shows current configuration' ],
668
+ [ 'setup', 'compiles ruby extentions and others' ],
669
+ [ 'install', 'installs files' ],
670
+ [ 'clean', "does `make clean' for each extention" ],
671
+ [ 'distclean',"does `make distclean' for each extention" ]
672
+ ]
673
+
674
+ def ToplevelInstaller.invoke
675
+ instance().invoke
676
+ end
677
+
678
+ @singleton = nil
679
+
680
+ def ToplevelInstaller.instance
681
+ @singleton ||= new(File.dirname($0))
682
+ @singleton
683
+ end
684
+
685
+ include MetaConfigAPI
686
+
687
+ def initialize(ardir_root)
688
+ @config = nil
689
+ @options = { 'verbose' => true }
690
+ @ardir = File.expand_path(ardir_root)
691
+ end
692
+
693
+ def inspect
694
+ "#<#{self.class} #{__id__()}>"
695
+ end
696
+
697
+ def invoke
698
+ run_metaconfigs
699
+ case task = parsearg_global()
700
+ when nil, 'all'
701
+ @config = load_config('config')
702
+ parsearg_config
703
+ init_installers
704
+ exec_config
705
+ exec_setup
706
+ exec_install
707
+ else
708
+ @config = load_config(task)
709
+ __send__ "parsearg_#{task}"
710
+ init_installers
711
+ __send__ "exec_#{task}"
712
+ end
713
+ end
714
+
715
+ def run_metaconfigs
716
+ eval_file_ifexist "#{@ardir}/metaconfig"
717
+ end
718
+
719
+ def load_config(task)
720
+ case task
721
+ when 'config'
722
+ ConfigTable.new
723
+ when 'clean', 'distclean'
724
+ if File.exist?(ConfigTable.savefile)
725
+ then ConfigTable.load
726
+ else ConfigTable.new
727
+ end
728
+ else
729
+ ConfigTable.load
730
+ end
731
+ end
732
+
733
+ def init_installers
734
+ @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
735
+ end
736
+
737
+ #
738
+ # Hook Script API bases
739
+ #
740
+
741
+ def srcdir_root
742
+ @ardir
743
+ end
744
+
745
+ def objdir_root
746
+ '.'
747
+ end
748
+
749
+ def relpath
750
+ '.'
751
+ end
752
+
753
+ #
754
+ # Option Parsing
755
+ #
756
+
757
+ def parsearg_global
758
+ valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
759
+
760
+ while arg = ARGV.shift
761
+ case arg
762
+ when /\A\w+\z/
763
+ setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
764
+ return arg
765
+
766
+ when '-q', '--quiet'
767
+ @options['verbose'] = false
768
+
769
+ when '--verbose'
770
+ @options['verbose'] = true
771
+
772
+ when '-h', '--help'
773
+ print_usage $stdout
774
+ exit 0
775
+
776
+ when '-v', '--version'
777
+ puts "#{File.basename($0)} version #{Version}"
778
+ exit 0
779
+
780
+ when '--copyright'
781
+ puts Copyright
782
+ exit 0
783
+
784
+ else
785
+ setup_rb_error "unknown global option '#{arg}'"
786
+ end
787
+ end
788
+
789
+ nil
790
+ end
791
+
792
+
793
+ def parsearg_no_options
794
+ unless ARGV.empty?
795
+ setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
796
+ end
797
+ end
798
+
799
+ alias parsearg_show parsearg_no_options
800
+ alias parsearg_setup parsearg_no_options
801
+ alias parsearg_clean parsearg_no_options
802
+ alias parsearg_distclean parsearg_no_options
803
+
804
+ def parsearg_config
805
+ re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
806
+ @options['config-opt'] = []
807
+
808
+ while i = ARGV.shift
809
+ if /\A--?\z/ =~ i
810
+ @options['config-opt'] = ARGV.dup
811
+ break
812
+ end
813
+ m = re.match(i) or setup_rb_error "config: unknown option #{i}"
814
+ name, value = *m.to_a[1,2]
815
+ @config[name] = value
816
+ end
817
+ end
818
+
819
+ def parsearg_install
820
+ @options['no-harm'] = false
821
+ @options['install-prefix'] = ''
822
+ while a = ARGV.shift
823
+ case a
824
+ when /\A--no-harm\z/
825
+ @options['no-harm'] = true
826
+ when /\A--prefix=(.*)\z/
827
+ path = $1
828
+ path = File.expand_path(path) unless path[0,1] == '/'
829
+ @options['install-prefix'] = path
830
+ else
831
+ setup_rb_error "install: unknown option #{a}"
832
+ end
833
+ end
834
+ end
835
+
836
+ def print_usage(out)
837
+ out.puts 'Typical Installation Procedure:'
838
+ out.puts " $ ruby #{File.basename $0} config"
839
+ out.puts " $ ruby #{File.basename $0} setup"
840
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
841
+ out.puts
842
+ out.puts 'Detailed Usage:'
843
+ out.puts " ruby #{File.basename $0} <global option>"
844
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
845
+
846
+ fmt = " %-24s %s\n"
847
+ out.puts
848
+ out.puts 'Global options:'
849
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
850
+ out.printf fmt, ' --verbose', 'output messages verbosely'
851
+ out.printf fmt, '-h,--help', 'print this message'
852
+ out.printf fmt, '-v,--version', 'print version and quit'
853
+ out.printf fmt, ' --copyright', 'print copyright and quit'
854
+ out.puts
855
+ out.puts 'Tasks:'
856
+ TASKS.each do |name, desc|
857
+ out.printf fmt, name, desc
858
+ end
859
+
860
+ fmt = " %-24s %s [%s]\n"
861
+ out.puts
862
+ out.puts 'Options for CONFIG or ALL:'
863
+ ConfigTable.each do |item|
864
+ out.printf fmt, item.help_opt, item.description, item.help_default
865
+ end
866
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
867
+ out.puts
868
+ out.puts 'Options for INSTALL:'
869
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
870
+ out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
871
+ out.puts
872
+ end
873
+
874
+ #
875
+ # Task Handlers
876
+ #
877
+
878
+ def exec_config
879
+ @installer.exec_config
880
+ @config.save # must be final
881
+ end
882
+
883
+ def exec_setup
884
+ @installer.exec_setup
885
+ end
886
+
887
+ def exec_install
888
+ @installer.exec_install
889
+ end
890
+
891
+ def exec_show
892
+ ConfigTable.each do |i|
893
+ printf "%-20s %s\n", i.name, i.value
894
+ end
895
+ end
896
+
897
+ def exec_clean
898
+ @installer.exec_clean
899
+ end
900
+
901
+ def exec_distclean
902
+ @installer.exec_distclean
903
+ end
904
+
905
+ end
906
+
907
+
908
+ class ToplevelInstallerMulti < ToplevelInstaller
909
+
910
+ include HookUtils
911
+ include HookScriptAPI
912
+ include FileOperations
913
+
914
+ def initialize(ardir)
915
+ super
916
+ @packages = all_dirs_in("#{@ardir}/packages")
917
+ raise 'no package exists' if @packages.empty?
918
+ end
919
+
920
+ def run_metaconfigs
921
+ eval_file_ifexist "#{@ardir}/metaconfig"
922
+ @packages.each do |name|
923
+ eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
924
+ end
925
+ end
926
+
927
+ def init_installers
928
+ @installers = {}
929
+ @packages.each do |pack|
930
+ @installers[pack] = Installer.new(@config, @options,
931
+ "#{@ardir}/packages/#{pack}",
932
+ "packages/#{pack}")
933
+ end
934
+
935
+ with = extract_selection(config('with'))
936
+ without = extract_selection(config('without'))
937
+ @selected = @installers.keys.select {|name|
938
+ (with.empty? or with.include?(name)) \
939
+ and not without.include?(name)
940
+ }
941
+ end
942
+
943
+ def extract_selection(list)
944
+ a = list.split(/,/)
945
+ a.each do |name|
946
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
947
+ end
948
+ a
949
+ end
950
+
951
+ def print_usage(f)
952
+ super
953
+ f.puts 'Inluded packages:'
954
+ f.puts ' ' + @packages.sort.join(' ')
955
+ f.puts
956
+ end
957
+
958
+ #
959
+ # multi-package metaconfig API
960
+ #
961
+
962
+ attr_reader :packages
963
+
964
+ def declare_packages(list)
965
+ raise 'package list is empty' if list.empty?
966
+ list.each do |name|
967
+ raise "directory packages/#{name} does not exist"\
968
+ unless File.dir?("#{@ardir}/packages/#{name}")
969
+ end
970
+ @packages = list
971
+ end
972
+
973
+ #
974
+ # Task Handlers
975
+ #
976
+
977
+ def exec_config
978
+ run_hook 'pre-config'
979
+ each_selected_installers {|inst| inst.exec_config }
980
+ run_hook 'post-config'
981
+ @config.save # must be final
982
+ end
983
+
984
+ def exec_setup
985
+ run_hook 'pre-setup'
986
+ each_selected_installers {|inst| inst.exec_setup }
987
+ run_hook 'post-setup'
988
+ end
989
+
990
+ def exec_install
991
+ run_hook 'pre-install'
992
+ each_selected_installers {|inst| inst.exec_install }
993
+ run_hook 'post-install'
994
+ end
995
+
996
+ def exec_clean
997
+ rm_f ConfigTable.savefile
998
+ run_hook 'pre-clean'
999
+ each_selected_installers {|inst| inst.exec_clean }
1000
+ run_hook 'post-clean'
1001
+ end
1002
+
1003
+ def exec_distclean
1004
+ rm_f ConfigTable.savefile
1005
+ run_hook 'pre-distclean'
1006
+ each_selected_installers {|inst| inst.exec_distclean }
1007
+ run_hook 'post-distclean'
1008
+ end
1009
+
1010
+ #
1011
+ # lib
1012
+ #
1013
+
1014
+ def each_selected_installers
1015
+ Dir.mkdir 'packages' unless File.dir?('packages')
1016
+ @selected.each do |pack|
1017
+ $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1018
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1019
+ Dir.chdir "packages/#{pack}"
1020
+ yield @installers[pack]
1021
+ Dir.chdir '../..'
1022
+ end
1023
+ end
1024
+
1025
+ def verbose?
1026
+ @options['verbose']
1027
+ end
1028
+
1029
+ def no_harm?
1030
+ @options['no-harm']
1031
+ end
1032
+
1033
+ end
1034
+
1035
+
1036
+ class Installer
1037
+
1038
+ FILETYPES = %w( bin lib ext data )
1039
+
1040
+ include HookScriptAPI
1041
+ include HookUtils
1042
+ include FileOperations
1043
+
1044
+ def initialize(config, opt, srcroot, objroot)
1045
+ @config = config
1046
+ @options = opt
1047
+ @srcdir = File.expand_path(srcroot)
1048
+ @objdir = File.expand_path(objroot)
1049
+ @currdir = '.'
1050
+ end
1051
+
1052
+ def inspect
1053
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1054
+ end
1055
+
1056
+ #
1057
+ # Hook Script API base methods
1058
+ #
1059
+
1060
+ def srcdir_root
1061
+ @srcdir
1062
+ end
1063
+
1064
+ def objdir_root
1065
+ @objdir
1066
+ end
1067
+
1068
+ def relpath
1069
+ @currdir
1070
+ end
1071
+
1072
+ #
1073
+ # configs/options
1074
+ #
1075
+
1076
+ def no_harm?
1077
+ @options['no-harm']
1078
+ end
1079
+
1080
+ def verbose?
1081
+ @options['verbose']
1082
+ end
1083
+
1084
+ def verbose_off
1085
+ begin
1086
+ save, @options['verbose'] = @options['verbose'], false
1087
+ yield
1088
+ ensure
1089
+ @options['verbose'] = save
1090
+ end
1091
+ end
1092
+
1093
+ #
1094
+ # TASK config
1095
+ #
1096
+
1097
+ def exec_config
1098
+ exec_task_traverse 'config'
1099
+ end
1100
+
1101
+ def config_dir_bin(rel)
1102
+ end
1103
+
1104
+ def config_dir_lib(rel)
1105
+ end
1106
+
1107
+ def config_dir_ext(rel)
1108
+ extconf if extdir?(curr_srcdir())
1109
+ end
1110
+
1111
+ def extconf
1112
+ opt = @options['config-opt'].join(' ')
1113
+ command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
1114
+ end
1115
+
1116
+ def config_dir_data(rel)
1117
+ end
1118
+
1119
+ #
1120
+ # TASK setup
1121
+ #
1122
+
1123
+ def exec_setup
1124
+ exec_task_traverse 'setup'
1125
+ end
1126
+
1127
+ def setup_dir_bin(rel)
1128
+ all_files_in(curr_srcdir()).each do |fname|
1129
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
1130
+ end
1131
+ end
1132
+
1133
+ def adjust_shebang(path)
1134
+ return if no_harm?
1135
+ tmpfile = File.basename(path) + '.tmp'
1136
+ begin
1137
+ File.open(path, 'rb') {|r|
1138
+ first = r.gets
1139
+ return unless File.basename(config('rubypath')) == 'ruby'
1140
+ return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
1141
+ $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1142
+ File.open(tmpfile, 'wb') {|w|
1143
+ w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
1144
+ w.write r.read
1145
+ }
1146
+ move_file tmpfile, File.basename(path)
1147
+ }
1148
+ ensure
1149
+ File.unlink tmpfile if File.exist?(tmpfile)
1150
+ end
1151
+ end
1152
+
1153
+ def setup_dir_lib(rel)
1154
+ end
1155
+
1156
+ def setup_dir_ext(rel)
1157
+ make if extdir?(curr_srcdir())
1158
+ end
1159
+
1160
+ def setup_dir_data(rel)
1161
+ end
1162
+
1163
+ #
1164
+ # TASK install
1165
+ #
1166
+
1167
+ def exec_install
1168
+ rm_f 'InstalledFiles'
1169
+ exec_task_traverse 'install'
1170
+ end
1171
+
1172
+ def install_dir_bin(rel)
1173
+ install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
1174
+ end
1175
+
1176
+ def install_dir_lib(rel)
1177
+ install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
1178
+ end
1179
+
1180
+ def install_dir_ext(rel)
1181
+ return unless extdir?(curr_srcdir())
1182
+ install_files ruby_extentions('.'),
1183
+ "#{config('sodir')}/#{File.dirname(rel)}",
1184
+ 0555
1185
+ end
1186
+
1187
+ def install_dir_data(rel)
1188
+ install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
1189
+ end
1190
+
1191
+ def install_files(list, dest, mode)
1192
+ mkdir_p dest, @options['install-prefix']
1193
+ list.each do |fname|
1194
+ install fname, dest, mode, @options['install-prefix']
1195
+ end
1196
+ end
1197
+
1198
+ def ruby_scripts
1199
+ collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1200
+ end
1201
+
1202
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1203
+ reject_patterns = %w(
1204
+ core RCSLOG tags TAGS .make.state
1205
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1206
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1207
+
1208
+ *.org *.in .*
1209
+ )
1210
+ mapping = {
1211
+ '.' => '\.',
1212
+ '$' => '\$',
1213
+ '#' => '\#',
1214
+ '*' => '.*'
1215
+ }
1216
+ REJECT_PATTERNS = Regexp.new('\A(?:' +
1217
+ reject_patterns.map {|pat|
1218
+ pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1219
+ }.join('|') +
1220
+ ')\z')
1221
+
1222
+ def collect_filenames_auto
1223
+ mapdir((existfiles() - hookfiles()).reject {|fname|
1224
+ REJECT_PATTERNS =~ fname
1225
+ })
1226
+ end
1227
+
1228
+ def existfiles
1229
+ all_files_in(curr_srcdir()) | all_files_in('.')
1230
+ end
1231
+
1232
+ def hookfiles
1233
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1234
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1235
+ }.flatten
1236
+ end
1237
+
1238
+ def mapdir(filelist)
1239
+ filelist.map {|fname|
1240
+ if File.exist?(fname) # objdir
1241
+ fname
1242
+ else # srcdir
1243
+ File.join(curr_srcdir(), fname)
1244
+ end
1245
+ }
1246
+ end
1247
+
1248
+ def ruby_extentions(dir)
1249
+ Dir.open(dir) {|d|
1250
+ ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
1251
+ if ents.empty?
1252
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1253
+ end
1254
+ return ents
1255
+ }
1256
+ end
1257
+
1258
+ #
1259
+ # TASK clean
1260
+ #
1261
+
1262
+ def exec_clean
1263
+ exec_task_traverse 'clean'
1264
+ rm_f ConfigTable.savefile
1265
+ rm_f 'InstalledFiles'
1266
+ end
1267
+
1268
+ def clean_dir_bin(rel)
1269
+ end
1270
+
1271
+ def clean_dir_lib(rel)
1272
+ end
1273
+
1274
+ def clean_dir_ext(rel)
1275
+ return unless extdir?(curr_srcdir())
1276
+ make 'clean' if File.file?('Makefile')
1277
+ end
1278
+
1279
+ def clean_dir_data(rel)
1280
+ end
1281
+
1282
+ #
1283
+ # TASK distclean
1284
+ #
1285
+
1286
+ def exec_distclean
1287
+ exec_task_traverse 'distclean'
1288
+ rm_f ConfigTable.savefile
1289
+ rm_f 'InstalledFiles'
1290
+ end
1291
+
1292
+ def distclean_dir_bin(rel)
1293
+ end
1294
+
1295
+ def distclean_dir_lib(rel)
1296
+ end
1297
+
1298
+ def distclean_dir_ext(rel)
1299
+ return unless extdir?(curr_srcdir())
1300
+ make 'distclean' if File.file?('Makefile')
1301
+ end
1302
+
1303
+ #
1304
+ # lib
1305
+ #
1306
+
1307
+ def exec_task_traverse(task)
1308
+ run_hook "pre-#{task}"
1309
+ FILETYPES.each do |type|
1310
+ if config('without-ext') == 'yes' and type == 'ext'
1311
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1312
+ next
1313
+ end
1314
+ traverse task, type, "#{task}_dir_#{type}"
1315
+ end
1316
+ run_hook "post-#{task}"
1317
+ end
1318
+
1319
+ def traverse(task, rel, mid)
1320
+ dive_into(rel) {
1321
+ run_hook "pre-#{task}"
1322
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1323
+ all_dirs_in(curr_srcdir()).each do |d|
1324
+ traverse task, "#{rel}/#{d}", mid
1325
+ end
1326
+ run_hook "post-#{task}"
1327
+ }
1328
+ end
1329
+
1330
+ def dive_into(rel)
1331
+ return unless File.dir?("#{@srcdir}/#{rel}")
1332
+
1333
+ dir = File.basename(rel)
1334
+ Dir.mkdir dir unless File.dir?(dir)
1335
+ prevdir = Dir.pwd
1336
+ Dir.chdir dir
1337
+ $stderr.puts '---> ' + rel if verbose?
1338
+ @currdir = rel
1339
+ yield
1340
+ Dir.chdir prevdir
1341
+ $stderr.puts '<--- ' + rel if verbose?
1342
+ @currdir = File.dirname(rel)
1343
+ end
1344
+
1345
+ end
1346
+
1347
+
1348
+ if $0 == __FILE__
1349
+ begin
1350
+ if multipackage_install?
1351
+ ToplevelInstallerMulti.invoke
1352
+ else
1353
+ ToplevelInstaller.invoke
1354
+ end
1355
+ rescue SetupError
1356
+ raise if $DEBUG
1357
+ $stderr.puts $!.message
1358
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1359
+ exit 1
1360
+ end
1361
+ end