ansi-sys 0.2.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.
File without changes
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/ansisys.rb'
15
+
16
+ version = AnsiSys::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/ansi-sys'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)
@@ -0,0 +1,1637 @@
1
+ #
2
+ # setup.rb
3
+ #
4
+ # Copyright (c) 2000-2005 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU LGPL, Lesser General Public License version 2.1.
9
+ #
10
+
11
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12
+ module Enumerable
13
+ alias map collect
14
+ end
15
+ end
16
+
17
+ unless File.respond_to?(:read) # Ruby 1.6
18
+ def File.read(fname)
19
+ open(fname) {|f|
20
+ return f.read
21
+ }
22
+ end
23
+ end
24
+
25
+ unless Errno.const_defined?(:ENOTEMPTY) # Windows?
26
+ module Errno
27
+ class ENOTEMPTY
28
+ # We do not raise this exception, implementation is not needed.
29
+ end
30
+ end
31
+ end
32
+
33
+ def File.binread(fname)
34
+ open(fname, 'rb') {|f|
35
+ return f.read
36
+ }
37
+ end
38
+
39
+ # for corrupted Windows' stat(2)
40
+ def File.dir?(path)
41
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
42
+ end
43
+
44
+
45
+ class ConfigTable
46
+
47
+ include Enumerable
48
+
49
+ def initialize(rbconfig)
50
+ @rbconfig = rbconfig
51
+ @items = []
52
+ @table = {}
53
+ # options
54
+ @install_prefix = nil
55
+ @config_opt = nil
56
+ @spec_opt = nil
57
+ @verbose = true
58
+ @no_harm = false
59
+ end
60
+
61
+ attr_accessor :install_prefix
62
+ attr_accessor :config_opt
63
+ attr_accessor :spec_opt
64
+
65
+ attr_writer :verbose
66
+
67
+ def verbose?
68
+ @verbose
69
+ end
70
+
71
+ attr_writer :no_harm
72
+
73
+ def no_harm?
74
+ @no_harm
75
+ end
76
+
77
+ def [](key)
78
+ lookup(key).resolve(self)
79
+ end
80
+
81
+ def []=(key, val)
82
+ lookup(key).set val
83
+ end
84
+
85
+ def names
86
+ @items.map {|i| i.name }
87
+ end
88
+
89
+ def each(&block)
90
+ @items.each(&block)
91
+ end
92
+
93
+ def key?(name)
94
+ @table.key?(name)
95
+ end
96
+
97
+ def lookup(name)
98
+ @table[name] or setup_rb_error "no such config item: #{name}"
99
+ end
100
+
101
+ def add(item)
102
+ @items.push item
103
+ @table[item.name] = item
104
+ end
105
+
106
+ def remove(name)
107
+ item = lookup(name)
108
+ @items.delete_if {|i| i.name == name }
109
+ @table.delete_if {|name, i| i.name == name }
110
+ item
111
+ end
112
+
113
+ def load_script(path, inst = nil)
114
+ if File.file?(path)
115
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
116
+ end
117
+ end
118
+
119
+ def savefile
120
+ '.config'
121
+ end
122
+
123
+ def load_savefile
124
+ begin
125
+ File.foreach(savefile()) do |line|
126
+ k, v = *line.split(/=/, 2)
127
+ self[k] = v.strip
128
+ end
129
+ rescue Errno::ENOENT
130
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
131
+ end
132
+ end
133
+
134
+ def save
135
+ @items.each {|i| i.value }
136
+ File.open(savefile(), 'w') {|f|
137
+ @items.each do |i|
138
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
139
+ end
140
+ }
141
+ end
142
+
143
+ def load_standard_entries
144
+ standard_entries(@rbconfig).each do |ent|
145
+ add ent
146
+ end
147
+ end
148
+
149
+ def standard_entries(rbconfig)
150
+ c = rbconfig
151
+
152
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
153
+
154
+ major = c['MAJOR'].to_i
155
+ minor = c['MINOR'].to_i
156
+ teeny = c['TEENY'].to_i
157
+ version = "#{major}.#{minor}"
158
+
159
+ # ruby ver. >= 1.4.4?
160
+ newpath_p = ((major >= 2) or
161
+ ((major == 1) and
162
+ ((minor >= 5) or
163
+ ((minor == 4) and (teeny >= 4)))))
164
+
165
+ if c['rubylibdir']
166
+ # V > 1.6.3
167
+ libruby = "#{c['prefix']}/lib/ruby"
168
+ librubyver = c['rubylibdir']
169
+ librubyverarch = c['archdir']
170
+ siteruby = c['sitedir']
171
+ siterubyver = c['sitelibdir']
172
+ siterubyverarch = c['sitearchdir']
173
+ elsif newpath_p
174
+ # 1.4.4 <= V <= 1.6.3
175
+ libruby = "#{c['prefix']}/lib/ruby"
176
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
177
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
178
+ siteruby = c['sitedir']
179
+ siterubyver = "$siteruby/#{version}"
180
+ siterubyverarch = "$siterubyver/#{c['arch']}"
181
+ else
182
+ # V < 1.4.4
183
+ libruby = "#{c['prefix']}/lib/ruby"
184
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
185
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
186
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
187
+ siterubyver = siteruby
188
+ siterubyverarch = "$siterubyver/#{c['arch']}"
189
+ end
190
+ parameterize = lambda {|path|
191
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
192
+ }
193
+
194
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
195
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
196
+ else
197
+ makeprog = 'make'
198
+ end
199
+
200
+ [
201
+ ExecItem.new('installdirs', 'std/site/home',
202
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
203
+ {|val, table|
204
+ case val
205
+ when 'std'
206
+ table['rbdir'] = '$librubyver'
207
+ table['sodir'] = '$librubyverarch'
208
+ when 'site'
209
+ table['rbdir'] = '$siterubyver'
210
+ table['sodir'] = '$siterubyverarch'
211
+ when 'home'
212
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
213
+ table['prefix'] = ENV['HOME']
214
+ table['rbdir'] = '$libdir/ruby'
215
+ table['sodir'] = '$libdir/ruby'
216
+ end
217
+ },
218
+ PathItem.new('prefix', 'path', c['prefix'],
219
+ 'path prefix of target environment'),
220
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
221
+ 'the directory for commands'),
222
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
223
+ 'the directory for libraries'),
224
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
225
+ 'the directory for shared data'),
226
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
227
+ 'the directory for man pages'),
228
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
229
+ 'the directory for system configuration files'),
230
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
231
+ 'the directory for local state data'),
232
+ PathItem.new('libruby', 'path', libruby,
233
+ 'the directory for ruby libraries'),
234
+ PathItem.new('librubyver', 'path', librubyver,
235
+ 'the directory for standard ruby libraries'),
236
+ PathItem.new('librubyverarch', 'path', librubyverarch,
237
+ 'the directory for standard ruby extensions'),
238
+ PathItem.new('siteruby', 'path', siteruby,
239
+ 'the directory for version-independent aux ruby libraries'),
240
+ PathItem.new('siterubyver', 'path', siterubyver,
241
+ 'the directory for aux ruby libraries'),
242
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
243
+ 'the directory for aux ruby binaries'),
244
+ PathItem.new('rbdir', 'path', '$siterubyver',
245
+ 'the directory for ruby scripts'),
246
+ PathItem.new('sodir', 'path', '$siterubyverarch',
247
+ 'the directory for ruby extentions'),
248
+ PathItem.new('rubypath', 'path', rubypath,
249
+ 'the path to set to #! line'),
250
+ ProgramItem.new('rubyprog', 'name', rubypath,
251
+ 'the ruby program using for installation'),
252
+ ProgramItem.new('makeprog', 'name', makeprog,
253
+ 'the make program to compile ruby extentions'),
254
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
255
+ 'shebang line (#!) editing mode'),
256
+ BoolItem.new('without-ext', 'yes/no', 'no',
257
+ 'does not compile/install ruby extentions')
258
+ ]
259
+ end
260
+ private :standard_entries
261
+
262
+ def load_multipackage_entries
263
+ multipackage_entries().each do |ent|
264
+ add ent
265
+ end
266
+ end
267
+
268
+ def multipackage_entries
269
+ [
270
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
271
+ 'package names that you want to install'),
272
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
273
+ 'package names that you do not want to install')
274
+ ]
275
+ end
276
+ private :multipackage_entries
277
+
278
+ ALIASES = {
279
+ 'std-ruby' => 'librubyver',
280
+ 'stdruby' => 'librubyver',
281
+ 'rubylibdir' => 'librubyver',
282
+ 'archdir' => 'librubyverarch',
283
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
284
+ 'site-ruby' => 'siterubyver', # For backward compatibility
285
+ 'bin-dir' => 'bindir',
286
+ 'bin-dir' => 'bindir',
287
+ 'rb-dir' => 'rbdir',
288
+ 'so-dir' => 'sodir',
289
+ 'data-dir' => 'datadir',
290
+ 'ruby-path' => 'rubypath',
291
+ 'ruby-prog' => 'rubyprog',
292
+ 'ruby' => 'rubyprog',
293
+ 'make-prog' => 'makeprog',
294
+ 'make' => 'makeprog'
295
+ }
296
+
297
+ def fixup
298
+ ALIASES.each do |ali, name|
299
+ @table[ali] = @table[name]
300
+ end
301
+ @items.freeze
302
+ @table.freeze
303
+ @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
304
+ end
305
+
306
+ def parse_opt(opt)
307
+ m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
308
+ m.to_a[1,2]
309
+ end
310
+
311
+ def dllext
312
+ @rbconfig['DLEXT']
313
+ end
314
+
315
+ def value_config?(name)
316
+ lookup(name).value?
317
+ end
318
+
319
+ class Item
320
+ def initialize(name, template, default, desc)
321
+ @name = name.freeze
322
+ @template = template
323
+ @value = default
324
+ @default = default
325
+ @description = desc
326
+ end
327
+
328
+ attr_reader :name
329
+ attr_reader :description
330
+
331
+ attr_accessor :default
332
+ alias help_default default
333
+
334
+ def help_opt
335
+ "--#{@name}=#{@template}"
336
+ end
337
+
338
+ def value?
339
+ true
340
+ end
341
+
342
+ def value
343
+ @value
344
+ end
345
+
346
+ def resolve(table)
347
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
348
+ end
349
+
350
+ def set(val)
351
+ @value = check(val)
352
+ end
353
+
354
+ private
355
+
356
+ def check(val)
357
+ setup_rb_error "config: --#{name} requires argument" unless val
358
+ val
359
+ end
360
+ end
361
+
362
+ class BoolItem < Item
363
+ def config_type
364
+ 'bool'
365
+ end
366
+
367
+ def help_opt
368
+ "--#{@name}"
369
+ end
370
+
371
+ private
372
+
373
+ def check(val)
374
+ return 'yes' unless val
375
+ case val
376
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
377
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
378
+ else
379
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
380
+ end
381
+ end
382
+ end
383
+
384
+ class PathItem < Item
385
+ def config_type
386
+ 'path'
387
+ end
388
+
389
+ private
390
+
391
+ def check(path)
392
+ setup_rb_error "config: --#{@name} requires argument" unless path
393
+ path[0,1] == '$' ? path : File.expand_path(path)
394
+ end
395
+ end
396
+
397
+ class ProgramItem < Item
398
+ def config_type
399
+ 'program'
400
+ end
401
+ end
402
+
403
+ class SelectItem < Item
404
+ def initialize(name, selection, default, desc)
405
+ super
406
+ @ok = selection.split('/')
407
+ end
408
+
409
+ def config_type
410
+ 'select'
411
+ end
412
+
413
+ private
414
+
415
+ def check(val)
416
+ unless @ok.include?(val.strip)
417
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
418
+ end
419
+ val.strip
420
+ end
421
+ end
422
+
423
+ class ExecItem < Item
424
+ def initialize(name, selection, desc, &block)
425
+ super name, selection, nil, desc
426
+ @ok = selection.split('/')
427
+ @action = block
428
+ end
429
+
430
+ def config_type
431
+ 'exec'
432
+ end
433
+
434
+ def value?
435
+ false
436
+ end
437
+
438
+ def resolve(table)
439
+ setup_rb_error "$#{name()} wrongly used as option value"
440
+ end
441
+
442
+ undef set
443
+
444
+ def evaluate(val, table)
445
+ v = val.strip.downcase
446
+ unless @ok.include?(v)
447
+ setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
448
+ end
449
+ @action.call v, table
450
+ end
451
+ end
452
+
453
+ class PackageSelectionItem < Item
454
+ def initialize(name, template, default, help_default, desc)
455
+ super name, template, default, desc
456
+ @help_default = help_default
457
+ end
458
+
459
+ attr_reader :help_default
460
+
461
+ def config_type
462
+ 'package'
463
+ end
464
+
465
+ private
466
+
467
+ def check(val)
468
+ unless File.dir?("packages/#{val}")
469
+ setup_rb_error "config: no such package: #{val}"
470
+ end
471
+ val
472
+ end
473
+ end
474
+
475
+ class MetaConfigEnvironment
476
+ def initialize(config, installer)
477
+ @config = config
478
+ @installer = installer
479
+ end
480
+
481
+ def config_names
482
+ @config.names
483
+ end
484
+
485
+ def config?(name)
486
+ @config.key?(name)
487
+ end
488
+
489
+ def bool_config?(name)
490
+ @config.lookup(name).config_type == 'bool'
491
+ end
492
+
493
+ def path_config?(name)
494
+ @config.lookup(name).config_type == 'path'
495
+ end
496
+
497
+ def value_config?(name)
498
+ @config.lookup(name).config_type != 'exec'
499
+ end
500
+
501
+ def add_config(item)
502
+ @config.add item
503
+ end
504
+
505
+ def add_bool_config(name, default, desc)
506
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
507
+ end
508
+
509
+ def add_path_config(name, default, desc)
510
+ @config.add PathItem.new(name, 'path', default, desc)
511
+ end
512
+
513
+ def set_config_default(name, default)
514
+ @config.lookup(name).default = default
515
+ end
516
+
517
+ def remove_config(name)
518
+ @config.remove(name)
519
+ end
520
+
521
+ # For only multipackage
522
+ def packages
523
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
524
+ @installer.packages
525
+ end
526
+
527
+ # For only multipackage
528
+ def declare_packages(list)
529
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
530
+ @installer.packages = list
531
+ end
532
+ end
533
+
534
+ end # class ConfigTable
535
+
536
+
537
+ # This module requires: #verbose?, #no_harm?
538
+ module FileOperations
539
+
540
+ def mkdir_p(dirname, prefix = nil)
541
+ dirname = prefix + File.expand_path(dirname) if prefix
542
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
543
+ return if no_harm?
544
+
545
+ # Does not check '/', it's too abnormal.
546
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
547
+ if /\A[a-z]:\z/i =~ dirs[0]
548
+ disk = dirs.shift
549
+ dirs[0] = disk + dirs[0]
550
+ end
551
+ dirs.each_index do |idx|
552
+ path = dirs[0..idx].join('')
553
+ Dir.mkdir path unless File.dir?(path)
554
+ end
555
+ end
556
+
557
+ def rm_f(path)
558
+ $stderr.puts "rm -f #{path}" if verbose?
559
+ return if no_harm?
560
+ force_remove_file path
561
+ end
562
+
563
+ def rm_rf(path)
564
+ $stderr.puts "rm -rf #{path}" if verbose?
565
+ return if no_harm?
566
+ remove_tree path
567
+ end
568
+
569
+ def remove_tree(path)
570
+ if File.symlink?(path)
571
+ remove_file path
572
+ elsif File.dir?(path)
573
+ remove_tree0 path
574
+ else
575
+ force_remove_file path
576
+ end
577
+ end
578
+
579
+ def remove_tree0(path)
580
+ Dir.foreach(path) do |ent|
581
+ next if ent == '.'
582
+ next if ent == '..'
583
+ entpath = "#{path}/#{ent}"
584
+ if File.symlink?(entpath)
585
+ remove_file entpath
586
+ elsif File.dir?(entpath)
587
+ remove_tree0 entpath
588
+ else
589
+ force_remove_file entpath
590
+ end
591
+ end
592
+ begin
593
+ Dir.rmdir path
594
+ rescue Errno::ENOTEMPTY
595
+ # directory may not be empty
596
+ end
597
+ end
598
+
599
+ def move_file(src, dest)
600
+ force_remove_file dest
601
+ begin
602
+ File.rename src, dest
603
+ rescue
604
+ File.open(dest, 'wb') {|f|
605
+ f.write File.binread(src)
606
+ }
607
+ File.chmod File.stat(src).mode, dest
608
+ File.unlink src
609
+ end
610
+ end
611
+
612
+ def force_remove_file(path)
613
+ begin
614
+ remove_file path
615
+ rescue
616
+ end
617
+ end
618
+
619
+ def remove_file(path)
620
+ File.chmod 0777, path
621
+ File.unlink path
622
+ end
623
+
624
+ def install(from, dest, mode, prefix = nil)
625
+ $stderr.puts "install #{from} #{dest}" if verbose?
626
+ return if no_harm?
627
+
628
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
629
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
630
+ str = File.binread(from)
631
+ if diff?(str, realdest)
632
+ verbose_off {
633
+ rm_f realdest if File.exist?(realdest)
634
+ }
635
+ File.open(realdest, 'wb') {|f|
636
+ f.write str
637
+ }
638
+ File.chmod mode, realdest
639
+
640
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
641
+ if prefix
642
+ f.puts realdest.sub(prefix, '')
643
+ else
644
+ f.puts realdest
645
+ end
646
+ }
647
+ end
648
+ end
649
+
650
+ def diff?(new_content, path)
651
+ return true unless File.exist?(path)
652
+ new_content != File.binread(path)
653
+ end
654
+
655
+ def command(*args)
656
+ $stderr.puts args.join(' ') if verbose?
657
+ system(*args) or raise RuntimeError,
658
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
659
+ end
660
+
661
+ def ruby(*args)
662
+ command config('rubyprog'), *args
663
+ end
664
+
665
+ def make(task = nil)
666
+ command(*[config('makeprog'), task].compact)
667
+ end
668
+
669
+ def extdir?(dir)
670
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
671
+ end
672
+
673
+ def files_of(dir)
674
+ Dir.open(dir) {|d|
675
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
676
+ }
677
+ end
678
+
679
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
680
+
681
+ def directories_of(dir)
682
+ Dir.open(dir) {|d|
683
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
684
+ }
685
+ end
686
+
687
+ end
688
+
689
+
690
+ # This module requires: #srcdir_root, #objdir_root, #relpath
691
+ module HookScriptAPI
692
+
693
+ def get_config(key)
694
+ @config[key]
695
+ end
696
+
697
+ alias config get_config
698
+
699
+ # obsolete: use metaconfig to change configuration
700
+ def set_config(key, val)
701
+ @config[key] = val
702
+ end
703
+
704
+ #
705
+ # srcdir/objdir (works only in the package directory)
706
+ #
707
+
708
+ def curr_srcdir
709
+ "#{srcdir_root()}/#{relpath()}"
710
+ end
711
+
712
+ def curr_objdir
713
+ "#{objdir_root()}/#{relpath()}"
714
+ end
715
+
716
+ def srcfile(path)
717
+ "#{curr_srcdir()}/#{path}"
718
+ end
719
+
720
+ def srcexist?(path)
721
+ File.exist?(srcfile(path))
722
+ end
723
+
724
+ def srcdirectory?(path)
725
+ File.dir?(srcfile(path))
726
+ end
727
+
728
+ def srcfile?(path)
729
+ File.file?(srcfile(path))
730
+ end
731
+
732
+ def srcentries(path = '.')
733
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
734
+ return d.to_a - %w(. ..)
735
+ }
736
+ end
737
+
738
+ def srcfiles(path = '.')
739
+ srcentries(path).select {|fname|
740
+ File.file?(File.join(curr_srcdir(), path, fname))
741
+ }
742
+ end
743
+
744
+ def srcdirectories(path = '.')
745
+ srcentries(path).select {|fname|
746
+ File.dir?(File.join(curr_srcdir(), path, fname))
747
+ }
748
+ end
749
+
750
+ end
751
+
752
+
753
+ class ToplevelInstaller
754
+
755
+ Version = '3.4.1'
756
+ Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
757
+
758
+ TASKS = [
759
+ [ 'all', 'do config, setup, then install' ],
760
+ [ 'config', 'saves your configurations' ],
761
+ [ 'show', 'shows current configuration' ],
762
+ [ 'setup', 'compiles ruby extentions and others' ],
763
+ [ 'install', 'installs files' ],
764
+ [ 'test', 'run all tests in test/' ],
765
+ [ 'spec', 'check all specs in spec/' ],
766
+ [ 'clean', "does `make clean' for each extention" ],
767
+ [ 'distclean',"does `make distclean' for each extention" ]
768
+ ]
769
+
770
+ def ToplevelInstaller.invoke
771
+ config = ConfigTable.new(load_rbconfig())
772
+ config.load_standard_entries
773
+ config.load_multipackage_entries if multipackage?
774
+ config.fixup
775
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
776
+ klass.new(File.dirname($0), config).invoke
777
+ end
778
+
779
+ def ToplevelInstaller.multipackage?
780
+ File.dir?(File.dirname($0) + '/packages')
781
+ end
782
+
783
+ def ToplevelInstaller.load_rbconfig
784
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
785
+ ARGV.delete(arg)
786
+ load File.expand_path(arg.split(/=/, 2)[1])
787
+ $".push 'rbconfig.rb'
788
+ else
789
+ require 'rbconfig'
790
+ end
791
+ ::Config::CONFIG
792
+ end
793
+
794
+ def initialize(ardir_root, config)
795
+ @ardir = File.expand_path(ardir_root)
796
+ @config = config
797
+ # cache
798
+ @valid_task_re = nil
799
+ end
800
+
801
+ def config(key)
802
+ @config[key]
803
+ end
804
+
805
+ def inspect
806
+ "#<#{self.class} #{__id__()}>"
807
+ end
808
+
809
+ def invoke
810
+ run_metaconfigs
811
+ case task = parsearg_global()
812
+ when nil, 'all'
813
+ parsearg_config
814
+ init_installers
815
+ exec_config
816
+ exec_setup
817
+ exec_install
818
+ else
819
+ case task
820
+ when 'config', 'test', 'spec'
821
+ ;
822
+ when 'clean', 'distclean'
823
+ @config.load_savefile if File.exist?(@config.savefile)
824
+ else
825
+ @config.load_savefile
826
+ end
827
+ __send__ "parsearg_#{task}"
828
+ init_installers
829
+ __send__ "exec_#{task}"
830
+ end
831
+ end
832
+
833
+ def run_metaconfigs
834
+ @config.load_script "#{@ardir}/metaconfig"
835
+ end
836
+
837
+ def init_installers
838
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
839
+ end
840
+
841
+ #
842
+ # Hook Script API bases
843
+ #
844
+
845
+ def srcdir_root
846
+ @ardir
847
+ end
848
+
849
+ def objdir_root
850
+ '.'
851
+ end
852
+
853
+ def relpath
854
+ '.'
855
+ end
856
+
857
+ #
858
+ # Option Parsing
859
+ #
860
+
861
+ def parsearg_global
862
+ while arg = ARGV.shift
863
+ case arg
864
+ when /\A\w+\z/
865
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
866
+ return arg
867
+ when '-q', '--quiet'
868
+ @config.verbose = false
869
+ when '--verbose'
870
+ @config.verbose = true
871
+ when '--help'
872
+ print_usage $stdout
873
+ exit 0
874
+ when '--version'
875
+ puts "#{File.basename($0)} version #{Version}"
876
+ exit 0
877
+ when '--copyright'
878
+ puts Copyright
879
+ exit 0
880
+ else
881
+ setup_rb_error "unknown global option '#{arg}'"
882
+ end
883
+ end
884
+ nil
885
+ end
886
+
887
+ def valid_task?(t)
888
+ valid_task_re() =~ t
889
+ end
890
+
891
+ def valid_task_re
892
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
893
+ end
894
+
895
+ def parsearg_no_options
896
+ unless ARGV.empty?
897
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
898
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
899
+ end
900
+ end
901
+
902
+ alias parsearg_show parsearg_no_options
903
+ alias parsearg_setup parsearg_no_options
904
+ alias parsearg_test parsearg_no_options
905
+ alias parsearg_clean parsearg_no_options
906
+ alias parsearg_distclean parsearg_no_options
907
+
908
+ def parsearg_config
909
+ evalopt = []
910
+ set = []
911
+ @config.config_opt = []
912
+ while i = ARGV.shift
913
+ if /\A--?\z/ =~ i
914
+ @config.config_opt = ARGV.dup
915
+ break
916
+ end
917
+ name, value = *@config.parse_opt(i)
918
+ if @config.value_config?(name)
919
+ @config[name] = value
920
+ else
921
+ evalopt.push [name, value]
922
+ end
923
+ set.push name
924
+ end
925
+ evalopt.each do |name, value|
926
+ @config.lookup(name).evaluate value, @config
927
+ end
928
+ # Check if configuration is valid
929
+ set.each do |n|
930
+ @config[n] if @config.value_config?(n)
931
+ end
932
+ end
933
+
934
+ def parsearg_install
935
+ @config.no_harm = false
936
+ @config.install_prefix = ''
937
+ while a = ARGV.shift
938
+ case a
939
+ when '--no-harm'
940
+ @config.no_harm = true
941
+ when /\A--prefix=/
942
+ path = a.split(/=/, 2)[1]
943
+ path = File.expand_path(path) unless path[0,1] == '/'
944
+ @config.install_prefix = path
945
+ else
946
+ setup_rb_error "install: unknown option #{a}"
947
+ end
948
+ end
949
+ end
950
+
951
+ def parsearg_spec
952
+ @config.spec_opt = []
953
+ while a = ARGV.shift
954
+ case a
955
+ when /\A--\Z/
956
+ break
957
+ when /\A-/
958
+ @config.spec_opt.push a
959
+ else
960
+ setup_rb_error "spec: invalid option #{a}"
961
+ end
962
+ end
963
+ unless ARGV.empty?
964
+ setup_rb_error "spec: invalid option #{ARGV.join(' ')}"
965
+ end
966
+ end
967
+
968
+ def print_usage(out)
969
+ out.puts 'Typical Installation Procedure:'
970
+ out.puts " $ ruby #{File.basename $0} config"
971
+ out.puts " $ ruby #{File.basename $0} setup"
972
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
973
+ out.puts
974
+ out.puts 'Detailed Usage:'
975
+ out.puts " ruby #{File.basename $0} <global option>"
976
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
977
+
978
+ fmt = " %-24s %s\n"
979
+ out.puts
980
+ out.puts 'Global options:'
981
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
982
+ out.printf fmt, ' --verbose', 'output messages verbosely'
983
+ out.printf fmt, ' --help', 'print this message'
984
+ out.printf fmt, ' --version', 'print version and quit'
985
+ out.printf fmt, ' --copyright', 'print copyright and quit'
986
+ out.puts
987
+ out.puts 'Tasks:'
988
+ TASKS.each do |name, desc|
989
+ out.printf fmt, name, desc
990
+ end
991
+
992
+ fmt = " %-24s %s [%s]\n"
993
+ out.puts
994
+ out.puts 'Options for CONFIG or ALL:'
995
+ @config.each do |item|
996
+ out.printf fmt, item.help_opt, item.description, item.help_default
997
+ end
998
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
999
+ out.puts
1000
+ out.puts 'Options for INSTALL:'
1001
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
1002
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
1003
+ out.puts
1004
+ end
1005
+
1006
+ #
1007
+ # Task Handlers
1008
+ #
1009
+
1010
+ def exec_config
1011
+ @installer.exec_config
1012
+ @config.save # must be final
1013
+ end
1014
+
1015
+ def exec_setup
1016
+ @installer.exec_setup
1017
+ end
1018
+
1019
+ def exec_install
1020
+ @installer.exec_install
1021
+ end
1022
+
1023
+ def exec_test
1024
+ @installer.exec_test
1025
+ end
1026
+
1027
+ def exec_spec
1028
+ @installer.exec_spec
1029
+ end
1030
+
1031
+ def exec_show
1032
+ @config.each do |i|
1033
+ printf "%-20s %s\n", i.name, i.value if i.value?
1034
+ end
1035
+ end
1036
+
1037
+ def exec_clean
1038
+ @installer.exec_clean
1039
+ end
1040
+
1041
+ def exec_distclean
1042
+ @installer.exec_distclean
1043
+ end
1044
+
1045
+ end # class ToplevelInstaller
1046
+
1047
+
1048
+ class ToplevelInstallerMulti < ToplevelInstaller
1049
+
1050
+ include FileOperations
1051
+
1052
+ def initialize(ardir_root, config)
1053
+ super
1054
+ @packages = directories_of("#{@ardir}/packages")
1055
+ raise 'no package exists' if @packages.empty?
1056
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1057
+ end
1058
+
1059
+ def run_metaconfigs
1060
+ @config.load_script "#{@ardir}/metaconfig", self
1061
+ @packages.each do |name|
1062
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1063
+ end
1064
+ end
1065
+
1066
+ attr_reader :packages
1067
+
1068
+ def packages=(list)
1069
+ raise 'package list is empty' if list.empty?
1070
+ list.each do |name|
1071
+ raise "directory packages/#{name} does not exist"\
1072
+ unless File.dir?("#{@ardir}/packages/#{name}")
1073
+ end
1074
+ @packages = list
1075
+ end
1076
+
1077
+ def init_installers
1078
+ @installers = {}
1079
+ @packages.each do |pack|
1080
+ @installers[pack] = Installer.new(@config,
1081
+ "#{@ardir}/packages/#{pack}",
1082
+ "packages/#{pack}")
1083
+ end
1084
+ with = extract_selection(config('with'))
1085
+ without = extract_selection(config('without'))
1086
+ @selected = @installers.keys.select {|name|
1087
+ (with.empty? or with.include?(name)) \
1088
+ and not without.include?(name)
1089
+ }
1090
+ end
1091
+
1092
+ def extract_selection(list)
1093
+ a = list.split(/,/)
1094
+ a.each do |name|
1095
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1096
+ end
1097
+ a
1098
+ end
1099
+
1100
+ def print_usage(f)
1101
+ super
1102
+ f.puts 'Inluded packages:'
1103
+ f.puts ' ' + @packages.sort.join(' ')
1104
+ f.puts
1105
+ end
1106
+
1107
+ #
1108
+ # Task Handlers
1109
+ #
1110
+
1111
+ def exec_config
1112
+ run_hook 'pre-config'
1113
+ each_selected_installers {|inst| inst.exec_config }
1114
+ run_hook 'post-config'
1115
+ @config.save # must be final
1116
+ end
1117
+
1118
+ def exec_setup
1119
+ run_hook 'pre-setup'
1120
+ each_selected_installers {|inst| inst.exec_setup }
1121
+ run_hook 'post-setup'
1122
+ end
1123
+
1124
+ def exec_install
1125
+ run_hook 'pre-install'
1126
+ each_selected_installers {|inst| inst.exec_install }
1127
+ run_hook 'post-install'
1128
+ end
1129
+
1130
+ def exec_test
1131
+ run_hook 'pre-test'
1132
+ each_selected_installers {|inst| inst.exec_test }
1133
+ run_hook 'post-test'
1134
+ end
1135
+
1136
+ def exec_spec
1137
+ run_hook 'pre-spec'
1138
+ each_selected_installers {|inst| inst.exec_spec }
1139
+ run_hook 'post-spec'
1140
+ end
1141
+
1142
+ def exec_clean
1143
+ rm_f @config.savefile
1144
+ run_hook 'pre-clean'
1145
+ each_selected_installers {|inst| inst.exec_clean }
1146
+ run_hook 'post-clean'
1147
+ end
1148
+
1149
+ def exec_distclean
1150
+ rm_f @config.savefile
1151
+ run_hook 'pre-distclean'
1152
+ each_selected_installers {|inst| inst.exec_distclean }
1153
+ run_hook 'post-distclean'
1154
+ end
1155
+
1156
+ #
1157
+ # lib
1158
+ #
1159
+
1160
+ def each_selected_installers
1161
+ Dir.mkdir 'packages' unless File.dir?('packages')
1162
+ @selected.each do |pack|
1163
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1164
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1165
+ Dir.chdir "packages/#{pack}"
1166
+ yield @installers[pack]
1167
+ Dir.chdir '../..'
1168
+ end
1169
+ end
1170
+
1171
+ def run_hook(id)
1172
+ @root_installer.run_hook id
1173
+ end
1174
+
1175
+ # module FileOperations requires this
1176
+ def verbose?
1177
+ @config.verbose?
1178
+ end
1179
+
1180
+ # module FileOperations requires this
1181
+ def no_harm?
1182
+ @config.no_harm?
1183
+ end
1184
+
1185
+ end # class ToplevelInstallerMulti
1186
+
1187
+
1188
+ class Installer
1189
+
1190
+ FILETYPES = %w( bin lib ext data conf man )
1191
+
1192
+ include FileOperations
1193
+ include HookScriptAPI
1194
+
1195
+ def initialize(config, srcroot, objroot)
1196
+ @config = config
1197
+ @srcdir = File.expand_path(srcroot)
1198
+ @objdir = File.expand_path(objroot)
1199
+ @currdir = '.'
1200
+ end
1201
+
1202
+ def inspect
1203
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1204
+ end
1205
+
1206
+ def noop(rel)
1207
+ end
1208
+
1209
+ #
1210
+ # Hook Script API base methods
1211
+ #
1212
+
1213
+ def srcdir_root
1214
+ @srcdir
1215
+ end
1216
+
1217
+ def objdir_root
1218
+ @objdir
1219
+ end
1220
+
1221
+ def relpath
1222
+ @currdir
1223
+ end
1224
+
1225
+ #
1226
+ # Config Access
1227
+ #
1228
+
1229
+ # module FileOperations requires this
1230
+ def verbose?
1231
+ @config.verbose?
1232
+ end
1233
+
1234
+ # module FileOperations requires this
1235
+ def no_harm?
1236
+ @config.no_harm?
1237
+ end
1238
+
1239
+ def verbose_off
1240
+ begin
1241
+ save, @config.verbose = @config.verbose?, false
1242
+ yield
1243
+ ensure
1244
+ @config.verbose = save
1245
+ end
1246
+ end
1247
+
1248
+ #
1249
+ # TASK config
1250
+ #
1251
+
1252
+ def exec_config
1253
+ exec_task_traverse 'config'
1254
+ end
1255
+
1256
+ alias config_dir_bin noop
1257
+ alias config_dir_lib noop
1258
+
1259
+ def config_dir_ext(rel)
1260
+ extconf if extdir?(curr_srcdir())
1261
+ end
1262
+
1263
+ alias config_dir_data noop
1264
+ alias config_dir_conf noop
1265
+ alias config_dir_man noop
1266
+
1267
+ def extconf
1268
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1269
+ end
1270
+
1271
+ #
1272
+ # TASK setup
1273
+ #
1274
+
1275
+ def exec_setup
1276
+ exec_task_traverse 'setup'
1277
+ end
1278
+
1279
+ def setup_dir_bin(rel)
1280
+ files_of(curr_srcdir()).each do |fname|
1281
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
1282
+ end
1283
+ end
1284
+
1285
+ alias setup_dir_lib noop
1286
+
1287
+ def setup_dir_ext(rel)
1288
+ make if extdir?(curr_srcdir())
1289
+ end
1290
+
1291
+ alias setup_dir_data noop
1292
+ alias setup_dir_conf noop
1293
+ alias setup_dir_man noop
1294
+
1295
+ def update_shebang_line(path)
1296
+ return if no_harm?
1297
+ return if config('shebang') == 'never'
1298
+ old = Shebang.load(path)
1299
+ if old
1300
+ $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
1301
+ new = new_shebang(old)
1302
+ return if new.to_s == old.to_s
1303
+ else
1304
+ return unless config('shebang') == 'all'
1305
+ new = Shebang.new(config('rubypath'))
1306
+ end
1307
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1308
+ open_atomic_writer(path) {|output|
1309
+ File.open(path, 'rb') {|f|
1310
+ f.gets if old # discard
1311
+ output.puts new.to_s
1312
+ output.print f.read
1313
+ }
1314
+ }
1315
+ end
1316
+
1317
+ def new_shebang(old)
1318
+ if /\Aruby/ =~ File.basename(old.cmd)
1319
+ Shebang.new(config('rubypath'), old.args)
1320
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1321
+ Shebang.new(config('rubypath'), old.args[1..-1])
1322
+ else
1323
+ return old unless config('shebang') == 'all'
1324
+ Shebang.new(config('rubypath'))
1325
+ end
1326
+ end
1327
+
1328
+ def open_atomic_writer(path, &block)
1329
+ tmpfile = File.basename(path) + '.tmp'
1330
+ begin
1331
+ File.open(tmpfile, 'wb', &block)
1332
+ File.rename tmpfile, File.basename(path)
1333
+ ensure
1334
+ File.unlink tmpfile if File.exist?(tmpfile)
1335
+ end
1336
+ end
1337
+
1338
+ class Shebang
1339
+ def Shebang.load(path)
1340
+ line = nil
1341
+ File.open(path) {|f|
1342
+ line = f.gets
1343
+ }
1344
+ return nil unless /\A#!/ =~ line
1345
+ parse(line)
1346
+ end
1347
+
1348
+ def Shebang.parse(line)
1349
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1350
+ new(cmd, args)
1351
+ end
1352
+
1353
+ def initialize(cmd, args = [])
1354
+ @cmd = cmd
1355
+ @args = args
1356
+ end
1357
+
1358
+ attr_reader :cmd
1359
+ attr_reader :args
1360
+
1361
+ def to_s
1362
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1363
+ end
1364
+ end
1365
+
1366
+ #
1367
+ # TASK install
1368
+ #
1369
+
1370
+ def exec_install
1371
+ rm_f 'InstalledFiles'
1372
+ exec_task_traverse 'install'
1373
+ end
1374
+
1375
+ def install_dir_bin(rel)
1376
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1377
+ end
1378
+
1379
+ def install_dir_lib(rel)
1380
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1381
+ end
1382
+
1383
+ def install_dir_ext(rel)
1384
+ return unless extdir?(curr_srcdir())
1385
+ install_files rubyextentions('.'),
1386
+ "#{config('sodir')}/#{File.dirname(rel)}",
1387
+ 0555
1388
+ end
1389
+
1390
+ def install_dir_data(rel)
1391
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1392
+ end
1393
+
1394
+ def install_dir_conf(rel)
1395
+ # FIXME: should not remove current config files
1396
+ # (rename previous file to .old/.org)
1397
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1398
+ end
1399
+
1400
+ def install_dir_man(rel)
1401
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1402
+ end
1403
+
1404
+ def install_files(list, dest, mode)
1405
+ mkdir_p dest, @config.install_prefix
1406
+ list.each do |fname|
1407
+ install fname, dest, mode, @config.install_prefix
1408
+ end
1409
+ end
1410
+
1411
+ def libfiles
1412
+ glob_reject(%w(*.y *.output), targetfiles())
1413
+ end
1414
+
1415
+ def rubyextentions(dir)
1416
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1417
+ if ents.empty?
1418
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1419
+ end
1420
+ ents
1421
+ end
1422
+
1423
+ def targetfiles
1424
+ mapdir(existfiles() - hookfiles())
1425
+ end
1426
+
1427
+ def mapdir(ents)
1428
+ ents.map {|ent|
1429
+ if File.exist?(ent)
1430
+ then ent # objdir
1431
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1432
+ end
1433
+ }
1434
+ end
1435
+
1436
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1437
+ JUNK_FILES = %w(
1438
+ core RCSLOG tags TAGS .make.state
1439
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1440
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1441
+
1442
+ *.org *.in .*
1443
+ )
1444
+
1445
+ def existfiles
1446
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1447
+ end
1448
+
1449
+ def hookfiles
1450
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1451
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1452
+ }.flatten
1453
+ end
1454
+
1455
+ def glob_select(pat, ents)
1456
+ re = globs2re([pat])
1457
+ ents.select {|ent| re =~ ent }
1458
+ end
1459
+
1460
+ def glob_reject(pats, ents)
1461
+ re = globs2re(pats)
1462
+ ents.reject {|ent| re =~ ent }
1463
+ end
1464
+
1465
+ GLOB2REGEX = {
1466
+ '.' => '\.',
1467
+ '$' => '\$',
1468
+ '#' => '\#',
1469
+ '*' => '.*'
1470
+ }
1471
+
1472
+ def globs2re(pats)
1473
+ /\A(?:#{
1474
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1475
+ })\z/
1476
+ end
1477
+
1478
+ #
1479
+ # TASK test
1480
+ #
1481
+
1482
+ TESTDIR = 'test'
1483
+
1484
+ def exec_test
1485
+ unless File.directory?('test')
1486
+ $stderr.puts 'no test in this package' if verbose?
1487
+ return
1488
+ end
1489
+ $stderr.puts 'Running tests...' if verbose?
1490
+ begin
1491
+ require 'test/unit'
1492
+ rescue LoadError
1493
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1494
+ end
1495
+ runner = Test::Unit::AutoRunner.new(true)
1496
+ runner.to_run << TESTDIR
1497
+ runner.run
1498
+ end
1499
+
1500
+ #
1501
+ # TASK spec
1502
+ #
1503
+
1504
+ SPECDIR = 'spec'
1505
+
1506
+ def exec_spec
1507
+ unless File.directory?('spec')
1508
+ $stderr.puts 'no spec in this package' if verbose?
1509
+ return
1510
+ end
1511
+ $stderr.puts 'Running specs...' if verbose?
1512
+ begin
1513
+ require 'spec'
1514
+ rescue LoadError
1515
+ setup_rb_error 'spec cannot be loaded. You need RSpec to invoke this task.'
1516
+ end
1517
+ specs = glob_select('*_spec.rb', files_of(SPECDIR)).map{|fname| "#{curr_srcdir()}/#{SPECDIR}/#{fname}"}
1518
+ spec_args = @config.spec_opt + specs
1519
+ Spec::Runner::CommandLine.run(spec_args, $stderr, $stdout, true, true)
1520
+ end
1521
+
1522
+ #
1523
+ # TASK clean
1524
+ #
1525
+
1526
+ def exec_clean
1527
+ exec_task_traverse 'clean'
1528
+ rm_f @config.savefile
1529
+ rm_f 'InstalledFiles'
1530
+ end
1531
+
1532
+ alias clean_dir_bin noop
1533
+ alias clean_dir_lib noop
1534
+ alias clean_dir_data noop
1535
+ alias clean_dir_conf noop
1536
+ alias clean_dir_man noop
1537
+
1538
+ def clean_dir_ext(rel)
1539
+ return unless extdir?(curr_srcdir())
1540
+ make 'clean' if File.file?('Makefile')
1541
+ end
1542
+
1543
+ #
1544
+ # TASK distclean
1545
+ #
1546
+
1547
+ def exec_distclean
1548
+ exec_task_traverse 'distclean'
1549
+ rm_f @config.savefile
1550
+ rm_f 'InstalledFiles'
1551
+ end
1552
+
1553
+ alias distclean_dir_bin noop
1554
+ alias distclean_dir_lib noop
1555
+
1556
+ def distclean_dir_ext(rel)
1557
+ return unless extdir?(curr_srcdir())
1558
+ make 'distclean' if File.file?('Makefile')
1559
+ end
1560
+
1561
+ alias distclean_dir_data noop
1562
+ alias distclean_dir_conf noop
1563
+ alias distclean_dir_man noop
1564
+
1565
+ #
1566
+ # Traversing
1567
+ #
1568
+
1569
+ def exec_task_traverse(task)
1570
+ run_hook "pre-#{task}"
1571
+ FILETYPES.each do |type|
1572
+ if type == 'ext' and config('without-ext') == 'yes'
1573
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1574
+ next
1575
+ end
1576
+ traverse task, type, "#{task}_dir_#{type}"
1577
+ end
1578
+ run_hook "post-#{task}"
1579
+ end
1580
+
1581
+ def traverse(task, rel, mid)
1582
+ dive_into(rel) {
1583
+ run_hook "pre-#{task}"
1584
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1585
+ directories_of(curr_srcdir()).each do |d|
1586
+ traverse task, "#{rel}/#{d}", mid
1587
+ end
1588
+ run_hook "post-#{task}"
1589
+ }
1590
+ end
1591
+
1592
+ def dive_into(rel)
1593
+ return unless File.dir?("#{@srcdir}/#{rel}")
1594
+
1595
+ dir = File.basename(rel)
1596
+ Dir.mkdir dir unless File.dir?(dir)
1597
+ prevdir = Dir.pwd
1598
+ Dir.chdir dir
1599
+ $stderr.puts '---> ' + rel if verbose?
1600
+ @currdir = rel
1601
+ yield
1602
+ Dir.chdir prevdir
1603
+ $stderr.puts '<--- ' + rel if verbose?
1604
+ @currdir = File.dirname(rel)
1605
+ end
1606
+
1607
+ def run_hook(id)
1608
+ path = [ "#{curr_srcdir()}/#{id}",
1609
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1610
+ return unless path
1611
+ begin
1612
+ instance_eval File.read(path), path, 1
1613
+ rescue
1614
+ raise if $DEBUG
1615
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1616
+ end
1617
+ end
1618
+
1619
+ end # class Installer
1620
+
1621
+
1622
+ class SetupError < StandardError; end
1623
+
1624
+ def setup_rb_error(msg)
1625
+ raise SetupError, msg
1626
+ end
1627
+
1628
+ if $0 == __FILE__
1629
+ begin
1630
+ ToplevelInstaller.invoke
1631
+ rescue SetupError
1632
+ raise if $DEBUG
1633
+ $stderr.puts $!.message
1634
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1635
+ exit 1
1636
+ end
1637
+ end