nmea 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ #ifndef _NMEA_PARSER_
2
+ #define _NMEA_PARSER_
3
+ #include <ruby.h>
4
+ #include <rubyio.h>
5
+ #include <stdio.h>
6
+ #include <string.h>
7
+ #include <stdlib.h>
8
+ #include <time.h>
9
+ #include <math.h>
10
+
11
+ extern VALUE mGPS, mNMEA, cLatitude, cLongitude, cTime, eParseError, cSatelliteInfo;
12
+ extern ID id_GPS, id_Latitude, id_Longitude, id_new, id_rmc, id_gsv, id_gsa, id_gga;
13
+ extern VALUE id_start, id_continue, id_finish, id_manual, id_automatic, id_no_fix, id_2d, id_3d;
14
+
15
+
16
+ int load_constants();
17
+ void nmea_scanner(char *buffer, VALUE handler);
18
+
19
+
20
+ #endif /* _NMEA_PARSER_ */
@@ -0,0 +1,65 @@
1
+ #include "nmea.h"
2
+
3
+ VALUE mGPS, mNMEA, cLatitude, cLongitude, cTime, eParseError, cSatelliteInfo;
4
+ ID id_GPS, id_Latitude, id_Longitude, id_new, id_rmc;
5
+ ID id_gsv, id_gsa, id_gga;
6
+ VALUE id_start, id_continue, id_finish;
7
+ VALUE id_manual, id_automatic, id_no_fix, id_2d, id_3d;
8
+
9
+ int load_constants() {
10
+ if(!rb_const_defined(rb_cObject, id_GPS)) return 0;
11
+ mGPS = rb_const_get(rb_cObject, rb_intern("GPS"));
12
+ if(!rb_const_defined(mGPS, id_Latitude)) return 0;
13
+ cLatitude = rb_const_get(mGPS, rb_intern("Latitude"));
14
+ if(!rb_const_defined(mGPS, id_Longitude)) return 0;
15
+ cLongitude = rb_const_get(mGPS, rb_intern("Longitude"));
16
+ return 1;
17
+ }
18
+
19
+ /*
20
+ * Usage: NMEA::scan(nmea_sentence, handler)
21
+ */
22
+ static VALUE scan(VALUE self, VALUE sentence, VALUE handler) {
23
+ Check_Type(sentence, T_STRING);
24
+ nmea_scanner(RSTRING(sentence)->ptr, handler);
25
+ return Qnil;
26
+ }
27
+
28
+
29
+
30
+ void Init_nmea() {
31
+ id_GPS = rb_intern("GPS");
32
+ id_Latitude = rb_intern("Latitude");
33
+ id_Longitude = rb_intern("Longitude");
34
+ id_new = rb_intern("new");
35
+ id_rmc = rb_intern("rmc");
36
+ id_gsv = rb_intern("gsv");
37
+ id_start = ID2SYM(rb_intern("start"));
38
+ id_continue = ID2SYM(rb_intern("continue"));
39
+ id_finish = ID2SYM(rb_intern("finish"));
40
+ id_gsa = rb_intern("gsa");
41
+ id_manual = ID2SYM(rb_intern("manual"));
42
+ id_automatic = ID2SYM(rb_intern("automatic"));
43
+ id_no_fix = ID2SYM(rb_intern("no_fix"));
44
+ id_2d = ID2SYM(rb_intern("gps_2d"));
45
+ id_3d = ID2SYM(rb_intern("gps_3d"));
46
+ id_gga = rb_intern("gga");
47
+ cLatitude = Qnil;
48
+ cLongitude = Qnil;
49
+ cTime = rb_const_get(rb_cObject, rb_intern("Time"));
50
+ /*
51
+ * Document-module: NMEA
52
+ * NMEA module has the only method: scan. Better read about it.
53
+ */
54
+ mNMEA = rb_define_module("NMEA");
55
+ rb_define_singleton_method(mNMEA, "scan", scan, 2);
56
+ /*
57
+ * Document-class: NMEA::ParseError
58
+ * You will receive ParseError if NMEA::scan cannot parse sentence. Usually it happens, when
59
+ * you try to parse broken sentence. Perhaps, through it away.
60
+ */
61
+ eParseError = rb_define_class_under(mNMEA, "ParseError", rb_eStandardError);
62
+ cSatelliteInfo = rb_struct_define(NULL, "number", "elevation", "azimuth", "signal_level", NULL);
63
+ /* Struct.new(:number, :elevation, :azimuth, :signal_level): assigned to SatelliteInfo */
64
+ rb_define_const(mNMEA, "SatelliteInfo", cSatelliteInfo);
65
+ }
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'rutils'
4
+ require 'serialport'
5
+ require 'nmea'
6
+ require File.dirname(__FILE__)+"/../test/mocks"
7
+
8
+
9
+
10
+ class SerialPort
11
+ def self.try_open(port, *params)
12
+ begin
13
+ sp = create(port)
14
+ sp.set_modem_params(*params)
15
+ return sp
16
+ rescue StandardError => e
17
+ puts e
18
+ sleep 2
19
+ retry
20
+ end
21
+ end
22
+
23
+ def self.try_gets(*params)
24
+ begin
25
+ @sp ||= try_open(*params)
26
+ return @sp.gets
27
+ rescue
28
+ @sp = nil
29
+ retry
30
+ end
31
+ end
32
+ end
33
+
34
+ @handler = NMEAHandler.new
35
+ @f = File.open(File.dirname(__FILE__)+"/../parser/log.txt")
36
+ loop do
37
+ #sentence = SerialPort.try_gets("/dev/tty.usbserial", 4800, 8, 1, SerialPort::NONE)
38
+ sentence = @f.gets
39
+ break unless sentence
40
+ #puts sentence if sentence =~ /\$GPGGA/
41
+ begin
42
+ NMEA.scan(sentence, NMEAHandler.new)
43
+ rescue NMEA::NMEAError => e
44
+ puts "Parse error: '#{sentence}'"
45
+ end
46
+ end
47
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__)+"/lib/nmea"
2
+
@@ -0,0 +1,1555 @@
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
+ @libsrc_pattern = '*.rb'
59
+ end
60
+
61
+ attr_accessor :install_prefix
62
+ attr_accessor :config_opt
63
+
64
+ attr_writer :verbose
65
+
66
+ def verbose?
67
+ @verbose
68
+ end
69
+
70
+ attr_writer :no_harm
71
+
72
+ def no_harm?
73
+ @no_harm
74
+ end
75
+
76
+ attr_accessor :libsrc_pattern
77
+
78
+ def [](key)
79
+ lookup(key).resolve(self)
80
+ end
81
+
82
+ def []=(key, val)
83
+ lookup(key).set val
84
+ end
85
+
86
+ def names
87
+ @items.map {|i| i.name }
88
+ end
89
+
90
+ def each(&block)
91
+ @items.each(&block)
92
+ end
93
+
94
+ def key?(name)
95
+ @table.key?(name)
96
+ end
97
+
98
+ def lookup(name)
99
+ @table[name] or setup_rb_error "no such config item: #{name}"
100
+ end
101
+
102
+ def add(item)
103
+ @items.push item
104
+ @table[item.name] = item
105
+ end
106
+
107
+ def remove(name)
108
+ item = lookup(name)
109
+ @items.delete_if {|i| i.name == name }
110
+ @table.delete_if {|name, i| i.name == name }
111
+ item
112
+ end
113
+
114
+ def load_script(path, inst = nil)
115
+ if File.file?(path)
116
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
117
+ end
118
+ end
119
+
120
+ def savefile
121
+ '.config'
122
+ end
123
+
124
+ def load_savefile
125
+ begin
126
+ File.foreach(savefile()) do |line|
127
+ k, v = *line.split(/=/, 2)
128
+ self[k] = v.strip
129
+ end
130
+ rescue Errno::ENOENT
131
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
132
+ end
133
+ end
134
+
135
+ def save
136
+ @items.each {|i| i.value }
137
+ File.open(savefile(), 'w') {|f|
138
+ @items.each do |i|
139
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
140
+ end
141
+ }
142
+ end
143
+
144
+ def load_standard_entries
145
+ standard_entries(@rbconfig).each do |ent|
146
+ add ent
147
+ end
148
+ end
149
+
150
+ def standard_entries(rbconfig)
151
+ c = rbconfig
152
+
153
+ rubypath = c['bindir'] + '/' + c['ruby_install_name']
154
+
155
+ major = c['MAJOR'].to_i
156
+ minor = c['MINOR'].to_i
157
+ teeny = c['TEENY'].to_i
158
+ version = "#{major}.#{minor}"
159
+
160
+ # ruby ver. >= 1.4.4?
161
+ newpath_p = ((major >= 2) or
162
+ ((major == 1) and
163
+ ((minor >= 5) or
164
+ ((minor == 4) and (teeny >= 4)))))
165
+
166
+ if c['rubylibdir']
167
+ # V > 1.6.3
168
+ libruby = "#{c['prefix']}/lib/ruby"
169
+ librubyver = c['rubylibdir']
170
+ librubyverarch = c['archdir']
171
+ siteruby = c['sitedir']
172
+ siterubyver = c['sitelibdir']
173
+ siterubyverarch = c['sitearchdir']
174
+ elsif newpath_p
175
+ # 1.4.4 <= V <= 1.6.3
176
+ libruby = "#{c['prefix']}/lib/ruby"
177
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
178
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
179
+ siteruby = c['sitedir']
180
+ siterubyver = "$siteruby/#{version}"
181
+ siterubyverarch = "$siterubyver/#{c['arch']}"
182
+ else
183
+ # V < 1.4.4
184
+ libruby = "#{c['prefix']}/lib/ruby"
185
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
186
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
187
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
188
+ siterubyver = siteruby
189
+ siterubyverarch = "$siterubyver/#{c['arch']}"
190
+ end
191
+ parameterize = lambda {|path|
192
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
193
+ }
194
+
195
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
196
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
197
+ else
198
+ makeprog = 'make'
199
+ end
200
+
201
+ [
202
+ ExecItem.new('installdirs', 'std/site/home',
203
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
204
+ {|val, table|
205
+ case val
206
+ when 'std'
207
+ table['rbdir'] = '$librubyver'
208
+ table['sodir'] = '$librubyverarch'
209
+ when 'site'
210
+ table['rbdir'] = '$siterubyver'
211
+ table['sodir'] = '$siterubyverarch'
212
+ when 'home'
213
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
214
+ table['prefix'] = ENV['HOME']
215
+ table['rbdir'] = '$libdir/ruby'
216
+ table['sodir'] = '$libdir/ruby'
217
+ end
218
+ },
219
+ PathItem.new('prefix', 'path', c['prefix'],
220
+ 'path prefix of target environment'),
221
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
222
+ 'the directory for commands'),
223
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
224
+ 'the directory for libraries'),
225
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
226
+ 'the directory for shared data'),
227
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
228
+ 'the directory for man pages'),
229
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
230
+ 'the directory for system configuration files'),
231
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
232
+ 'the directory for local state data'),
233
+ PathItem.new('libruby', 'path', libruby,
234
+ 'the directory for ruby libraries'),
235
+ PathItem.new('librubyver', 'path', librubyver,
236
+ 'the directory for standard ruby libraries'),
237
+ PathItem.new('librubyverarch', 'path', librubyverarch,
238
+ 'the directory for standard ruby extensions'),
239
+ PathItem.new('siteruby', 'path', siteruby,
240
+ 'the directory for version-independent aux ruby libraries'),
241
+ PathItem.new('siterubyver', 'path', siterubyver,
242
+ 'the directory for aux ruby libraries'),
243
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
244
+ 'the directory for aux ruby binaries'),
245
+ PathItem.new('rbdir', 'path', '$siterubyver',
246
+ 'the directory for ruby scripts'),
247
+ PathItem.new('sodir', 'path', '$siterubyverarch',
248
+ 'the directory for ruby extentions'),
249
+ PathItem.new('rubypath', 'path', rubypath,
250
+ 'the path to set to #! line'),
251
+ ProgramItem.new('rubyprog', 'name', rubypath,
252
+ 'the ruby program using for installation'),
253
+ ProgramItem.new('makeprog', 'name', makeprog,
254
+ 'the make program to compile ruby extentions'),
255
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
256
+ 'shebang line (#!) editing mode'),
257
+ BoolItem.new('without-ext', 'yes/no', 'no',
258
+ 'does not compile/install ruby extentions')
259
+ ]
260
+ end
261
+ private :standard_entries
262
+
263
+ def load_multipackage_entries
264
+ multipackage_entries().each do |ent|
265
+ add ent
266
+ end
267
+ end
268
+
269
+ def multipackage_entries
270
+ [
271
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
272
+ 'package names that you want to install'),
273
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
274
+ 'package names that you do not want to install')
275
+ ]
276
+ end
277
+ private :multipackage_entries
278
+
279
+ ALIASES = {
280
+ 'std-ruby' => 'librubyver',
281
+ 'stdruby' => 'librubyver',
282
+ 'rubylibdir' => 'librubyver',
283
+ 'archdir' => 'librubyverarch',
284
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
285
+ 'site-ruby' => 'siterubyver', # For backward compatibility
286
+ 'bin-dir' => 'bindir',
287
+ 'bin-dir' => 'bindir',
288
+ 'rb-dir' => 'rbdir',
289
+ 'so-dir' => 'sodir',
290
+ 'data-dir' => 'datadir',
291
+ 'ruby-path' => 'rubypath',
292
+ 'ruby-prog' => 'rubyprog',
293
+ 'ruby' => 'rubyprog',
294
+ 'make-prog' => 'makeprog',
295
+ 'make' => 'makeprog'
296
+ }
297
+
298
+ def fixup
299
+ ALIASES.each do |ali, name|
300
+ @table[ali] = @table[name]
301
+ end
302
+ @items.freeze
303
+ @table.freeze
304
+ @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
305
+ end
306
+
307
+ def parse_opt(opt)
308
+ m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
309
+ m.to_a[1,2]
310
+ end
311
+
312
+ def dllext
313
+ @rbconfig['DLEXT']
314
+ end
315
+
316
+ def value_config?(name)
317
+ lookup(name).value?
318
+ end
319
+
320
+ class Item
321
+ def initialize(name, template, default, desc)
322
+ @name = name.freeze
323
+ @template = template
324
+ @value = default
325
+ @default = default
326
+ @description = desc
327
+ end
328
+
329
+ attr_reader :name
330
+ attr_reader :description
331
+
332
+ attr_accessor :default
333
+ alias help_default default
334
+
335
+ def help_opt
336
+ "--#{@name}=#{@template}"
337
+ end
338
+
339
+ def value?
340
+ true
341
+ end
342
+
343
+ def value
344
+ @value
345
+ end
346
+
347
+ def resolve(table)
348
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
349
+ end
350
+
351
+ def set(val)
352
+ @value = check(val)
353
+ end
354
+
355
+ private
356
+
357
+ def check(val)
358
+ setup_rb_error "config: --#{name} requires argument" unless val
359
+ val
360
+ end
361
+ end
362
+
363
+ class BoolItem < Item
364
+ def config_type
365
+ 'bool'
366
+ end
367
+
368
+ def help_opt
369
+ "--#{@name}"
370
+ end
371
+
372
+ private
373
+
374
+ def check(val)
375
+ return 'yes' unless val
376
+ unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
377
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378
+ end
379
+ (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
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 intiailize(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
+ rescue => e
667
+ puts "The C extensions were not installed. But don't worry. Everything should work fine"
668
+ end
669
+
670
+ def extdir?(dir)
671
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
672
+ end
673
+
674
+ def files_of(dir)
675
+ Dir.open(dir) {|d|
676
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
677
+ }
678
+ end
679
+
680
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
681
+
682
+ def directories_of(dir)
683
+ Dir.open(dir) {|d|
684
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
685
+ }
686
+ end
687
+
688
+ end
689
+
690
+
691
+ # This module requires: #srcdir_root, #objdir_root, #relpath
692
+ module HookScriptAPI
693
+
694
+ def get_config(key)
695
+ @config[key]
696
+ end
697
+
698
+ alias config get_config
699
+
700
+ # obsolete: use metaconfig to change configuration
701
+ def set_config(key, val)
702
+ @config[key] = val
703
+ end
704
+
705
+ #
706
+ # srcdir/objdir (works only in the package directory)
707
+ #
708
+
709
+ def curr_srcdir
710
+ "#{srcdir_root()}/#{relpath()}"
711
+ end
712
+
713
+ def curr_objdir
714
+ "#{objdir_root()}/#{relpath()}"
715
+ end
716
+
717
+ def srcfile(path)
718
+ "#{curr_srcdir()}/#{path}"
719
+ end
720
+
721
+ def srcexist?(path)
722
+ File.exist?(srcfile(path))
723
+ end
724
+
725
+ def srcdirectory?(path)
726
+ File.dir?(srcfile(path))
727
+ end
728
+
729
+ def srcfile?(path)
730
+ File.file?(srcfile(path))
731
+ end
732
+
733
+ def srcentries(path = '.')
734
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
735
+ return d.to_a - %w(. ..)
736
+ }
737
+ end
738
+
739
+ def srcfiles(path = '.')
740
+ srcentries(path).select {|fname|
741
+ File.file?(File.join(curr_srcdir(), path, fname))
742
+ }
743
+ end
744
+
745
+ def srcdirectories(path = '.')
746
+ srcentries(path).select {|fname|
747
+ File.dir?(File.join(curr_srcdir(), path, fname))
748
+ }
749
+ end
750
+
751
+ end
752
+
753
+
754
+ class ToplevelInstaller
755
+
756
+ Version = '3.4.0'
757
+ Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
758
+
759
+ TASKS = [
760
+ [ 'all', 'do config, setup, then install' ],
761
+ [ 'config', 'saves your configurations' ],
762
+ [ 'show', 'shows current configuration' ],
763
+ [ 'setup', 'compiles ruby extentions and others' ],
764
+ [ 'install', 'installs files' ],
765
+ [ 'test', 'run all tests in test/' ],
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'
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
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
898
+ end
899
+ end
900
+
901
+ alias parsearg_show parsearg_no_options
902
+ alias parsearg_setup parsearg_no_options
903
+ alias parsearg_test parsearg_no_options
904
+ alias parsearg_clean parsearg_no_options
905
+ alias parsearg_distclean parsearg_no_options
906
+
907
+ def parsearg_config
908
+ evalopt = []
909
+ set = []
910
+ @config.config_opt = []
911
+ while i = ARGV.shift
912
+ if /\A--?\z/ =~ i
913
+ @config.config_opt = ARGV.dup
914
+ break
915
+ end
916
+ name, value = *@config.parse_opt(i)
917
+ if @config.value_config?(name)
918
+ @config[name] = value
919
+ else
920
+ evalopt.push [name, value]
921
+ end
922
+ set.push name
923
+ end
924
+ evalopt.each do |name, value|
925
+ @config.lookup(name).evaluate value, @config
926
+ end
927
+ # Check if configuration is valid
928
+ set.each do |n|
929
+ @config[n] if @config.value_config?(n)
930
+ end
931
+ end
932
+
933
+ def parsearg_install
934
+ @config.no_harm = false
935
+ @config.install_prefix = ''
936
+ while a = ARGV.shift
937
+ case a
938
+ when '--no-harm'
939
+ @config.no_harm = true
940
+ when /\A--prefix=/
941
+ path = a.split(/=/, 2)[1]
942
+ path = File.expand_path(path) unless path[0,1] == '/'
943
+ @config.install_prefix = path
944
+ else
945
+ setup_rb_error "install: unknown option #{a}"
946
+ end
947
+ end
948
+ end
949
+
950
+ def print_usage(out)
951
+ out.puts 'Typical Installation Procedure:'
952
+ out.puts " $ ruby #{File.basename $0} config"
953
+ out.puts " $ ruby #{File.basename $0} setup"
954
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
955
+ out.puts
956
+ out.puts 'Detailed Usage:'
957
+ out.puts " ruby #{File.basename $0} <global option>"
958
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
959
+
960
+ fmt = " %-24s %s\n"
961
+ out.puts
962
+ out.puts 'Global options:'
963
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
964
+ out.printf fmt, ' --verbose', 'output messages verbosely'
965
+ out.printf fmt, ' --help', 'print this message'
966
+ out.printf fmt, ' --version', 'print version and quit'
967
+ out.printf fmt, ' --copyright', 'print copyright and quit'
968
+ out.puts
969
+ out.puts 'Tasks:'
970
+ TASKS.each do |name, desc|
971
+ out.printf fmt, name, desc
972
+ end
973
+
974
+ fmt = " %-24s %s [%s]\n"
975
+ out.puts
976
+ out.puts 'Options for CONFIG or ALL:'
977
+ @config.each do |item|
978
+ out.printf fmt, item.help_opt, item.description, item.help_default
979
+ end
980
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
981
+ out.puts
982
+ out.puts 'Options for INSTALL:'
983
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
984
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
985
+ out.puts
986
+ end
987
+
988
+ #
989
+ # Task Handlers
990
+ #
991
+
992
+ def exec_config
993
+ @installer.exec_config
994
+ @config.save # must be final
995
+ end
996
+
997
+ def exec_setup
998
+ @installer.exec_setup
999
+ end
1000
+
1001
+ def exec_install
1002
+ @installer.exec_install
1003
+ end
1004
+
1005
+ def exec_test
1006
+ @installer.exec_test
1007
+ end
1008
+
1009
+ def exec_show
1010
+ @config.each do |i|
1011
+ printf "%-20s %s\n", i.name, i.value if i.value?
1012
+ end
1013
+ end
1014
+
1015
+ def exec_clean
1016
+ @installer.exec_clean
1017
+ end
1018
+
1019
+ def exec_distclean
1020
+ @installer.exec_distclean
1021
+ end
1022
+
1023
+ end # class ToplevelInstaller
1024
+
1025
+
1026
+ class ToplevelInstallerMulti < ToplevelInstaller
1027
+
1028
+ include FileOperations
1029
+
1030
+ def initialize(ardir_root, config)
1031
+ super
1032
+ @packages = directories_of("#{@ardir}/packages")
1033
+ raise 'no package exists' if @packages.empty?
1034
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1035
+ end
1036
+
1037
+ def run_metaconfigs
1038
+ @config.load_script "#{@ardir}/metaconfig", self
1039
+ @packages.each do |name|
1040
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1041
+ end
1042
+ end
1043
+
1044
+ attr_reader :packages
1045
+
1046
+ def packages=(list)
1047
+ raise 'package list is empty' if list.empty?
1048
+ list.each do |name|
1049
+ raise "directory packages/#{name} does not exist"\
1050
+ unless File.dir?("#{@ardir}/packages/#{name}")
1051
+ end
1052
+ @packages = list
1053
+ end
1054
+
1055
+ def init_installers
1056
+ @installers = {}
1057
+ @packages.each do |pack|
1058
+ @installers[pack] = Installer.new(@config,
1059
+ "#{@ardir}/packages/#{pack}",
1060
+ "packages/#{pack}")
1061
+ end
1062
+ with = extract_selection(config('with'))
1063
+ without = extract_selection(config('without'))
1064
+ @selected = @installers.keys.select {|name|
1065
+ (with.empty? or with.include?(name)) \
1066
+ and not without.include?(name)
1067
+ }
1068
+ end
1069
+
1070
+ def extract_selection(list)
1071
+ a = list.split(/,/)
1072
+ a.each do |name|
1073
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1074
+ end
1075
+ a
1076
+ end
1077
+
1078
+ def print_usage(f)
1079
+ super
1080
+ f.puts 'Inluded packages:'
1081
+ f.puts ' ' + @packages.sort.join(' ')
1082
+ f.puts
1083
+ end
1084
+
1085
+ #
1086
+ # Task Handlers
1087
+ #
1088
+
1089
+ def exec_config
1090
+ run_hook 'pre-config'
1091
+ each_selected_installers {|inst| inst.exec_config }
1092
+ run_hook 'post-config'
1093
+ @config.save # must be final
1094
+ end
1095
+
1096
+ def exec_setup
1097
+ run_hook 'pre-setup'
1098
+ each_selected_installers {|inst| inst.exec_setup }
1099
+ run_hook 'post-setup'
1100
+ end
1101
+
1102
+ def exec_install
1103
+ run_hook 'pre-install'
1104
+ each_selected_installers {|inst| inst.exec_install }
1105
+ run_hook 'post-install'
1106
+ end
1107
+
1108
+ def exec_test
1109
+ run_hook 'pre-test'
1110
+ each_selected_installers {|inst| inst.exec_test }
1111
+ run_hook 'post-test'
1112
+ end
1113
+
1114
+ def exec_clean
1115
+ rm_f @config.savefile
1116
+ run_hook 'pre-clean'
1117
+ each_selected_installers {|inst| inst.exec_clean }
1118
+ run_hook 'post-clean'
1119
+ end
1120
+
1121
+ def exec_distclean
1122
+ rm_f @config.savefile
1123
+ run_hook 'pre-distclean'
1124
+ each_selected_installers {|inst| inst.exec_distclean }
1125
+ run_hook 'post-distclean'
1126
+ end
1127
+
1128
+ #
1129
+ # lib
1130
+ #
1131
+
1132
+ def each_selected_installers
1133
+ Dir.mkdir 'packages' unless File.dir?('packages')
1134
+ @selected.each do |pack|
1135
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1136
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1137
+ Dir.chdir "packages/#{pack}"
1138
+ yield @installers[pack]
1139
+ Dir.chdir '../..'
1140
+ end
1141
+ end
1142
+
1143
+ def run_hook(id)
1144
+ @root_installer.run_hook id
1145
+ end
1146
+
1147
+ # module FileOperations requires this
1148
+ def verbose?
1149
+ @config.verbose?
1150
+ end
1151
+
1152
+ # module FileOperations requires this
1153
+ def no_harm?
1154
+ @config.no_harm?
1155
+ end
1156
+
1157
+ end # class ToplevelInstallerMulti
1158
+
1159
+
1160
+ class Installer
1161
+
1162
+ FILETYPES = %w( bin lib ext data conf man )
1163
+
1164
+ include FileOperations
1165
+ include HookScriptAPI
1166
+
1167
+ def initialize(config, srcroot, objroot)
1168
+ @config = config
1169
+ @srcdir = File.expand_path(srcroot)
1170
+ @objdir = File.expand_path(objroot)
1171
+ @currdir = '.'
1172
+ end
1173
+
1174
+ def inspect
1175
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1176
+ end
1177
+
1178
+ #
1179
+ # Hook Script API base methods
1180
+ #
1181
+
1182
+ def srcdir_root
1183
+ @srcdir
1184
+ end
1185
+
1186
+ def objdir_root
1187
+ @objdir
1188
+ end
1189
+
1190
+ def relpath
1191
+ @currdir
1192
+ end
1193
+
1194
+ #
1195
+ # Config Access
1196
+ #
1197
+
1198
+ # module FileOperations requires this
1199
+ def verbose?
1200
+ @config.verbose?
1201
+ end
1202
+
1203
+ # module FileOperations requires this
1204
+ def no_harm?
1205
+ @config.no_harm?
1206
+ end
1207
+
1208
+ def verbose_off
1209
+ begin
1210
+ save, @config.verbose = @config.verbose?, false
1211
+ yield
1212
+ ensure
1213
+ @config.verbose = save
1214
+ end
1215
+ end
1216
+
1217
+ #
1218
+ # TASK config
1219
+ #
1220
+
1221
+ def exec_config
1222
+ exec_task_traverse 'config'
1223
+ end
1224
+
1225
+ def config_dir_bin(rel)
1226
+ end
1227
+
1228
+ def config_dir_lib(rel)
1229
+ end
1230
+
1231
+ def config_dir_man(rel)
1232
+ end
1233
+
1234
+ def config_dir_ext(rel)
1235
+ extconf if extdir?(curr_srcdir())
1236
+ end
1237
+
1238
+ def extconf
1239
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1240
+ rescue => e
1241
+ puts "The C extensions could not be installed"
1242
+ end
1243
+
1244
+ def config_dir_data(rel)
1245
+ end
1246
+
1247
+ def config_dir_conf(rel)
1248
+ end
1249
+
1250
+ #
1251
+ # TASK setup
1252
+ #
1253
+
1254
+ def exec_setup
1255
+ exec_task_traverse 'setup'
1256
+ end
1257
+
1258
+ def setup_dir_bin(rel)
1259
+ files_of(curr_srcdir()).each do |fname|
1260
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
1261
+ end
1262
+ end
1263
+
1264
+ def adjust_shebang(path)
1265
+ return if no_harm?
1266
+ tmpfile = File.basename(path) + '.tmp'
1267
+ begin
1268
+ File.open(path, 'rb') {|r|
1269
+ first = r.gets
1270
+ return unless File.basename(first.sub(/\A\#!/, '').split[0].to_s) == 'ruby'
1271
+ $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1272
+ File.open(tmpfile, 'wb') {|w|
1273
+ w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
1274
+ w.write r.read
1275
+ }
1276
+ }
1277
+ move_file tmpfile, File.basename(path)
1278
+ ensure
1279
+ File.unlink tmpfile if File.exist?(tmpfile)
1280
+ end
1281
+ end
1282
+
1283
+ def setup_dir_lib(rel)
1284
+ end
1285
+
1286
+ def setup_dir_man(rel)
1287
+ end
1288
+
1289
+ def setup_dir_ext(rel)
1290
+ make if extdir?(curr_srcdir())
1291
+ end
1292
+
1293
+ def setup_dir_data(rel)
1294
+ end
1295
+
1296
+ def setup_dir_conf(rel)
1297
+ end
1298
+
1299
+ #
1300
+ # TASK install
1301
+ #
1302
+
1303
+ def exec_install
1304
+ rm_f 'InstalledFiles'
1305
+ exec_task_traverse 'install'
1306
+ end
1307
+
1308
+ def install_dir_bin(rel)
1309
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1310
+ end
1311
+
1312
+ def install_dir_lib(rel)
1313
+ install_files rubyscripts(), "#{config('rbdir')}/#{rel}", 0644
1314
+ end
1315
+
1316
+ def install_dir_ext(rel)
1317
+ return unless extdir?(curr_srcdir())
1318
+ install_files rubyextentions('.'),
1319
+ "#{config('sodir')}/#{File.dirname(rel)}",
1320
+ 0555
1321
+ end
1322
+
1323
+ def install_dir_data(rel)
1324
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1325
+ end
1326
+
1327
+ def install_dir_conf(rel)
1328
+ # FIXME: should not remove current config files
1329
+ # (rename previous file to .old/.org)
1330
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1331
+ end
1332
+
1333
+ def install_dir_man(rel)
1334
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1335
+ end
1336
+
1337
+ def install_files(list, dest, mode)
1338
+ mkdir_p dest, @config.install_prefix
1339
+ list.each do |fname|
1340
+ install fname, dest, mode, @config.install_prefix
1341
+ end
1342
+ end
1343
+
1344
+ def rubyscripts
1345
+ glob_select(@config.libsrc_pattern, targetfiles())
1346
+ end
1347
+
1348
+ def rubyextentions(dir)
1349
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1350
+ if ents.empty?
1351
+ puts "no ruby extention exists: 'ruby #{$0} setup' first"
1352
+ end
1353
+ ents
1354
+ end
1355
+
1356
+ def targetfiles
1357
+ mapdir(existfiles() - hookfiles())
1358
+ end
1359
+
1360
+ def mapdir(ents)
1361
+ ents.map {|ent|
1362
+ if File.exist?(ent)
1363
+ then ent # objdir
1364
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1365
+ end
1366
+ }
1367
+ end
1368
+
1369
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1370
+ JUNK_FILES = %w(
1371
+ core RCSLOG tags TAGS .make.state
1372
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1373
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1374
+
1375
+ *.org *.in .*
1376
+ )
1377
+
1378
+ def existfiles
1379
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1380
+ end
1381
+
1382
+ def hookfiles
1383
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1384
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1385
+ }.flatten
1386
+ end
1387
+
1388
+ def glob_select(pat, ents)
1389
+ re = globs2re([pat])
1390
+ ents.select {|ent| re =~ ent }
1391
+ end
1392
+
1393
+ def glob_reject(pats, ents)
1394
+ re = globs2re(pats)
1395
+ ents.reject {|ent| re =~ ent }
1396
+ end
1397
+
1398
+ GLOB2REGEX = {
1399
+ '.' => '\.',
1400
+ '$' => '\$',
1401
+ '#' => '\#',
1402
+ '*' => '.*'
1403
+ }
1404
+
1405
+ def globs2re(pats)
1406
+ /\A(?:#{
1407
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1408
+ })\z/
1409
+ end
1410
+
1411
+ #
1412
+ # TASK test
1413
+ #
1414
+
1415
+ TESTDIR = 'test'
1416
+
1417
+ def exec_test
1418
+ unless File.directory?('test')
1419
+ $stderr.puts 'no test in this package' if verbose?
1420
+ return
1421
+ end
1422
+ $stderr.puts 'Running tests...' if verbose?
1423
+ require 'test/unit'
1424
+ runner = Test::Unit::AutoRunner.new(true)
1425
+ runner.to_run << TESTDIR
1426
+ runner.run
1427
+ end
1428
+
1429
+ #
1430
+ # TASK clean
1431
+ #
1432
+
1433
+ def exec_clean
1434
+ exec_task_traverse 'clean'
1435
+ rm_f @config.savefile
1436
+ rm_f 'InstalledFiles'
1437
+ end
1438
+
1439
+ def clean_dir_bin(rel)
1440
+ end
1441
+
1442
+ def clean_dir_lib(rel)
1443
+ end
1444
+
1445
+ def clean_dir_ext(rel)
1446
+ return unless extdir?(curr_srcdir())
1447
+ make 'clean' if File.file?('Makefile')
1448
+ end
1449
+
1450
+ def clean_dir_data(rel)
1451
+ end
1452
+
1453
+ def clean_dir_conf(rel)
1454
+ end
1455
+
1456
+ #
1457
+ # TASK distclean
1458
+ #
1459
+
1460
+ def exec_distclean
1461
+ exec_task_traverse 'distclean'
1462
+ rm_f @config.savefile
1463
+ rm_f 'InstalledFiles'
1464
+ end
1465
+
1466
+ def distclean_dir_bin(rel)
1467
+ end
1468
+
1469
+ def distclean_dir_lib(rel)
1470
+ end
1471
+
1472
+ def distclean_dir_ext(rel)
1473
+ return unless extdir?(curr_srcdir())
1474
+ make 'distclean' if File.file?('Makefile')
1475
+ end
1476
+
1477
+ def distclean_dir_data(rel)
1478
+ end
1479
+
1480
+ def distclean_dir_conf(rel)
1481
+ end
1482
+
1483
+ #
1484
+ # lib
1485
+ #
1486
+
1487
+ def exec_task_traverse(task)
1488
+ run_hook "pre-#{task}"
1489
+ FILETYPES.each do |type|
1490
+ if config('without-ext') == 'yes' and type == 'ext'
1491
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1492
+ next
1493
+ end
1494
+ traverse task, type, "#{task}_dir_#{type}"
1495
+ end
1496
+ run_hook "post-#{task}"
1497
+ end
1498
+
1499
+ def traverse(task, rel, mid)
1500
+ dive_into(rel) {
1501
+ run_hook "pre-#{task}"
1502
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1503
+ directories_of(curr_srcdir()).each do |d|
1504
+ traverse task, "#{rel}/#{d}", mid
1505
+ end
1506
+ run_hook "post-#{task}"
1507
+ }
1508
+ end
1509
+
1510
+ def dive_into(rel)
1511
+ return unless File.dir?("#{@srcdir}/#{rel}")
1512
+
1513
+ dir = File.basename(rel)
1514
+ Dir.mkdir dir unless File.dir?(dir)
1515
+ prevdir = Dir.pwd
1516
+ Dir.chdir dir
1517
+ $stderr.puts '---> ' + rel if verbose?
1518
+ @currdir = rel
1519
+ yield
1520
+ Dir.chdir prevdir
1521
+ $stderr.puts '<--- ' + rel if verbose?
1522
+ @currdir = File.dirname(rel)
1523
+ end
1524
+
1525
+ def run_hook(id)
1526
+ path = [ "#{curr_srcdir()}/#{id}",
1527
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1528
+ return unless path
1529
+ begin
1530
+ instance_eval File.read(path), path, 1
1531
+ rescue
1532
+ raise if $DEBUG
1533
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1534
+ end
1535
+ end
1536
+
1537
+ end # class Installer
1538
+
1539
+
1540
+ class SetupError < StandardError; end
1541
+
1542
+ def setup_rb_error(msg)
1543
+ raise SetupError, msg
1544
+ end
1545
+
1546
+ if $0 == __FILE__
1547
+ begin
1548
+ ToplevelInstaller.invoke
1549
+ rescue SetupError
1550
+ raise if $DEBUG
1551
+ $stderr.puts $!.message
1552
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1553
+ exit 1
1554
+ end
1555
+ end