unicorn-rupcio 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +144 -0
  8. data/.olddoc.yml +25 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +39 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +99 -0
  13. data/Documentation/.gitignore +3 -0
  14. data/Documentation/unicorn.1 +222 -0
  15. data/Documentation/unicorn_rails.1 +207 -0
  16. data/FAQ +70 -0
  17. data/GIT-VERSION-FILE +1 -0
  18. data/GIT-VERSION-GEN +39 -0
  19. data/GNUmakefile +318 -0
  20. data/HACKING +117 -0
  21. data/ISSUES +102 -0
  22. data/KNOWN_ISSUES +79 -0
  23. data/LICENSE +67 -0
  24. data/Links +58 -0
  25. data/PHILOSOPHY +139 -0
  26. data/README +165 -0
  27. data/Rakefile +17 -0
  28. data/SIGNALS +123 -0
  29. data/Sandbox +104 -0
  30. data/TODO +1 -0
  31. data/TUNING +119 -0
  32. data/archive/.gitignore +3 -0
  33. data/archive/slrnpull.conf +4 -0
  34. data/bin/unicorn +129 -0
  35. data/bin/unicorn_rails +210 -0
  36. data/examples/big_app_gc.rb +3 -0
  37. data/examples/echo.ru +27 -0
  38. data/examples/init.sh +102 -0
  39. data/examples/logger_mp_safe.rb +26 -0
  40. data/examples/logrotate.conf +44 -0
  41. data/examples/nginx.conf +156 -0
  42. data/examples/unicorn.conf.minimal.rb +14 -0
  43. data/examples/unicorn.conf.rb +111 -0
  44. data/examples/unicorn.socket +11 -0
  45. data/examples/unicorn@.service +40 -0
  46. data/ext/unicorn_http/CFLAGS +13 -0
  47. data/ext/unicorn_http/c_util.h +115 -0
  48. data/ext/unicorn_http/common_field_optimization.h +128 -0
  49. data/ext/unicorn_http/epollexclusive.h +128 -0
  50. data/ext/unicorn_http/ext_help.h +38 -0
  51. data/ext/unicorn_http/extconf.rb +40 -0
  52. data/ext/unicorn_http/global_variables.h +97 -0
  53. data/ext/unicorn_http/httpdate.c +91 -0
  54. data/ext/unicorn_http/unicorn_http.c +4348 -0
  55. data/ext/unicorn_http/unicorn_http.rl +1054 -0
  56. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  57. data/lib/unicorn/app/old_rails/static.rb +60 -0
  58. data/lib/unicorn/app/old_rails.rb +36 -0
  59. data/lib/unicorn/cgi_wrapper.rb +148 -0
  60. data/lib/unicorn/configurator.rb +749 -0
  61. data/lib/unicorn/const.rb +22 -0
  62. data/lib/unicorn/http_request.rb +180 -0
  63. data/lib/unicorn/http_response.rb +95 -0
  64. data/lib/unicorn/http_server.rb +860 -0
  65. data/lib/unicorn/launcher.rb +63 -0
  66. data/lib/unicorn/oob_gc.rb +82 -0
  67. data/lib/unicorn/preread_input.rb +34 -0
  68. data/lib/unicorn/select_waiter.rb +7 -0
  69. data/lib/unicorn/socket_helper.rb +186 -0
  70. data/lib/unicorn/stream_input.rb +152 -0
  71. data/lib/unicorn/tee_input.rb +132 -0
  72. data/lib/unicorn/tmpio.rb +34 -0
  73. data/lib/unicorn/util.rb +91 -0
  74. data/lib/unicorn/version.rb +1 -0
  75. data/lib/unicorn/worker.rb +166 -0
  76. data/lib/unicorn.rb +137 -0
  77. data/man/man1/unicorn.1 +222 -0
  78. data/man/man1/unicorn_rails.1 +207 -0
  79. data/setup.rb +1587 -0
  80. data/t/.gitignore +4 -0
  81. data/t/GNUmakefile +5 -0
  82. data/t/README +49 -0
  83. data/t/active-unix-socket.t +110 -0
  84. data/t/back-out-of-upgrade.t +44 -0
  85. data/t/bin/unused_listen +40 -0
  86. data/t/client_body_buffer_size.ru +15 -0
  87. data/t/client_body_buffer_size.t +79 -0
  88. data/t/detach.ru +12 -0
  89. data/t/env.ru +4 -0
  90. data/t/fails-rack-lint.ru +6 -0
  91. data/t/heartbeat-timeout.ru +13 -0
  92. data/t/heartbeat-timeout.t +60 -0
  93. data/t/integration.ru +129 -0
  94. data/t/integration.t +509 -0
  95. data/t/lib.perl +309 -0
  96. data/t/listener_names.ru +5 -0
  97. data/t/my-tap-lib.sh +201 -0
  98. data/t/oob_gc.ru +18 -0
  99. data/t/oob_gc_path.ru +18 -0
  100. data/t/pid.ru +4 -0
  101. data/t/preread_input.ru +23 -0
  102. data/t/reload-bad-config.t +49 -0
  103. data/t/reopen-logs.ru +14 -0
  104. data/t/reopen-logs.t +36 -0
  105. data/t/t0010-reap-logging.sh +55 -0
  106. data/t/t0012-reload-empty-config.sh +86 -0
  107. data/t/t0013-rewindable-input-false.sh +24 -0
  108. data/t/t0013.ru +13 -0
  109. data/t/t0014-rewindable-input-true.sh +24 -0
  110. data/t/t0014.ru +13 -0
  111. data/t/t0015-configurator-internals.sh +25 -0
  112. data/t/t0020-at_exit-handler.sh +49 -0
  113. data/t/t0021-process_detach.sh +29 -0
  114. data/t/t0022-listener_names-preload_app.sh +32 -0
  115. data/t/t0300-no-default-middleware.sh +20 -0
  116. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  117. data/t/t0301.ru +14 -0
  118. data/t/t9001-oob_gc.sh +47 -0
  119. data/t/t9002-oob_gc-path.sh +75 -0
  120. data/t/test-lib.sh +125 -0
  121. data/t/winch_ttin.t +64 -0
  122. data/t/working_directory.t +86 -0
  123. data/test/aggregate.rb +16 -0
  124. data/test/benchmark/README +60 -0
  125. data/test/benchmark/dd.ru +19 -0
  126. data/test/benchmark/ddstream.ru +51 -0
  127. data/test/benchmark/readinput.ru +41 -0
  128. data/test/benchmark/stack.ru +9 -0
  129. data/test/benchmark/uconnect.perl +66 -0
  130. data/test/exec/README +5 -0
  131. data/test/exec/test_exec.rb +1030 -0
  132. data/test/test_helper.rb +307 -0
  133. data/test/unit/test_configurator.rb +176 -0
  134. data/test/unit/test_droplet.rb +29 -0
  135. data/test/unit/test_http_parser.rb +885 -0
  136. data/test/unit/test_http_parser_ng.rb +715 -0
  137. data/test/unit/test_server.rb +245 -0
  138. data/test/unit/test_signals.rb +189 -0
  139. data/test/unit/test_socket_helper.rb +160 -0
  140. data/test/unit/test_stream_input.rb +211 -0
  141. data/test/unit/test_tee_input.rb +304 -0
  142. data/test/unit/test_util.rb +132 -0
  143. data/test/unit/test_waiter.rb +35 -0
  144. data/unicorn.gemspec +49 -0
  145. metadata +266 -0
data/setup.rb ADDED
@@ -0,0 +1,1587 @@
1
+ # -*- encoding: binary -*-
2
+ # frozen_string_literal: false
3
+ #
4
+ # setup.rb
5
+ #
6
+ # Copyright (c) 2000-2005 Minero Aoki
7
+ #
8
+ # This program is free software.
9
+ # You can distribute/modify this program under the terms of
10
+ # the GNU LGPL, Lesser General Public License version 2.1.
11
+ #
12
+
13
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
14
+ module Enumerable
15
+ alias map collect
16
+ end
17
+ end
18
+
19
+ unless File.respond_to?(:read) # Ruby 1.6
20
+ def File.read(fname)
21
+ open(fname) {|f|
22
+ return f.read
23
+ }
24
+ end
25
+ end
26
+
27
+ unless Errno.const_defined?(:ENOTEMPTY) # Windows?
28
+ module Errno
29
+ class ENOTEMPTY
30
+ # We do not raise this exception, implementation is not needed.
31
+ end
32
+ end
33
+ end
34
+
35
+ def File.binread(fname)
36
+ open(fname, 'rb') {|f|
37
+ return f.read
38
+ }
39
+ end
40
+
41
+ # for corrupted Windows' stat(2)
42
+ def File.dir?(path)
43
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
44
+ end
45
+
46
+
47
+ class ConfigTable
48
+
49
+ include Enumerable
50
+
51
+ def initialize(rbconfig)
52
+ @rbconfig = rbconfig
53
+ @items = []
54
+ @table = {}
55
+ # options
56
+ @install_prefix = nil
57
+ @config_opt = nil
58
+ @verbose = true
59
+ @no_harm = false
60
+ end
61
+
62
+ attr_accessor :install_prefix
63
+ attr_accessor :config_opt
64
+
65
+ attr_writer :verbose
66
+
67
+ def verbose?
68
+ @verbose
69
+ end
70
+
71
+ attr_writer :no_harm
72
+
73
+ def no_harm?
74
+ @no_harm
75
+ end
76
+
77
+ def [](key)
78
+ lookup(key).resolve(self)
79
+ end
80
+
81
+ def []=(key, val)
82
+ lookup(key).set val
83
+ end
84
+
85
+ def names
86
+ @items.map {|i| i.name }
87
+ end
88
+
89
+ def each(&block)
90
+ @items.each(&block)
91
+ end
92
+
93
+ def key?(name)
94
+ @table.key?(name)
95
+ end
96
+
97
+ def lookup(name)
98
+ @table[name] or setup_rb_error "no such config item: #{name}"
99
+ end
100
+
101
+ def add(item)
102
+ @items.push item
103
+ @table[item.name] = item
104
+ end
105
+
106
+ def remove(name)
107
+ item = lookup(name)
108
+ @items.delete_if {|i| i.name == name }
109
+ @table.delete_if {|name, i| i.name == name }
110
+ item
111
+ end
112
+
113
+ def load_script(path, inst = nil)
114
+ if File.file?(path)
115
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
116
+ end
117
+ end
118
+
119
+ def savefile
120
+ '.config'
121
+ end
122
+
123
+ def load_savefile
124
+ begin
125
+ File.foreach(savefile()) do |line|
126
+ k, v = *line.split(/=/, 2)
127
+ self[k] = v.strip
128
+ end
129
+ rescue Errno::ENOENT
130
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
131
+ end
132
+ end
133
+
134
+ def save
135
+ @items.each {|i| i.value }
136
+ File.open(savefile(), 'w') {|f|
137
+ @items.each do |i|
138
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
139
+ end
140
+ }
141
+ end
142
+
143
+ def load_standard_entries
144
+ standard_entries(@rbconfig).each do |ent|
145
+ add ent
146
+ end
147
+ end
148
+
149
+ def standard_entries(rbconfig)
150
+ c = rbconfig
151
+
152
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
153
+
154
+ major = c['MAJOR'].to_i
155
+ minor = c['MINOR'].to_i
156
+ teeny = c['TEENY'].to_i
157
+ version = "#{major}.#{minor}"
158
+
159
+ # ruby ver. >= 1.4.4?
160
+ newpath_p = ((major >= 2) or
161
+ ((major == 1) and
162
+ ((minor >= 5) or
163
+ ((minor == 4) and (teeny >= 4)))))
164
+
165
+ if c['rubylibdir']
166
+ # V > 1.6.3
167
+ libruby = "#{c['prefix']}/lib/ruby"
168
+ librubyver = c['rubylibdir']
169
+ librubyverarch = c['archdir']
170
+ siteruby = c['sitedir']
171
+ siterubyver = c['sitelibdir']
172
+ siterubyverarch = c['sitearchdir']
173
+ elsif newpath_p
174
+ # 1.4.4 <= V <= 1.6.3
175
+ libruby = "#{c['prefix']}/lib/ruby"
176
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
177
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
178
+ siteruby = c['sitedir']
179
+ siterubyver = "$siteruby/#{version}"
180
+ siterubyverarch = "$siterubyver/#{c['arch']}"
181
+ else
182
+ # V < 1.4.4
183
+ libruby = "#{c['prefix']}/lib/ruby"
184
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
185
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
186
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
187
+ siterubyver = siteruby
188
+ siterubyverarch = "$siterubyver/#{c['arch']}"
189
+ end
190
+ parameterize = lambda {|path|
191
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
192
+ }
193
+
194
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
195
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
196
+ else
197
+ makeprog = 'make'
198
+ end
199
+
200
+ [
201
+ ExecItem.new('installdirs', 'std/site/home',
202
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
203
+ {|val, table|
204
+ case val
205
+ when 'std'
206
+ table['rbdir'] = '$librubyver'
207
+ table['sodir'] = '$librubyverarch'
208
+ when 'site'
209
+ table['rbdir'] = '$siterubyver'
210
+ table['sodir'] = '$siterubyverarch'
211
+ when 'home'
212
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
213
+ table['prefix'] = ENV['HOME']
214
+ table['rbdir'] = '$libdir/ruby'
215
+ table['sodir'] = '$libdir/ruby'
216
+ end
217
+ },
218
+ PathItem.new('prefix', 'path', c['prefix'],
219
+ 'path prefix of target environment'),
220
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
221
+ 'the directory for commands'),
222
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
223
+ 'the directory for libraries'),
224
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
225
+ 'the directory for shared data'),
226
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
227
+ 'the directory for man pages'),
228
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
229
+ 'the directory for system configuration files'),
230
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
231
+ 'the directory for local state data'),
232
+ PathItem.new('libruby', 'path', libruby,
233
+ 'the directory for ruby libraries'),
234
+ PathItem.new('librubyver', 'path', librubyver,
235
+ 'the directory for standard ruby libraries'),
236
+ PathItem.new('librubyverarch', 'path', librubyverarch,
237
+ 'the directory for standard ruby extensions'),
238
+ PathItem.new('siteruby', 'path', siteruby,
239
+ 'the directory for version-independent aux ruby libraries'),
240
+ PathItem.new('siterubyver', 'path', siterubyver,
241
+ 'the directory for aux ruby libraries'),
242
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
243
+ 'the directory for aux ruby binaries'),
244
+ PathItem.new('rbdir', 'path', '$siterubyver',
245
+ 'the directory for ruby scripts'),
246
+ PathItem.new('sodir', 'path', '$siterubyverarch',
247
+ 'the directory for ruby extentions'),
248
+ PathItem.new('rubypath', 'path', rubypath,
249
+ 'the path to set to #! line'),
250
+ ProgramItem.new('rubyprog', 'name', rubypath,
251
+ 'the ruby program using for installation'),
252
+ ProgramItem.new('makeprog', 'name', makeprog,
253
+ 'the make program to compile ruby extentions'),
254
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
255
+ 'shebang line (#!) editing mode'),
256
+ BoolItem.new('without-ext', 'yes/no', 'no',
257
+ 'does not compile/install ruby extentions')
258
+ ]
259
+ end
260
+ private :standard_entries
261
+
262
+ def load_multipackage_entries
263
+ multipackage_entries().each do |ent|
264
+ add ent
265
+ end
266
+ end
267
+
268
+ def multipackage_entries
269
+ [
270
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
271
+ 'package names that you want to install'),
272
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
273
+ 'package names that you do not want to install')
274
+ ]
275
+ end
276
+ private :multipackage_entries
277
+
278
+ ALIASES = {
279
+ 'std-ruby' => 'librubyver',
280
+ 'stdruby' => 'librubyver',
281
+ 'rubylibdir' => 'librubyver',
282
+ 'archdir' => 'librubyverarch',
283
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
284
+ 'site-ruby' => 'siterubyver', # For backward compatibility
285
+ 'bin-dir' => 'bindir',
286
+ 'bin-dir' => 'bindir',
287
+ 'rb-dir' => 'rbdir',
288
+ 'so-dir' => 'sodir',
289
+ 'data-dir' => 'datadir',
290
+ 'ruby-path' => 'rubypath',
291
+ 'ruby-prog' => 'rubyprog',
292
+ 'ruby' => 'rubyprog',
293
+ 'make-prog' => 'makeprog',
294
+ 'make' => 'makeprog'
295
+ }
296
+
297
+ def fixup
298
+ ALIASES.each do |ali, name|
299
+ @table[ali] = @table[name]
300
+ end
301
+ @items.freeze
302
+ @table.freeze
303
+ @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
304
+ end
305
+
306
+ def parse_opt(opt)
307
+ m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
308
+ m.to_a[1,2]
309
+ end
310
+
311
+ def dllext
312
+ @rbconfig['DLEXT']
313
+ end
314
+
315
+ def value_config?(name)
316
+ lookup(name).value?
317
+ end
318
+
319
+ class Item
320
+ def initialize(name, template, default, desc)
321
+ @name = name.freeze
322
+ @template = template
323
+ @value = default
324
+ @default = default
325
+ @description = desc
326
+ end
327
+
328
+ attr_reader :name
329
+ attr_reader :description
330
+
331
+ attr_accessor :default
332
+ alias help_default default
333
+
334
+ def help_opt
335
+ "--#{@name}=#{@template}"
336
+ end
337
+
338
+ def value?
339
+ true
340
+ end
341
+
342
+ def value
343
+ @value
344
+ end
345
+
346
+ def resolve(table)
347
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
348
+ end
349
+
350
+ def set(val)
351
+ @value = check(val)
352
+ end
353
+
354
+ private
355
+
356
+ def check(val)
357
+ setup_rb_error "config: --#{name} requires argument" unless val
358
+ val
359
+ end
360
+ end
361
+
362
+ class BoolItem < Item
363
+ def config_type
364
+ 'bool'
365
+ end
366
+
367
+ def help_opt
368
+ "--#{@name}"
369
+ end
370
+
371
+ private
372
+
373
+ def check(val)
374
+ return 'yes' unless val
375
+ case val
376
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
377
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
378
+ else
379
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
380
+ end
381
+ end
382
+ end
383
+
384
+ class PathItem < Item
385
+ def config_type
386
+ 'path'
387
+ end
388
+
389
+ private
390
+
391
+ def check(path)
392
+ setup_rb_error "config: --#{@name} requires argument" unless path
393
+ path[0,1] == '$' ? path : File.expand_path(path)
394
+ end
395
+ end
396
+
397
+ class ProgramItem < Item
398
+ def config_type
399
+ 'program'
400
+ end
401
+ end
402
+
403
+ class SelectItem < Item
404
+ def initialize(name, selection, default, desc)
405
+ super
406
+ @ok = selection.split('/')
407
+ end
408
+
409
+ def config_type
410
+ 'select'
411
+ end
412
+
413
+ private
414
+
415
+ def check(val)
416
+ unless @ok.include?(val.strip)
417
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
418
+ end
419
+ val.strip
420
+ end
421
+ end
422
+
423
+ class ExecItem < Item
424
+ def initialize(name, selection, desc, &block)
425
+ super name, selection, nil, desc
426
+ @ok = selection.split('/')
427
+ @action = block
428
+ end
429
+
430
+ def config_type
431
+ 'exec'
432
+ end
433
+
434
+ def value?
435
+ false
436
+ end
437
+
438
+ def resolve(table)
439
+ setup_rb_error "$#{name()} wrongly used as option value"
440
+ end
441
+
442
+ undef set
443
+
444
+ def evaluate(val, table)
445
+ v = val.strip.downcase
446
+ unless @ok.include?(v)
447
+ setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
448
+ end
449
+ @action.call v, table
450
+ end
451
+ end
452
+
453
+ class PackageSelectionItem < Item
454
+ def initialize(name, template, default, help_default, desc)
455
+ super name, template, default, desc
456
+ @help_default = help_default
457
+ end
458
+
459
+ attr_reader :help_default
460
+
461
+ def config_type
462
+ 'package'
463
+ end
464
+
465
+ private
466
+
467
+ def check(val)
468
+ unless File.dir?("packages/#{val}")
469
+ setup_rb_error "config: no such package: #{val}"
470
+ end
471
+ val
472
+ end
473
+ end
474
+
475
+ class MetaConfigEnvironment
476
+ def initialize(config, installer)
477
+ @config = config
478
+ @installer = installer
479
+ end
480
+
481
+ def config_names
482
+ @config.names
483
+ end
484
+
485
+ def config?(name)
486
+ @config.key?(name)
487
+ end
488
+
489
+ def bool_config?(name)
490
+ @config.lookup(name).config_type == 'bool'
491
+ end
492
+
493
+ def path_config?(name)
494
+ @config.lookup(name).config_type == 'path'
495
+ end
496
+
497
+ def value_config?(name)
498
+ @config.lookup(name).config_type != 'exec'
499
+ end
500
+
501
+ def add_config(item)
502
+ @config.add item
503
+ end
504
+
505
+ def add_bool_config(name, default, desc)
506
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
507
+ end
508
+
509
+ def add_path_config(name, default, desc)
510
+ @config.add PathItem.new(name, 'path', default, desc)
511
+ end
512
+
513
+ def set_config_default(name, default)
514
+ @config.lookup(name).default = default
515
+ end
516
+
517
+ def remove_config(name)
518
+ @config.remove(name)
519
+ end
520
+
521
+ # For only multipackage
522
+ def packages
523
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
524
+ @installer.packages
525
+ end
526
+
527
+ # For only multipackage
528
+ def declare_packages(list)
529
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
530
+ @installer.packages = list
531
+ end
532
+ end
533
+
534
+ end # class ConfigTable
535
+
536
+
537
+ # This module requires: #verbose?, #no_harm?
538
+ module FileOperations
539
+
540
+ def mkdir_p(dirname, prefix = nil)
541
+ dirname = prefix + File.expand_path(dirname) if prefix
542
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
543
+ return if no_harm?
544
+
545
+ # Does not check '/', it's too abnormal.
546
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
547
+ if /\A[a-z]:\z/i =~ dirs[0]
548
+ disk = dirs.shift
549
+ dirs[0] = disk + dirs[0]
550
+ end
551
+ dirs.each_index do |idx|
552
+ path = dirs[0..idx].join('')
553
+ Dir.mkdir path unless File.dir?(path)
554
+ end
555
+ end
556
+
557
+ def rm_f(path)
558
+ $stderr.puts "rm -f #{path}" if verbose?
559
+ return if no_harm?
560
+ force_remove_file path
561
+ end
562
+
563
+ def rm_rf(path)
564
+ $stderr.puts "rm -rf #{path}" if verbose?
565
+ return if no_harm?
566
+ remove_tree path
567
+ end
568
+
569
+ def remove_tree(path)
570
+ if File.symlink?(path)
571
+ remove_file path
572
+ elsif File.dir?(path)
573
+ remove_tree0 path
574
+ else
575
+ force_remove_file path
576
+ end
577
+ end
578
+
579
+ def remove_tree0(path)
580
+ Dir.foreach(path) do |ent|
581
+ next if ent == '.'
582
+ next if ent == '..'
583
+ entpath = "#{path}/#{ent}"
584
+ if File.symlink?(entpath)
585
+ remove_file entpath
586
+ elsif File.dir?(entpath)
587
+ remove_tree0 entpath
588
+ else
589
+ force_remove_file entpath
590
+ end
591
+ end
592
+ begin
593
+ Dir.rmdir path
594
+ rescue Errno::ENOTEMPTY
595
+ # directory may not be empty
596
+ end
597
+ end
598
+
599
+ def move_file(src, dest)
600
+ force_remove_file dest
601
+ begin
602
+ File.rename src, dest
603
+ rescue
604
+ File.open(dest, 'wb') {|f|
605
+ f.write File.binread(src)
606
+ }
607
+ File.chmod File.stat(src).mode, dest
608
+ File.unlink src
609
+ end
610
+ end
611
+
612
+ def force_remove_file(path)
613
+ begin
614
+ remove_file path
615
+ rescue
616
+ end
617
+ end
618
+
619
+ def remove_file(path)
620
+ File.chmod 0777, path
621
+ File.unlink path
622
+ end
623
+
624
+ def install(from, dest, mode, prefix = nil)
625
+ $stderr.puts "install #{from} #{dest}" if verbose?
626
+ return if no_harm?
627
+
628
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
629
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
630
+ str = File.binread(from)
631
+ if diff?(str, realdest)
632
+ verbose_off {
633
+ rm_f realdest if File.exist?(realdest)
634
+ }
635
+ File.open(realdest, 'wb') {|f|
636
+ f.write str
637
+ }
638
+ File.chmod mode, realdest
639
+
640
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
641
+ if prefix
642
+ f.puts realdest.sub(prefix, '')
643
+ else
644
+ f.puts realdest
645
+ end
646
+ }
647
+ end
648
+ end
649
+
650
+ def diff?(new_content, path)
651
+ return true unless File.exist?(path)
652
+ new_content != File.binread(path)
653
+ end
654
+
655
+ def command(*args)
656
+ $stderr.puts args.join(' ') if verbose?
657
+ system(*args) or raise RuntimeError,
658
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
659
+ end
660
+
661
+ def ruby(*args)
662
+ command config('rubyprog'), *args
663
+ end
664
+
665
+ def make(task = nil)
666
+ command(*[config('makeprog'), task].compact)
667
+ end
668
+
669
+ def extdir?(dir)
670
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
671
+ end
672
+
673
+ def files_of(dir)
674
+ Dir.open(dir) {|d|
675
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
676
+ }
677
+ end
678
+
679
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
680
+
681
+ def directories_of(dir)
682
+ Dir.open(dir) {|d|
683
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
684
+ }
685
+ end
686
+
687
+ end
688
+
689
+
690
+ # This module requires: #srcdir_root, #objdir_root, #relpath
691
+ module HookScriptAPI
692
+
693
+ def get_config(key)
694
+ @config[key]
695
+ end
696
+
697
+ alias config get_config
698
+
699
+ # obsolete: use metaconfig to change configuration
700
+ def set_config(key, val)
701
+ @config[key] = val
702
+ end
703
+
704
+ #
705
+ # srcdir/objdir (works only in the package directory)
706
+ #
707
+
708
+ def curr_srcdir
709
+ "#{srcdir_root()}/#{relpath()}"
710
+ end
711
+
712
+ def curr_objdir
713
+ "#{objdir_root()}/#{relpath()}"
714
+ end
715
+
716
+ def srcfile(path)
717
+ "#{curr_srcdir()}/#{path}"
718
+ end
719
+
720
+ def srcexist?(path)
721
+ File.exist?(srcfile(path))
722
+ end
723
+
724
+ def srcdirectory?(path)
725
+ File.dir?(srcfile(path))
726
+ end
727
+
728
+ def srcfile?(path)
729
+ File.file?(srcfile(path))
730
+ end
731
+
732
+ def srcentries(path = '.')
733
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
734
+ return d.to_a - %w(. ..)
735
+ }
736
+ end
737
+
738
+ def srcfiles(path = '.')
739
+ srcentries(path).select {|fname|
740
+ File.file?(File.join(curr_srcdir(), path, fname))
741
+ }
742
+ end
743
+
744
+ def srcdirectories(path = '.')
745
+ srcentries(path).select {|fname|
746
+ File.dir?(File.join(curr_srcdir(), path, fname))
747
+ }
748
+ end
749
+
750
+ end
751
+
752
+
753
+ class ToplevelInstaller
754
+
755
+ Version = '3.4.1'
756
+ Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
757
+
758
+ TASKS = [
759
+ [ 'all', 'do config, setup, then install' ],
760
+ [ 'config', 'saves your configurations' ],
761
+ [ 'show', 'shows current configuration' ],
762
+ [ 'setup', 'compiles ruby extentions and others' ],
763
+ [ 'install', 'installs files' ],
764
+ [ 'test', 'run all tests in test/' ],
765
+ [ 'clean', "does `make clean' for each extention" ],
766
+ [ 'distclean',"does `make distclean' for each extention" ]
767
+ ]
768
+
769
+ def ToplevelInstaller.invoke
770
+ config = ConfigTable.new(load_rbconfig())
771
+ config.load_standard_entries
772
+ config.load_multipackage_entries if multipackage?
773
+ config.fixup
774
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
775
+ klass.new(File.dirname($0), config).invoke
776
+ end
777
+
778
+ def ToplevelInstaller.multipackage?
779
+ File.dir?(File.dirname($0) + '/packages')
780
+ end
781
+
782
+ def ToplevelInstaller.load_rbconfig
783
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
784
+ ARGV.delete(arg)
785
+ load File.expand_path(arg.split(/=/, 2)[1])
786
+ $".push 'rbconfig.rb'
787
+ else
788
+ require 'rbconfig'
789
+ end
790
+ ::Config::CONFIG
791
+ end
792
+
793
+ def initialize(ardir_root, config)
794
+ @ardir = File.expand_path(ardir_root)
795
+ @config = config
796
+ # cache
797
+ @valid_task_re = nil
798
+ end
799
+
800
+ def config(key)
801
+ @config[key]
802
+ end
803
+
804
+ def inspect
805
+ "#<#{self.class} #{__id__()}>"
806
+ end
807
+
808
+ def invoke
809
+ run_metaconfigs
810
+ case task = parsearg_global()
811
+ when nil, 'all'
812
+ parsearg_config
813
+ init_installers
814
+ exec_config
815
+ exec_setup
816
+ exec_install
817
+ else
818
+ case task
819
+ when 'config', 'test'
820
+ ;
821
+ when 'clean', 'distclean'
822
+ @config.load_savefile if File.exist?(@config.savefile)
823
+ else
824
+ @config.load_savefile
825
+ end
826
+ __send__ "parsearg_#{task}"
827
+ init_installers
828
+ __send__ "exec_#{task}"
829
+ end
830
+ end
831
+
832
+ def run_metaconfigs
833
+ @config.load_script "#{@ardir}/metaconfig"
834
+ end
835
+
836
+ def init_installers
837
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
838
+ end
839
+
840
+ #
841
+ # Hook Script API bases
842
+ #
843
+
844
+ def srcdir_root
845
+ @ardir
846
+ end
847
+
848
+ def objdir_root
849
+ '.'
850
+ end
851
+
852
+ def relpath
853
+ '.'
854
+ end
855
+
856
+ #
857
+ # Option Parsing
858
+ #
859
+
860
+ def parsearg_global
861
+ while arg = ARGV.shift
862
+ case arg
863
+ when /\A\w+\z/
864
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
865
+ return arg
866
+ when '-q', '--quiet'
867
+ @config.verbose = false
868
+ when '--verbose'
869
+ @config.verbose = true
870
+ when '--help'
871
+ print_usage $stdout
872
+ exit 0
873
+ when '--version'
874
+ puts "#{File.basename($0)} version #{Version}"
875
+ exit 0
876
+ when '--copyright'
877
+ puts Copyright
878
+ exit 0
879
+ else
880
+ setup_rb_error "unknown global option '#{arg}'"
881
+ end
882
+ end
883
+ nil
884
+ end
885
+
886
+ def valid_task?(t)
887
+ valid_task_re() =~ t
888
+ end
889
+
890
+ def valid_task_re
891
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
892
+ end
893
+
894
+ def parsearg_no_options
895
+ unless ARGV.empty?
896
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
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
+ def noop(rel)
1179
+ end
1180
+
1181
+ #
1182
+ # Hook Script API base methods
1183
+ #
1184
+
1185
+ def srcdir_root
1186
+ @srcdir
1187
+ end
1188
+
1189
+ def objdir_root
1190
+ @objdir
1191
+ end
1192
+
1193
+ def relpath
1194
+ @currdir
1195
+ end
1196
+
1197
+ #
1198
+ # Config Access
1199
+ #
1200
+
1201
+ # module FileOperations requires this
1202
+ def verbose?
1203
+ @config.verbose?
1204
+ end
1205
+
1206
+ # module FileOperations requires this
1207
+ def no_harm?
1208
+ @config.no_harm?
1209
+ end
1210
+
1211
+ def verbose_off
1212
+ begin
1213
+ save, @config.verbose = @config.verbose?, false
1214
+ yield
1215
+ ensure
1216
+ @config.verbose = save
1217
+ end
1218
+ end
1219
+
1220
+ #
1221
+ # TASK config
1222
+ #
1223
+
1224
+ def exec_config
1225
+ exec_task_traverse 'config'
1226
+ end
1227
+
1228
+ alias config_dir_bin noop
1229
+ alias config_dir_lib noop
1230
+
1231
+ def config_dir_ext(rel)
1232
+ extconf if extdir?(curr_srcdir())
1233
+ end
1234
+
1235
+ alias config_dir_data noop
1236
+ alias config_dir_conf noop
1237
+ alias config_dir_man noop
1238
+
1239
+ def extconf
1240
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1241
+ end
1242
+
1243
+ #
1244
+ # TASK setup
1245
+ #
1246
+
1247
+ def exec_setup
1248
+ exec_task_traverse 'setup'
1249
+ end
1250
+
1251
+ def setup_dir_bin(rel)
1252
+ files_of(curr_srcdir()).each do |fname|
1253
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
1254
+ end
1255
+ end
1256
+
1257
+ alias setup_dir_lib noop
1258
+
1259
+ def setup_dir_ext(rel)
1260
+ make if extdir?(curr_srcdir())
1261
+ end
1262
+
1263
+ alias setup_dir_data noop
1264
+ alias setup_dir_conf noop
1265
+ alias setup_dir_man noop
1266
+
1267
+ def update_shebang_line(path)
1268
+ return if no_harm?
1269
+ return if config('shebang') == 'never'
1270
+ old = Shebang.load(path)
1271
+ if old
1272
+ $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
1273
+ new = new_shebang(old)
1274
+ return if new.to_s == old.to_s
1275
+ else
1276
+ return unless config('shebang') == 'all'
1277
+ new = Shebang.new(config('rubypath'))
1278
+ end
1279
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1280
+ open_atomic_writer(path) {|output|
1281
+ File.open(path, 'rb') {|f|
1282
+ f.gets if old # discard
1283
+ output.puts new.to_s
1284
+ output.print f.read
1285
+ }
1286
+ }
1287
+ end
1288
+
1289
+ def new_shebang(old)
1290
+ if /\Aruby/ =~ File.basename(old.cmd)
1291
+ Shebang.new(config('rubypath'), old.args)
1292
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1293
+ Shebang.new(config('rubypath'), old.args[1..-1])
1294
+ else
1295
+ return old unless config('shebang') == 'all'
1296
+ Shebang.new(config('rubypath'))
1297
+ end
1298
+ end
1299
+
1300
+ def open_atomic_writer(path, &block)
1301
+ tmpfile = File.basename(path) + '.tmp'
1302
+ begin
1303
+ File.open(tmpfile, 'wb', &block)
1304
+ File.rename tmpfile, File.basename(path)
1305
+ ensure
1306
+ File.unlink tmpfile if File.exist?(tmpfile)
1307
+ end
1308
+ end
1309
+
1310
+ class Shebang
1311
+ def Shebang.load(path)
1312
+ line = nil
1313
+ File.open(path) {|f|
1314
+ line = f.gets
1315
+ }
1316
+ return nil unless /\A#!/ =~ line
1317
+ parse(line)
1318
+ end
1319
+
1320
+ def Shebang.parse(line)
1321
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1322
+ new(cmd, args)
1323
+ end
1324
+
1325
+ def initialize(cmd, args = [])
1326
+ @cmd = cmd
1327
+ @args = args
1328
+ end
1329
+
1330
+ attr_reader :cmd
1331
+ attr_reader :args
1332
+
1333
+ def to_s
1334
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1335
+ end
1336
+ end
1337
+
1338
+ #
1339
+ # TASK install
1340
+ #
1341
+
1342
+ def exec_install
1343
+ rm_f 'InstalledFiles'
1344
+ exec_task_traverse 'install'
1345
+ end
1346
+
1347
+ def install_dir_bin(rel)
1348
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1349
+ end
1350
+
1351
+ def install_dir_lib(rel)
1352
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1353
+ end
1354
+
1355
+ def install_dir_ext(rel)
1356
+ return unless extdir?(curr_srcdir())
1357
+ install_files rubyextentions('.'),
1358
+ "#{config('sodir')}/#{File.dirname(rel)}",
1359
+ 0555
1360
+ end
1361
+
1362
+ def install_dir_data(rel)
1363
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1364
+ end
1365
+
1366
+ def install_dir_conf(rel)
1367
+ # FIXME: should not remove current config files
1368
+ # (rename previous file to .old/.org)
1369
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1370
+ end
1371
+
1372
+ def install_dir_man(rel)
1373
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1374
+ end
1375
+
1376
+ def install_files(list, dest, mode)
1377
+ mkdir_p dest, @config.install_prefix
1378
+ list.each do |fname|
1379
+ install fname, dest, mode, @config.install_prefix
1380
+ end
1381
+ end
1382
+
1383
+ def libfiles
1384
+ glob_reject(%w(*.y *.output), targetfiles())
1385
+ end
1386
+
1387
+ def rubyextentions(dir)
1388
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1389
+ if ents.empty?
1390
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1391
+ end
1392
+ ents
1393
+ end
1394
+
1395
+ def targetfiles
1396
+ mapdir(existfiles() - hookfiles())
1397
+ end
1398
+
1399
+ def mapdir(ents)
1400
+ ents.map {|ent|
1401
+ if File.exist?(ent)
1402
+ then ent # objdir
1403
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1404
+ end
1405
+ }
1406
+ end
1407
+
1408
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1409
+ JUNK_FILES = %w(
1410
+ core RCSLOG tags TAGS .make.state
1411
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1412
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1413
+
1414
+ *.org *.in .*
1415
+ )
1416
+
1417
+ def existfiles
1418
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1419
+ end
1420
+
1421
+ def hookfiles
1422
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1423
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1424
+ }.flatten
1425
+ end
1426
+
1427
+ def glob_select(pat, ents)
1428
+ re = globs2re([pat])
1429
+ ents.select {|ent| re =~ ent }
1430
+ end
1431
+
1432
+ def glob_reject(pats, ents)
1433
+ re = globs2re(pats)
1434
+ ents.reject {|ent| re =~ ent }
1435
+ end
1436
+
1437
+ GLOB2REGEX = {
1438
+ '.' => '\.',
1439
+ '$' => '\$',
1440
+ '#' => '\#',
1441
+ '*' => '.*'
1442
+ }
1443
+
1444
+ def globs2re(pats)
1445
+ /\A(?:#{
1446
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1447
+ })\z/
1448
+ end
1449
+
1450
+ #
1451
+ # TASK test
1452
+ #
1453
+
1454
+ TESTDIR = 'test'
1455
+
1456
+ def exec_test
1457
+ unless File.directory?('test')
1458
+ $stderr.puts 'no test in this package' if verbose?
1459
+ return
1460
+ end
1461
+ $stderr.puts 'Running tests...' if verbose?
1462
+ begin
1463
+ require 'test/unit'
1464
+ rescue LoadError
1465
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1466
+ end
1467
+ runner = Test::Unit::AutoRunner.new(true)
1468
+ runner.to_run << TESTDIR
1469
+ runner.run
1470
+ end
1471
+
1472
+ #
1473
+ # TASK clean
1474
+ #
1475
+
1476
+ def exec_clean
1477
+ exec_task_traverse 'clean'
1478
+ rm_f @config.savefile
1479
+ rm_f 'InstalledFiles'
1480
+ end
1481
+
1482
+ alias clean_dir_bin noop
1483
+ alias clean_dir_lib noop
1484
+ alias clean_dir_data noop
1485
+ alias clean_dir_conf noop
1486
+ alias clean_dir_man noop
1487
+
1488
+ def clean_dir_ext(rel)
1489
+ return unless extdir?(curr_srcdir())
1490
+ make 'clean' if File.file?('Makefile')
1491
+ end
1492
+
1493
+ #
1494
+ # TASK distclean
1495
+ #
1496
+
1497
+ def exec_distclean
1498
+ exec_task_traverse 'distclean'
1499
+ rm_f @config.savefile
1500
+ rm_f 'InstalledFiles'
1501
+ end
1502
+
1503
+ alias distclean_dir_bin noop
1504
+ alias distclean_dir_lib noop
1505
+
1506
+ def distclean_dir_ext(rel)
1507
+ return unless extdir?(curr_srcdir())
1508
+ make 'distclean' if File.file?('Makefile')
1509
+ end
1510
+
1511
+ alias distclean_dir_data noop
1512
+ alias distclean_dir_conf noop
1513
+ alias distclean_dir_man noop
1514
+
1515
+ #
1516
+ # Traversing
1517
+ #
1518
+
1519
+ def exec_task_traverse(task)
1520
+ run_hook "pre-#{task}"
1521
+ FILETYPES.each do |type|
1522
+ if type == 'ext' and config('without-ext') == 'yes'
1523
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1524
+ next
1525
+ end
1526
+ traverse task, type, "#{task}_dir_#{type}"
1527
+ end
1528
+ run_hook "post-#{task}"
1529
+ end
1530
+
1531
+ def traverse(task, rel, mid)
1532
+ dive_into(rel) {
1533
+ run_hook "pre-#{task}"
1534
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1535
+ directories_of(curr_srcdir()).each do |d|
1536
+ traverse task, "#{rel}/#{d}", mid
1537
+ end
1538
+ run_hook "post-#{task}"
1539
+ }
1540
+ end
1541
+
1542
+ def dive_into(rel)
1543
+ return unless File.dir?("#{@srcdir}/#{rel}")
1544
+
1545
+ dir = File.basename(rel)
1546
+ Dir.mkdir dir unless File.dir?(dir)
1547
+ prevdir = Dir.pwd
1548
+ Dir.chdir dir
1549
+ $stderr.puts '---> ' + rel if verbose?
1550
+ @currdir = rel
1551
+ yield
1552
+ Dir.chdir prevdir
1553
+ $stderr.puts '<--- ' + rel if verbose?
1554
+ @currdir = File.dirname(rel)
1555
+ end
1556
+
1557
+ def run_hook(id)
1558
+ path = [ "#{curr_srcdir()}/#{id}",
1559
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1560
+ return unless path
1561
+ begin
1562
+ instance_eval File.read(path), path, 1
1563
+ rescue
1564
+ raise if $DEBUG
1565
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1566
+ end
1567
+ end
1568
+
1569
+ end # class Installer
1570
+
1571
+
1572
+ class SetupError < StandardError; end
1573
+
1574
+ def setup_rb_error(msg)
1575
+ raise SetupError, msg
1576
+ end
1577
+
1578
+ if $0 == __FILE__
1579
+ begin
1580
+ ToplevelInstaller.invoke
1581
+ rescue SetupError
1582
+ raise if $DEBUG
1583
+ $stderr.puts $!.message
1584
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1585
+ exit 1
1586
+ end
1587
+ end