~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

« back to all changes in this revision

Viewing changes to setup.rb

  • Committer: Bazaar Package Importer
  • Author(s): Vincent Fourmond
  • Date: 2011-01-24 21:36:06 UTC
  • Revision ID: james.westby@ubuntu.com-20110124213606-9ettx0ugl83z0bzp
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

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