guid 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/CHANGES +2 -0
  2. data/FAQ +82 -0
  3. data/README +70 -0
  4. data/Rakefile +70 -0
  5. data/install.rb +1298 -0
  6. data/lib/guid.rb +153 -0
  7. metadata +62 -0
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ 0.1.0
2
+ - Forked uuid project - http://rubyforge.org/projects/uuid
data/FAQ ADDED
@@ -0,0 +1,82 @@
1
+ What is GUID/UUID?
2
+ ==================
3
+
4
+ A GUID (Globally Unique Identifier) or UUID (Universally Unique Identifer)
5
+ is a 128-bit value that is supposed to be unique across time and space
6
+ dimension. This means that if you create a GUID, it will be different from
7
+ another GUID previously created, or from GUID created by someone else on a
8
+ different computer, even at the same time.
9
+
10
+ A GUID is usually represented by hexadecimal notation like this:
11
+
12
+ {24cb2bdc-e604-809f-fb74-077c201e009e}
13
+ 24cb2bdc-e604-809f-fb74-077c201e009e
14
+ 24cb2bdce604809ffb74077c201e009e
15
+
16
+ There are various algorithms to generate GUID, including one that uses MAC
17
+ address and current timestamp, and one that uses random number. The Ruby
18
+ Guid library uses the latter.
19
+
20
+
21
+ What is the difference between GUID and UUID?
22
+ =============================================
23
+
24
+ None except the name. GUID is how Microsoft calls UUID, and it seems to be
25
+ the more popular one because Microsoft uses GUID/UUID extensively.
26
+
27
+
28
+ What is GUID good for?
29
+ ======================
30
+
31
+ It is primarily used to generate primary key for database tables, or for
32
+ some sort of ID that needs to be globally unique (like in P2P applications
33
+ or in distributed computing).
34
+
35
+ What makes GUID especially useful is that you can create it autonomously. If
36
+ instead of GUID you use a two-part ID (local ID + node ID), or a
37
+ hierarchical ID (like DNS or public IP address), then you need to contact
38
+ another node or some sort of numbering authority.
39
+
40
+
41
+ Is GUID really globally unique?
42
+ ===============================
43
+
44
+ Yes, you can say it's statistically unique, meaning that the chance of
45
+ duplicate GUIDs are very, very, very small. The chance of generating two
46
+ same GUIDs consecutively are 2^-128 (2.9e-39). Even If your table contains a
47
+ billion rows, the chance of creating a GUID that collides with an existing
48
+ primary key is 2.9e-30. To appreciate the scale, the chance of GUID
49
+ collision is:
50
+
51
+ * about the same as 16 random people that you put into a room all having the
52
+ same birthday (assuming the birthday distribution is uniform);
53
+ * smaller than your computer parts (harddisk, RAM) failing;
54
+ * smaller than you winning a multistate lottery;
55
+ * smaller than you winning a multistate lottery twice!
56
+ * smaller than you being struck by a lightning multiple times!
57
+
58
+ So for most practical purposes, you can assume it's unique. If you are still
59
+ not convinced, try generating 1 million (or 100 millions) of GUIDs using the
60
+ Ruby Guid library and try finding a duplicate. If you do find one, consider
61
+ yourself very, very, very lucky (or unlucky :-).
62
+
63
+ But actually, GUID is not mathematically unique. Meaning the probability of
64
+ duplication is not exactly zero, just a very small number.
65
+
66
+
67
+ But isn't GUID produced using MAC address really unique?
68
+ ========================================================
69
+
70
+ Not really. MAC addresses have been known to be not unique. Many cards also
71
+ allow their MAC addresses to be set. This, along with the problem of clock
72
+ rewinding, is probably one of the reasons why Microsoft switched their GUID
73
+ algorithm to using random number instead. (Another reason is the complaint
74
+ that using MAC address raises privacy concern).
75
+
76
+
77
+ Why do you use /dev/urandom instead of /dev/random?
78
+ ===================================================
79
+
80
+ Because /dev/random blocks.
81
+
82
+
data/README ADDED
@@ -0,0 +1,70 @@
1
+ NAME
2
+
3
+ Guid - Produce GUID/UUID from Ruby
4
+
5
+
6
+ SYNOPSIS
7
+
8
+ require 'guid'
9
+
10
+ # generate a GUID
11
+ g = Guid.new
12
+ puts g # 79328095-636a-6cc5-2bbb-606df7228a01
13
+ puts g.to_s # 79328095-636a-6cc5-2bbb-606df7228a01
14
+ puts g.hexdigest # 79328095636a6cc52bbb606df7228a01
15
+ puts g.raw.inspect # "\227#\010Y6\246\306\\\262\273\006\326\177\"\250\020"
16
+ puts g.raw.length # 16
17
+
18
+ # generate another GUID, should be different everytime
19
+ g2 = Guid.new
20
+ puts g == g2 # false (it better be! :-)
21
+
22
+ # convert a hexstring into Guid object
23
+ g3 = Guid.from_s("79328095-636a-6cc5-2bbb-606df7228a01")
24
+ puts g3 # 79328095-636a-6cc5-2bbb-606df7228a01
25
+ puts g == g3 # true
26
+
27
+ # convert a raw 16-byte string into Guid object
28
+ g4 = Guid.from_raw("\227#\010Y6\246\306\\\262\273\006\326\177\"\250\020")
29
+ puts g4 # 79328095-636a-6cc5-2bbb-606df7228a01
30
+ puts g == g4 # true
31
+
32
+
33
+ DESCRIPTION
34
+
35
+ This library can produce GUID/UUID on Windows (except first release of
36
+ Win95 and older version) and on Unix using random number. I have only
37
+ tested this library under Win2k and Redhat 7.3; please report if you fail
38
+ to use it on other platforms. On Windows, it uses CryptGenRandom(). On
39
+ Unix, it uses /dev/urandom (and if fail, try to use /dev/random). No other
40
+ external program or library is needed.
41
+
42
+ The latest version of this library can be obtained from:
43
+
44
+ http://rubyforge.org/projects/uuid/
45
+
46
+ To install:
47
+
48
+ % ruby install.rb config
49
+ % ruby install.rb setup
50
+ # ruby install.rb install
51
+
52
+
53
+ SEE ALSO
54
+
55
+ * FAQ
56
+
57
+
58
+ CREDITS
59
+
60
+ * Thanks to Stephen Veit <sveit at earthlink net> for pointing out how to
61
+ use Win32API.
62
+
63
+
64
+ LICENSE
65
+
66
+ Copyright (c) 2004 David Garamond <davegaramond at icqmail com>.
67
+
68
+ This library is free software; you can redistribute it and/or modify it
69
+ under the same terms as Ruby itself.
70
+
@@ -0,0 +1,70 @@
1
+ require "rake"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/contrib/rubyforgepublisher'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc "Runs the Rspec suite"
9
+ task(:default) do
10
+ run_suite
11
+ end
12
+
13
+ desc "Runs the Rspec suite"
14
+ task(:spec) do
15
+ run_suite
16
+ end
17
+
18
+ desc "Copies the trunk to a tag with the name of the current release"
19
+ task(:tag_release) do
20
+ tag_release
21
+ end
22
+
23
+ def run_suite
24
+ dir = File.dirname(__FILE__)
25
+ system("ruby #{dir}/spec/spec_suite.rb") || raise("Example Suite failed")
26
+ end
27
+
28
+ PKG_NAME = "guid"
29
+ PKG_VERSION = "0.1.0"
30
+ PKG_FILES = FileList[
31
+ '[A-Z]*',
32
+ '*.rb',
33
+ 'lib/**/*.rb',
34
+ 'core/**',
35
+ 'bin/**',
36
+ 'spec/**/*.rb'
37
+ ]
38
+
39
+ spec = Gem::Specification.new do |s|
40
+ s.name = PKG_NAME
41
+ s.version = PKG_VERSION
42
+ s.summary = "Guid is a Ruby library for portable GUID/UUID generation. At the moment it can be used on Windows (except first release of Win95 and older) and on Unix. This is a fork of David Garamond's ruby-uuid library."
43
+ s.test_files = "spec/spec_suite.rb"
44
+ s.description = s.summary
45
+
46
+ s.files = PKG_FILES.to_a
47
+ s.require_path = 'lib'
48
+
49
+ s.has_rdoc = true
50
+ s.extra_rdoc_files = [ "README", "CHANGES" ]
51
+ s.rdoc_options = ["--main", "README", "--inline-source", "--line-numbers"]
52
+
53
+ s.test_files = Dir.glob('spec/*_spec.rb')
54
+ s.require_path = 'lib'
55
+ s.author = "Brian Takita, Ross Hale, & David Garamond"
56
+ s.email = "opensource@pivotallabs.com"
57
+ s.homepage = "http://pivotallabs.com"
58
+ s.rubyforge_project = "pivotalrb"
59
+ end
60
+
61
+ Rake::GemPackageTask.new(spec) do |pkg|
62
+ pkg.need_zip = true
63
+ pkg.need_tar = true
64
+ end
65
+
66
+ def tag_release
67
+ dashed_version = PKG_VERSION.gsub('.', '-')
68
+ svn_user = "#{ENV["SVN_USER"]}@" || ""
69
+ `svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/guid/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/guid/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
70
+ end
@@ -0,0 +1,1298 @@
1
+ #
2
+ # This file is automatically generated. DO NOT MODIFY!
3
+ #
4
+ # setup.rb
5
+ #
6
+ # Copyright (c) 2000-2003 Minero Aoki <aamine@loveruby.net>
7
+ #
8
+ # This program is free software.
9
+ # You can distribute/modify this program under the terms of
10
+ # the GNU Lesser General Public License version 2.
11
+ #
12
+
13
+ def multipackage_install?
14
+ FileTest.directory?(File.dirname($0) + '/packages')
15
+ end
16
+
17
+ #
18
+ # compat.rb
19
+ #
20
+
21
+ module Enumerable
22
+ methods = instance_methods(true)
23
+
24
+ unless methods.include?('map')
25
+ alias map collect
26
+ end
27
+
28
+ unless methods.include?('select')
29
+ alias select find_all
30
+ end
31
+
32
+ unless methods.include?('reject')
33
+ def reject
34
+ result = []
35
+ each do |i|
36
+ result.push i unless yield(i)
37
+ end
38
+ result
39
+ end
40
+ end
41
+
42
+ unless methods.include?('inject')
43
+ def inject( result )
44
+ each do |i|
45
+ result = yield(result, i)
46
+ end
47
+ result
48
+ end
49
+ end
50
+
51
+ unless methods.include?('any?')
52
+ def any?
53
+ each do |i|
54
+ return true if yield(i)
55
+ end
56
+ false
57
+ end
58
+ end
59
+ end
60
+ #
61
+ # fileop.rb
62
+ #
63
+
64
+ module FileOperations
65
+
66
+ def mkdir_p( dirname, prefix = nil )
67
+ dirname = prefix + dirname if prefix
68
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
69
+ return if no_harm?
70
+
71
+ # does not check '/'... it's too abnormal case
72
+ dirs = dirname.split(%r<(?=/)>)
73
+ if /\A[a-z]:\z/i === dirs[0]
74
+ disk = dirs.shift
75
+ dirs[0] = disk + dirs[0]
76
+ end
77
+ dirs.each_index do |idx|
78
+ path = dirs[0..idx].join('')
79
+ Dir.mkdir path unless dir?(path)
80
+ end
81
+ end
82
+
83
+ def rm_f( fname )
84
+ $stderr.puts "rm -f #{fname}" if verbose?
85
+ return if no_harm?
86
+
87
+ if FileTest.exist?(fname) or FileTest.symlink?(fname)
88
+ File.chmod 0777, fname
89
+ File.unlink fname
90
+ end
91
+ end
92
+
93
+ def rm_rf( dn )
94
+ $stderr.puts "rm -rf #{dn}" if verbose?
95
+ return if no_harm?
96
+
97
+ Dir.chdir dn
98
+ Dir.foreach('.') do |fn|
99
+ next if fn == '.'
100
+ next if fn == '..'
101
+ if dir?(fn)
102
+ verbose_off {
103
+ rm_rf fn
104
+ }
105
+ else
106
+ verbose_off {
107
+ rm_f fn
108
+ }
109
+ end
110
+ end
111
+ Dir.chdir '..'
112
+ Dir.rmdir dn
113
+ end
114
+
115
+ def move_file( src, dest )
116
+ File.unlink dest if FileTest.exist?(dest)
117
+ begin
118
+ File.rename src, dest
119
+ rescue
120
+ File.open(dest, 'wb') {|f| f.write read_file(src) }
121
+ File.chmod File.stat(src).mode, dest
122
+ File.unlink src
123
+ end
124
+ end
125
+
126
+ def install( from, dest, mode, prefix = nil )
127
+ $stderr.puts "install #{from} #{dest}" if verbose?
128
+ return if no_harm?
129
+
130
+ realdest = prefix + dest if prefix
131
+ realdest += '/' + File.basename(from) if dir?(realdest)
132
+ str = read_file(from)
133
+ if diff?(str, realdest)
134
+ verbose_off {
135
+ rm_f realdest if File.exist?(realdest)
136
+ }
137
+ File.open(realdest, 'wb') {|f| f.write str }
138
+ File.chmod mode, realdest
139
+
140
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| f.puts realdest }
141
+ end
142
+ end
143
+
144
+ def diff?( orig, targ )
145
+ return true unless File.exist?(targ)
146
+ orig != read_file(targ)
147
+ end
148
+
149
+ def command( str )
150
+ $stderr.puts str if verbose?
151
+ system str or raise RuntimeError, "'system #{str}' failed"
152
+ end
153
+
154
+ def ruby( str )
155
+ command config('ruby-prog') + ' ' + str
156
+ end
157
+
158
+ def make( task = '' )
159
+ command config('make-prog') + ' ' + task
160
+ end
161
+
162
+ def extdir?( dir )
163
+ File.exist?(dir + '/MANIFEST')
164
+ end
165
+
166
+ def dir?( path )
167
+ # for corrupted windows stat()
168
+ FileTest.directory?((path[-1,1] == '/') ? path : path + '/')
169
+ end
170
+
171
+ def all_files_in( dirname )
172
+ Dir.open(dirname) {|d|
173
+ return d.select {|ent| FileTest.file?("#{dirname}/#{ent}") }
174
+ }
175
+ end
176
+
177
+ REJECT_DIRS = %w(
178
+ CVS SCCS RCS CVS.adm
179
+ )
180
+
181
+ def all_dirs_in( dirname )
182
+ Dir.open(dirname) {|d|
183
+ return d.select {|n| dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
184
+ }
185
+ end
186
+
187
+ def read_file( fname )
188
+ File.open(fname, 'rb') {|f| return f.read }
189
+ end
190
+
191
+ end
192
+ #
193
+ # config.rb
194
+ #
195
+
196
+ if i = ARGV.index(/\A--rbconfig=/)
197
+ file = $'
198
+ ARGV.delete_at(i)
199
+ require file
200
+ else
201
+ require 'rbconfig'
202
+ end
203
+
204
+
205
+ class ConfigTable
206
+
207
+ c = ::Config::CONFIG
208
+
209
+ rubypath = c['bindir'] + '/' + c['ruby_install_name']
210
+
211
+ major = c['MAJOR'].to_i
212
+ minor = c['MINOR'].to_i
213
+ teeny = c['TEENY'].to_i
214
+ version = "#{major}.#{minor}"
215
+
216
+ # ruby ver. >= 1.4.4?
217
+ newpath_p = ((major >= 2) or
218
+ ((major == 1) and
219
+ ((minor >= 5) or
220
+ ((minor == 4) and (teeny >= 4)))))
221
+
222
+ re = Regexp.new('\A' + Regexp.quote(c['prefix']))
223
+ subprefix = lambda {|path|
224
+ re === path and path.sub(re, '$prefix')
225
+ }
226
+
227
+ if c['rubylibdir']
228
+ # V < 1.6.3
229
+ stdruby = subprefix.call(c['rubylibdir'])
230
+ siteruby = subprefix.call(c['sitedir'])
231
+ versite = subprefix.call(c['sitelibdir'])
232
+ sodir = subprefix.call(c['sitearchdir'])
233
+ elsif newpath_p
234
+ # 1.4.4 <= V <= 1.6.3
235
+ stdruby = "$prefix/lib/ruby/#{version}"
236
+ siteruby = subprefix.call(c['sitedir'])
237
+ versite = siteruby + '/' + version
238
+ sodir = "$site-ruby/#{c['arch']}"
239
+ else
240
+ # V < 1.4.4
241
+ stdruby = "$prefix/lib/ruby/#{version}"
242
+ siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
243
+ versite = siteruby
244
+ sodir = "$site-ruby/#{c['arch']}"
245
+ end
246
+
247
+ common_descripters = [
248
+ [ 'prefix', [ c['prefix'],
249
+ 'path',
250
+ 'path prefix of target environment' ] ],
251
+ [ 'std-ruby', [ stdruby,
252
+ 'path',
253
+ 'the directory for standard ruby libraries' ] ],
254
+ [ 'site-ruby-common', [ siteruby,
255
+ 'path',
256
+ 'the directory for version-independent non-standard ruby libraries' ] ],
257
+ [ 'site-ruby', [ versite,
258
+ 'path',
259
+ 'the directory for non-standard ruby libraries' ] ],
260
+ [ 'bin-dir', [ '$prefix/bin',
261
+ 'path',
262
+ 'the directory for commands' ] ],
263
+ [ 'rb-dir', [ '$site-ruby',
264
+ 'path',
265
+ 'the directory for ruby scripts' ] ],
266
+ [ 'so-dir', [ sodir,
267
+ 'path',
268
+ 'the directory for ruby extentions' ] ],
269
+ [ 'data-dir', [ '$prefix/share',
270
+ 'path',
271
+ 'the directory for shared data' ] ],
272
+ [ 'ruby-path', [ rubypath,
273
+ 'path',
274
+ 'path to set to #! line' ] ],
275
+ [ 'ruby-prog', [ rubypath,
276
+ 'name',
277
+ 'the ruby program using for installation' ] ],
278
+ [ 'make-prog', [ 'make',
279
+ 'name',
280
+ 'the make program to compile ruby extentions' ] ],
281
+ [ 'without-ext', [ 'no',
282
+ 'yes/no',
283
+ 'does not compile/install ruby extentions' ] ]
284
+ ]
285
+ multipackage_descripters = [
286
+ [ 'with', [ '',
287
+ 'name,name...',
288
+ 'package names that you want to install',
289
+ 'ALL' ] ],
290
+ [ 'without', [ '',
291
+ 'name,name...',
292
+ 'package names that you do not want to install',
293
+ 'NONE' ] ]
294
+ ]
295
+ if multipackage_install?
296
+ DESCRIPTER = common_descripters + multipackage_descripters
297
+ else
298
+ DESCRIPTER = common_descripters
299
+ end
300
+
301
+ SAVE_FILE = 'config.save'
302
+
303
+ def ConfigTable.each_name( &block )
304
+ keys().each(&block)
305
+ end
306
+
307
+ def ConfigTable.keys
308
+ DESCRIPTER.map {|name, *dummy| name }
309
+ end
310
+
311
+ def ConfigTable.each_definition( &block )
312
+ DESCRIPTER.each(&block)
313
+ end
314
+
315
+ def ConfigTable.get_entry( name )
316
+ name, ent = DESCRIPTER.assoc(name)
317
+ ent
318
+ end
319
+
320
+ def ConfigTable.get_entry!( name )
321
+ get_entry(name) or raise ArgumentError, "no such config: #{name}"
322
+ end
323
+
324
+ def ConfigTable.add_entry( name, vals )
325
+ ConfigTable::DESCRIPTER.push [name,vals]
326
+ end
327
+
328
+ def ConfigTable.remove_entry( name )
329
+ get_entry(name) or raise ArgumentError, "no such config: #{name}"
330
+ DESCRIPTER.delete_if {|n, arr| n == name }
331
+ end
332
+
333
+ def ConfigTable.config_key?( name )
334
+ get_entry(name) ? true : false
335
+ end
336
+
337
+ def ConfigTable.bool_config?( name )
338
+ ent = get_entry(name) or return false
339
+ ent[1] == 'yes/no'
340
+ end
341
+
342
+ def ConfigTable.value_config?( name )
343
+ ent = get_entry(name) or return false
344
+ ent[1] != 'yes/no'
345
+ end
346
+
347
+ def ConfigTable.path_config?( name )
348
+ ent = get_entry(name) or return false
349
+ ent[1] == 'path'
350
+ end
351
+
352
+
353
+ class << self
354
+ alias newobj new
355
+ end
356
+
357
+ def ConfigTable.new
358
+ c = newobj()
359
+ c.initialize_from_table
360
+ c
361
+ end
362
+
363
+ def ConfigTable.load
364
+ c = newobj()
365
+ c.initialize_from_file
366
+ c
367
+ end
368
+
369
+ def initialize_from_table
370
+ @table = {}
371
+ DESCRIPTER.each do |k, (default, vname, desc, default2)|
372
+ @table[k] = default
373
+ end
374
+ end
375
+
376
+ def initialize_from_file
377
+ raise InstallError, "#{File.basename $0} config first"\
378
+ unless FileTest.file?(SAVE_FILE)
379
+ @table = {}
380
+ File.foreach(SAVE_FILE) do |line|
381
+ k, v = line.split(/=/, 2)
382
+ @table[k] = v.strip
383
+ end
384
+ end
385
+
386
+ def save
387
+ File.open(SAVE_FILE, 'w') {|f|
388
+ @table.each do |k, v|
389
+ f.printf "%s=%s\n", k, v if v
390
+ end
391
+ }
392
+ end
393
+
394
+ def []=( k, v )
395
+ raise InstallError, "unknown config option #{k}"\
396
+ unless ConfigTable.config_key?(k)
397
+ if ConfigTable.path_config?(k)
398
+ @table[k] = ((v[0,1] != '$') ? File.expand_path(v) : v)
399
+ else
400
+ @table[k] = v
401
+ end
402
+ end
403
+
404
+ def []( key )
405
+ return nil unless @table[key]
406
+ @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
407
+ end
408
+
409
+ def set_raw( key, val )
410
+ @table[key] = val
411
+ end
412
+
413
+ def get_raw( key )
414
+ @table[key]
415
+ end
416
+
417
+ end
418
+
419
+
420
+ module MetaConfigAPI
421
+
422
+ def eval_file_ifexist( fname )
423
+ instance_eval read_file(fname), fname, 1 if FileTest.file?(fname)
424
+ end
425
+
426
+ def config_names
427
+ ConfigTable.keys
428
+ end
429
+
430
+ def config?( name )
431
+ ConfigTable.config_key?(name)
432
+ end
433
+
434
+ def bool_config?( name )
435
+ ConfigTable.bool_config?(name)
436
+ end
437
+
438
+ def value_config?( name )
439
+ ConfigTable.value_config?(name)
440
+ end
441
+
442
+ def path_config?( name )
443
+ ConfigTable.path_config?(name)
444
+ end
445
+
446
+ def add_config( name, argname, default, desc )
447
+ ConfigTable.add_entry name,[default,argname,desc]
448
+ end
449
+
450
+ def add_path_config( name, default, desc )
451
+ add_config name, 'path', default, desc
452
+ end
453
+
454
+ def add_bool_config( name, default, desc )
455
+ add_config name, 'yes/no', default ? 'yes' : 'no', desc
456
+ end
457
+
458
+ def set_config_default( name, default )
459
+ if bool_config?(name)
460
+ ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
461
+ else
462
+ ConfigTable.get_entry!(name)[0] = default
463
+ end
464
+ end
465
+
466
+ def remove_config( name )
467
+ ent = ConfigTable.get_entry(name)
468
+ ConfigTable.remove_entry name
469
+ ent
470
+ end
471
+
472
+ end
473
+ #
474
+ # base.rb
475
+ #
476
+
477
+ require 'rbconfig'
478
+
479
+
480
+ class InstallError < StandardError; end
481
+
482
+
483
+ module HookUtils
484
+
485
+ def run_hook( name )
486
+ try_run_hook "#{curr_srcdir()}/#{name}" or
487
+ try_run_hook "#{curr_srcdir()}/#{name}.rb"
488
+ end
489
+
490
+ def try_run_hook( fname )
491
+ return false unless FileTest.file?(fname)
492
+ begin
493
+ instance_eval read_file(fname), fname, 1
494
+ rescue
495
+ raise InstallError, "hook #{fname} failed:\n" + $!.message
496
+ end
497
+ true
498
+ end
499
+
500
+ end
501
+
502
+
503
+ module HookScriptAPI
504
+
505
+ def get_config( key )
506
+ @config[key]
507
+ end
508
+
509
+ alias config get_config
510
+
511
+ def set_config( key, val )
512
+ @config[key] = val
513
+ end
514
+
515
+ #
516
+ # srcdir/objdir (works only in the package directory)
517
+ #
518
+
519
+ #abstract srcdir_root
520
+ #abstract objdir_root
521
+ #abstract relpath
522
+
523
+ def curr_srcdir
524
+ "#{srcdir_root()}/#{relpath()}"
525
+ end
526
+
527
+ def curr_objdir
528
+ "#{objdir_root()}/#{relpath()}"
529
+ end
530
+
531
+ def srcfile( path )
532
+ "#{curr_srcdir()}/#{path}"
533
+ end
534
+
535
+ def srcexist?( path )
536
+ FileTest.exist? srcfile(path)
537
+ end
538
+
539
+ def srcdirectory?( path )
540
+ dir? srcfile(path)
541
+ end
542
+
543
+ def srcfile?( path )
544
+ FileTest.file? srcfile(path)
545
+ end
546
+
547
+ def srcentries( path = '.' )
548
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
549
+ return d.to_a - %w(. ..)
550
+ }
551
+ end
552
+
553
+ def srcfiles( path = '.' )
554
+ srcentries(path).select {|fname|
555
+ FileTest.file?(File.join(curr_srcdir(), path, fname))
556
+ }
557
+ end
558
+
559
+ def srcdirectories( path = '.' )
560
+ srcentries(path).select {|fname|
561
+ dir?(File.join(curr_srcdir(), path, fname))
562
+ }
563
+ end
564
+
565
+ end
566
+
567
+
568
+ class Installer
569
+
570
+ FILETYPES = %w( bin lib ext data )
571
+
572
+ include HookScriptAPI
573
+ include HookUtils
574
+ include FileOperations
575
+
576
+ def initialize( config, opt, srcroot, objroot )
577
+ @config = config
578
+ @options = opt
579
+ @srcdir = File.expand_path(srcroot)
580
+ @objdir = File.expand_path(objroot)
581
+ @currdir = '.'
582
+ end
583
+
584
+ def inspect
585
+ "#<#{self.class} #{File.basename(@srcdir)}>"
586
+ end
587
+
588
+ #
589
+ # Hook Script API bases
590
+ #
591
+
592
+ def srcdir_root
593
+ @srcdir
594
+ end
595
+
596
+ def objdir_root
597
+ @objdir
598
+ end
599
+
600
+ def relpath
601
+ @currdir
602
+ end
603
+
604
+ #
605
+ # configs/options
606
+ #
607
+
608
+ def no_harm?
609
+ @options['no-harm']
610
+ end
611
+
612
+ def verbose?
613
+ @options['verbose']
614
+ end
615
+
616
+ def verbose_off
617
+ begin
618
+ save, @options['verbose'] = @options['verbose'], false
619
+ yield
620
+ ensure
621
+ @options['verbose'] = save
622
+ end
623
+ end
624
+
625
+ #
626
+ # TASK config
627
+ #
628
+
629
+ def exec_config
630
+ exec_task_traverse 'config'
631
+ end
632
+
633
+ def config_dir_bin( rel )
634
+ end
635
+
636
+ def config_dir_lib( rel )
637
+ end
638
+
639
+ def config_dir_ext( rel )
640
+ extconf if extdir?(curr_srcdir())
641
+ end
642
+
643
+ def extconf
644
+ opt = @options['config-opt'].join(' ')
645
+ command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
646
+ end
647
+
648
+ def config_dir_data( rel )
649
+ end
650
+
651
+ #
652
+ # TASK setup
653
+ #
654
+
655
+ def exec_setup
656
+ exec_task_traverse 'setup'
657
+ end
658
+
659
+ def setup_dir_bin( rel )
660
+ all_files_in(curr_srcdir()).each do |fname|
661
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
662
+ end
663
+ end
664
+
665
+ # modify: #!/usr/bin/ruby
666
+ # modify: #! /usr/bin/ruby
667
+ # modify: #!ruby
668
+ # not modify: #!/usr/bin/env ruby
669
+ SHEBANG_RE = /\A\#!\s*\S*ruby\S*/
670
+
671
+ def adjust_shebang( path )
672
+ return if no_harm?
673
+
674
+ tmpfile = File.basename(path) + '.tmp'
675
+ begin
676
+ File.open(path) {|r|
677
+ File.open(tmpfile, 'w') {|w|
678
+ first = r.gets
679
+ return unless SHEBANG_RE === first
680
+
681
+ $stderr.puts "adjusting shebang: #{File.basename path}" if verbose?
682
+ w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
683
+ w.write r.read
684
+ }
685
+ }
686
+ move_file tmpfile, File.basename(path)
687
+ ensure
688
+ File.unlink tmpfile if File.exist?(tmpfile)
689
+ end
690
+ end
691
+
692
+ def setup_dir_lib( rel )
693
+ end
694
+
695
+ def setup_dir_ext( rel )
696
+ make if extdir?(curr_srcdir())
697
+ end
698
+
699
+ def setup_dir_data( rel )
700
+ end
701
+
702
+ #
703
+ # TASK install
704
+ #
705
+
706
+ def exec_install
707
+ exec_task_traverse 'install'
708
+ end
709
+
710
+ def install_dir_bin( rel )
711
+ install_files collect_filenames_auto(), config('bin-dir') + '/' + rel, 0755
712
+ end
713
+
714
+ def install_dir_lib( rel )
715
+ install_files ruby_scripts(), config('rb-dir') + '/' + rel, 0644
716
+ end
717
+
718
+ def install_dir_ext( rel )
719
+ return unless extdir?(curr_srcdir())
720
+ install_files ruby_extentions('.'),
721
+ config('so-dir') + '/' + File.dirname(rel),
722
+ 0555
723
+ end
724
+
725
+ def install_dir_data( rel )
726
+ install_files collect_filenames_auto(), config('data-dir') + '/' + rel, 0644
727
+ end
728
+
729
+ def install_files( list, dest, mode )
730
+ mkdir_p dest, @options['install-prefix']
731
+ list.each do |fname|
732
+ install fname, dest, mode, @options['install-prefix']
733
+ end
734
+ end
735
+
736
+ def ruby_scripts
737
+ collect_filenames_auto().select {|n| /\.rb\z/ === n }
738
+ end
739
+
740
+ # picked up many entries from cvs-1.11.1/src/ignore.c
741
+ reject_patterns = %w(
742
+ core RCSLOG tags TAGS .make.state
743
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.a *.olb *.o *.obj
744
+ *.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$
745
+
746
+ *.org *.in .*
747
+ )
748
+ maptab = {
749
+ '.' => '\\.',
750
+ '$' => '\\$',
751
+ '*' => '.*'
752
+ }
753
+ REJECT_PATTERNS = Regexp.new('\A(?:' +
754
+ reject_patterns.map {|pat|
755
+ pat.gsub(/[\.\$\*]/) {|s| maptab[s] }
756
+ }.join('|') +
757
+ ')\z')
758
+
759
+ def collect_filenames_auto
760
+ mapdir((existfiles() - hookfiles()).reject {|fname|
761
+ REJECT_PATTERNS === fname
762
+ })
763
+ end
764
+
765
+ def existfiles
766
+ all_files_in(curr_srcdir()) | all_files_in('.')
767
+ end
768
+
769
+ def hookfiles
770
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
771
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
772
+ }.flatten
773
+ end
774
+
775
+ def mapdir( filelist )
776
+ filelist.map {|fname|
777
+ if File.exist?(fname) # objdir
778
+ fname
779
+ else # srcdir
780
+ File.join(curr_srcdir(), fname)
781
+ end
782
+ }
783
+ end
784
+
785
+ def ruby_extentions( dir )
786
+ _ruby_extentions(dir) or
787
+ raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
788
+ end
789
+
790
+ DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
791
+
792
+ def _ruby_extentions( dir )
793
+ Dir.open(dir) {|d|
794
+ return d.select {|fname| DLEXT === fname }
795
+ }
796
+ end
797
+
798
+ #
799
+ # TASK clean
800
+ #
801
+
802
+ def exec_clean
803
+ exec_task_traverse 'clean'
804
+ rm_f 'config.save'
805
+ rm_f 'InstalledFiles'
806
+ end
807
+
808
+ def clean_dir_bin( rel )
809
+ end
810
+
811
+ def clean_dir_lib( rel )
812
+ end
813
+
814
+ def clean_dir_ext( rel )
815
+ return unless extdir?(curr_srcdir())
816
+ make 'clean' if FileTest.file?('Makefile')
817
+ end
818
+
819
+ def clean_dir_data( rel )
820
+ end
821
+
822
+ #
823
+ # TASK distclean
824
+ #
825
+
826
+ def exec_distclean
827
+ exec_task_traverse 'distclean'
828
+ rm_f 'config.save'
829
+ rm_f 'InstalledFiles'
830
+ end
831
+
832
+ def distclean_dir_bin( rel )
833
+ end
834
+
835
+ def distclean_dir_lib( rel )
836
+ end
837
+
838
+ def distclean_dir_ext( rel )
839
+ return unless extdir?(curr_srcdir())
840
+ make 'distclean' if FileTest.file?('Makefile')
841
+ end
842
+
843
+ #
844
+ # lib
845
+ #
846
+
847
+ def exec_task_traverse( task )
848
+ run_hook 'pre-' + task
849
+ FILETYPES.each do |type|
850
+ if config('without-ext') == 'yes' and type == 'ext'
851
+ $stderr.puts 'skipping ext/* by user option' if verbose?
852
+ next
853
+ end
854
+ traverse task, type, task + '_dir_' + type
855
+ end
856
+ run_hook 'post-' + task
857
+ end
858
+
859
+ def traverse( task, rel, mid )
860
+ dive_into(rel) {
861
+ run_hook 'pre-' + task
862
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
863
+ all_dirs_in(curr_srcdir()).each do |d|
864
+ traverse task, rel + '/' + d, mid
865
+ end
866
+ run_hook 'post-' + task
867
+ }
868
+ end
869
+
870
+ def dive_into( rel )
871
+ return unless dir?("#{@srcdir}/#{rel}")
872
+
873
+ dir = File.basename(rel)
874
+ Dir.mkdir dir unless dir?(dir)
875
+ prevdir = Dir.pwd
876
+ Dir.chdir dir
877
+ $stderr.puts '---> ' + rel if verbose?
878
+ @currdir = rel
879
+ yield
880
+ Dir.chdir prevdir
881
+ $stderr.puts '<--- ' + rel if verbose?
882
+ @currdir = File.dirname(rel)
883
+ end
884
+
885
+ end
886
+ #
887
+ # toplevel.rb
888
+ #
889
+
890
+ class ToplevelInstaller
891
+
892
+ Version = '3.2.1'
893
+ Copyright = 'Copyright (c) 2000-2003 Minero Aoki'
894
+
895
+ TASKS = [
896
+ [ 'config', 'saves your configurations' ],
897
+ [ 'show', 'shows current configuration' ],
898
+ [ 'setup', 'compiles ruby extentions and others' ],
899
+ [ 'install', 'installs files' ],
900
+ [ 'clean', "does `make clean' for each extention" ],
901
+ [ 'distclean',"does `make distclean' for each extention" ]
902
+ ]
903
+
904
+ def ToplevelInstaller.invoke
905
+ instance().invoke
906
+ end
907
+
908
+ @singleton = nil
909
+
910
+ def ToplevelInstaller.instance
911
+ @singleton ||= new(File.dirname($0))
912
+ @singleton
913
+ end
914
+
915
+ include MetaConfigAPI
916
+
917
+ def initialize( ardir_root )
918
+ @config = nil
919
+ @options = { 'verbose' => true }
920
+ @ardir = File.expand_path(ardir_root)
921
+ end
922
+
923
+ def inspect
924
+ "#<#{self.class} #{__id__()}>"
925
+ end
926
+
927
+ def invoke
928
+ run_metaconfigs
929
+ task = parsearg_global()
930
+ @config = load_config(task)
931
+ __send__ "parsearg_#{task}"
932
+ init_installers
933
+ __send__ "exec_#{task}"
934
+ end
935
+
936
+ def run_metaconfigs
937
+ eval_file_ifexist "#{@ardir}/metaconfig"
938
+ end
939
+
940
+ def load_config( task )
941
+ case task
942
+ when 'config'
943
+ ConfigTable.new
944
+ when 'clean', 'distclean'
945
+ if File.exist?('config.save')
946
+ then ConfigTable.load
947
+ else ConfigTable.new
948
+ end
949
+ else
950
+ ConfigTable.load
951
+ end
952
+ end
953
+
954
+ def init_installers
955
+ @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
956
+ end
957
+
958
+ #
959
+ # Hook Script API bases
960
+ #
961
+
962
+ def srcdir_root
963
+ @ardir
964
+ end
965
+
966
+ def objdir_root
967
+ '.'
968
+ end
969
+
970
+ def relpath
971
+ '.'
972
+ end
973
+
974
+ #
975
+ # Option Parsing
976
+ #
977
+
978
+ def parsearg_global
979
+ valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
980
+
981
+ while arg = ARGV.shift
982
+ case arg
983
+ when /\A\w+\z/
984
+ raise InstallError, "invalid task: #{arg}" unless valid_task === arg
985
+ return arg
986
+
987
+ when '-q', '--quiet'
988
+ @options['verbose'] = false
989
+
990
+ when '--verbose'
991
+ @options['verbose'] = true
992
+
993
+ when '-h', '--help'
994
+ print_usage $stdout
995
+ exit 0
996
+
997
+ when '-v', '--version'
998
+ puts "#{File.basename($0)} version #{Version}"
999
+ exit 0
1000
+
1001
+ when '--copyright'
1002
+ puts Copyright
1003
+ exit 0
1004
+
1005
+ else
1006
+ raise InstallError, "unknown global option '#{arg}'"
1007
+ end
1008
+ end
1009
+
1010
+ raise InstallError, <<EOS
1011
+ No task or global option given.
1012
+ Typical installation procedure is:
1013
+ $ ruby #{File.basename($0)} config
1014
+ $ ruby #{File.basename($0)} setup
1015
+ # ruby #{File.basename($0)} install (may require root privilege)
1016
+ EOS
1017
+ end
1018
+
1019
+
1020
+ def parsearg_no_options
1021
+ raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
1022
+ unless ARGV.empty?
1023
+ end
1024
+
1025
+ alias parsearg_show parsearg_no_options
1026
+ alias parsearg_setup parsearg_no_options
1027
+ alias parsearg_clean parsearg_no_options
1028
+ alias parsearg_distclean parsearg_no_options
1029
+
1030
+ def parsearg_config
1031
+ re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
1032
+ @options['config-opt'] = []
1033
+
1034
+ while i = ARGV.shift
1035
+ if /\A--?\z/ === i
1036
+ @options['config-opt'] = ARGV.dup
1037
+ break
1038
+ end
1039
+ m = re.match(i) or raise InstallError, "config: unknown option #{i}"
1040
+ name, value = m.to_a[1,2]
1041
+ if value
1042
+ if ConfigTable.bool_config?(name)
1043
+ raise InstallError, "config: --#{name} allows only yes/no for argument"\
1044
+ unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i === value
1045
+ value = (/\Ay(es)?|\At(rue)/i === value) ? 'yes' : 'no'
1046
+ end
1047
+ else
1048
+ raise InstallError, "config: --#{name} requires argument"\
1049
+ unless ConfigTable.bool_config?(name)
1050
+ value = 'yes'
1051
+ end
1052
+ @config[name] = value
1053
+ end
1054
+ end
1055
+
1056
+ def parsearg_install
1057
+ @options['no-harm'] = false
1058
+ @options['install-prefix'] = ''
1059
+ while a = ARGV.shift
1060
+ case a
1061
+ when /\A--no-harm\z/
1062
+ @options['no-harm'] = true
1063
+ when /\A--prefix=(.*)\z/
1064
+ path = $1
1065
+ path = File.expand_path(path) unless path[0,1] == '/'
1066
+ @options['install-prefix'] = path
1067
+ else
1068
+ raise InstallError, "install: unknown option #{a}"
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ def print_usage( out )
1074
+ out.puts 'Typical Installation Procedure:'
1075
+ out.puts " $ ruby #{File.basename $0} config"
1076
+ out.puts " $ ruby #{File.basename $0} setup"
1077
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
1078
+ out.puts
1079
+ out.puts 'Detailed Usage:'
1080
+ out.puts " ruby #{File.basename $0} <global option>"
1081
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
1082
+
1083
+ fmt = " %-20s %s\n"
1084
+ out.puts
1085
+ out.puts 'Global options:'
1086
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
1087
+ out.printf fmt, ' --verbose', 'output messages verbosely'
1088
+ out.printf fmt, '-h,--help', 'print this message'
1089
+ out.printf fmt, '-v,--version', 'print version and quit'
1090
+ out.printf fmt, ' --copyright', 'print copyright and quit'
1091
+
1092
+ out.puts
1093
+ out.puts 'Tasks:'
1094
+ TASKS.each do |name, desc|
1095
+ out.printf " %-10s %s\n", name, desc
1096
+ end
1097
+
1098
+ out.puts
1099
+ out.puts 'Options for config:'
1100
+ ConfigTable.each_definition do |name, (default, arg, desc, default2)|
1101
+ out.printf " %-20s %s [%s]\n",
1102
+ '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
1103
+ desc,
1104
+ default2 || default
1105
+ end
1106
+ out.printf " %-20s %s [%s]\n",
1107
+ '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
1108
+
1109
+ out.puts
1110
+ out.puts 'Options for install:'
1111
+ out.printf " %-20s %s [%s]\n",
1112
+ '--no-harm', 'only display what to do if given', 'off'
1113
+ out.printf " %-20s %s [%s]\n",
1114
+ '--prefix', 'install path prefix', '$prefix'
1115
+
1116
+ out.puts
1117
+ end
1118
+
1119
+ #
1120
+ # Task Handlers
1121
+ #
1122
+
1123
+ def exec_config
1124
+ @config.save
1125
+ @installer.exec_config
1126
+ end
1127
+
1128
+ def exec_setup
1129
+ @installer.exec_setup
1130
+ end
1131
+
1132
+ def exec_install
1133
+ @installer.exec_install
1134
+ end
1135
+
1136
+ def exec_show
1137
+ ConfigTable.each_name do |k|
1138
+ v = @config.get_raw(k)
1139
+ if not v or v.empty?
1140
+ v = '(not specified)'
1141
+ end
1142
+ printf "%-10s %s\n", k, v
1143
+ end
1144
+ end
1145
+
1146
+ def exec_clean
1147
+ @installer.exec_clean
1148
+ end
1149
+
1150
+ def exec_distclean
1151
+ @installer.exec_distclean
1152
+ end
1153
+
1154
+ end
1155
+
1156
+
1157
+ class ToplevelInstallerMulti < ToplevelInstaller
1158
+
1159
+ include HookUtils
1160
+ include HookScriptAPI
1161
+ include FileOperations
1162
+
1163
+ def initialize( ardir )
1164
+ super
1165
+ @packages = all_dirs_in("#{@ardir}/packages")
1166
+ raise 'no package exists' if @packages.empty?
1167
+ end
1168
+
1169
+ def run_metaconfigs
1170
+ eval_file_ifexist "#{@ardir}/metaconfig"
1171
+ @packages.each do |name|
1172
+ eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
1173
+ end
1174
+ end
1175
+
1176
+ def init_installers
1177
+ @installers = {}
1178
+ @packages.each do |pack|
1179
+ @installers[pack] = Installer.new(@config, @options,
1180
+ "#{@ardir}/packages/#{pack}",
1181
+ "packages/#{pack}")
1182
+ end
1183
+
1184
+ with = extract_selection(config('with'))
1185
+ without = extract_selection(config('without'))
1186
+ @selected = @installers.keys.select {|name|
1187
+ (with.empty? or with.include?(name)) \
1188
+ and not without.include?(name)
1189
+ }
1190
+ end
1191
+
1192
+ def extract_selection( list )
1193
+ a = list.split(/,/)
1194
+ a.each do |name|
1195
+ raise InstallError, "no such package: #{name}" \
1196
+ unless @installers.key?(name)
1197
+ end
1198
+ a
1199
+ end
1200
+
1201
+ def print_usage( f )
1202
+ super
1203
+ f.puts 'Inluded packages:'
1204
+ f.puts ' ' + @packages.sort.join(' ')
1205
+ f.puts
1206
+ end
1207
+
1208
+ #
1209
+ # multi-package metaconfig API
1210
+ #
1211
+
1212
+ attr_reader :packages
1213
+
1214
+ def declare_packages( list )
1215
+ raise 'package list is empty' if list.empty?
1216
+ list.each do |name|
1217
+ raise "directory packages/#{name} does not exist"\
1218
+ unless dir?("#{@ardir}/packages/#{name}")
1219
+ end
1220
+ @packages = list
1221
+ end
1222
+
1223
+ #
1224
+ # Task Handlers
1225
+ #
1226
+
1227
+ def exec_config
1228
+ run_hook 'pre-config'
1229
+ @config.save
1230
+ each_selected_installers {|inst| inst.exec_config }
1231
+ run_hook 'post-config'
1232
+ end
1233
+
1234
+ def exec_setup
1235
+ run_hook 'pre-setup'
1236
+ each_selected_installers {|inst| inst.exec_setup }
1237
+ run_hook 'post-setup'
1238
+ end
1239
+
1240
+ def exec_install
1241
+ run_hook 'pre-install'
1242
+ each_selected_installers {|inst| inst.exec_install }
1243
+ run_hook 'post-install'
1244
+ end
1245
+
1246
+ def exec_clean
1247
+ rm_f 'config.save'
1248
+ run_hook 'pre-clean'
1249
+ each_selected_installers {|inst| inst.exec_clean }
1250
+ run_hook 'post-clean'
1251
+ end
1252
+
1253
+ def exec_distclean
1254
+ rm_f 'config.save'
1255
+ run_hook 'pre-distclean'
1256
+ each_selected_installers {|inst| inst.exec_distclean }
1257
+ run_hook 'post-distclean'
1258
+ end
1259
+
1260
+ #
1261
+ # lib
1262
+ #
1263
+
1264
+ def each_selected_installers
1265
+ Dir.mkdir 'packages' unless dir?('packages')
1266
+ @selected.each do |pack|
1267
+ $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1268
+ Dir.mkdir "packages/#{pack}" unless dir?("packages/#{pack}")
1269
+ Dir.chdir "packages/#{pack}"
1270
+ yield @installers[pack]
1271
+ Dir.chdir '../..'
1272
+ end
1273
+ end
1274
+
1275
+ def verbose?
1276
+ @options['verbose']
1277
+ end
1278
+
1279
+ def no_harm?
1280
+ @options['no-harm']
1281
+ end
1282
+
1283
+ end
1284
+
1285
+ if $0 == __FILE__
1286
+ begin
1287
+ if multipackage_install?
1288
+ ToplevelInstallerMulti.invoke
1289
+ else
1290
+ ToplevelInstaller.invoke
1291
+ end
1292
+ rescue
1293
+ raise if $DEBUG
1294
+ $stderr.puts $!.message
1295
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1296
+ exit 1
1297
+ end
1298
+ end