Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 344
- Log:
Massive changeset which brings the old, ROOL customised Instiki
version up to date, but without any ROOL customisations in this
latest checked-in version (which is 0.19.1). This is deliberate,
so that it's easy to see the changes made for the ROOL version
in a subsequent changeset. The 'app/views/shared' directory is not
part of Instiki but is kept to maintain the change history with
updated ROOL customisations, some of which involve the same files
in that same directory.
- Author:
- rool
- Date:
- Sat Mar 19 19:52:13 +0000 2011
- Size:
- 28298 Bytes
1 | # |
2 | # setup.rb |
3 | # |
4 | # Copyright (c) 2000-2004 Minero Aoki |
5 | # |
6 | # This program is free software. |
7 | # You can distribute/modify this program under the terms of |
8 | # the GNU Lesser General Public License version 2.1. |
9 | # |
10 | |
11 | # |
12 | # For backward compatibility |
13 | # |
14 | |
15 | unless Enumerable.method_defined?(:map) |
16 | module Enumerable |
17 | alias map collect |
18 | end |
19 | end |
20 | |
21 | unless Enumerable.method_defined?(:detect) |
22 | module Enumerable |
23 | alias detect find |
24 | end |
25 | end |
26 | |
27 | unless Enumerable.method_defined?(:select) |
28 | module Enumerable |
29 | alias select find_all |
30 | end |
31 | end |
32 | |
33 | unless Enumerable.method_defined?(:reject) |
34 | module Enumerable |
35 | def reject |
36 | result = [] |
37 | each do |i| |
38 | result.push i unless yield(i) |
39 | end |
40 | result |
41 | end |
42 | end |
43 | end |
44 | |
45 | unless Enumerable.method_defined?(:inject) |
46 | module Enumerable |
47 | def inject(result) |
48 | each do |i| |
49 | result = yield(result, i) |
50 | end |
51 | result |
52 | end |
53 | end |
54 | end |
55 | |
56 | unless Enumerable.method_defined?(:any?) |
57 | module Enumerable |
58 | def any? |
59 | each do |i| |
60 | return true if yield(i) |
61 | end |
62 | false |
63 | end |
64 | end |
65 | end |
66 | |
67 | unless File.respond_to?(:read) |
68 | def File.read(fname) |
69 | open(fname) {|f| |
70 | return f.read |
71 | } |
72 | end |
73 | end |
74 | |
75 | # |
76 | # Application independent utilities |
77 | # |
78 | |
79 | def File.binread(fname) |
80 | open(fname, 'rb') {|f| |
81 | return f.read |
82 | } |
83 | end |
84 | |
85 | # for corrupted windows stat(2) |
86 | def File.dir?(path) |
87 | File.directory?((path[-1,1] == '/') ? path : path + '/') |
88 | end |
89 | |
90 | # |
91 | # Config |
92 | # |
93 | |
94 | if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg } |
95 | ARGV.delete(arg) |
96 | require arg.split(/=/, 2)[1] |
97 | $".push 'rbconfig.rb' |
98 | else |
99 | require 'rbconfig' |
100 | end |
101 | |
102 | def multipackage_install? |
103 | FileTest.directory?(File.dirname($0) + '/packages') |
104 | end |
105 | |
106 | |
107 | class ConfigTable |
108 | |
109 | c = ::Config::CONFIG |
110 | |
111 | rubypath = c['bindir'] + '/' + c['ruby_install_name'] |
112 | |
113 | major = c['MAJOR'].to_i |
114 | minor = c['MINOR'].to_i |
115 | teeny = c['TEENY'].to_i |
116 | version = "#{major}.#{minor}" |
117 | |
118 | # ruby ver. >= 1.4.4? |
119 | newpath_p = ((major >= 2) or |
120 | ((major == 1) and |
121 | ((minor >= 5) or |
122 | ((minor == 4) and (teeny >= 4))))) |
123 | |
124 | subprefix = lambda {|path| |
125 | path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix') |
126 | } |
127 | |
128 | if c['rubylibdir'] |
129 | # V < 1.6.3 |
130 | stdruby = subprefix.call(c['rubylibdir']) |
131 | siteruby = subprefix.call(c['sitedir']) |
132 | versite = subprefix.call(c['sitelibdir']) |
133 | sodir = subprefix.call(c['sitearchdir']) |
134 | elsif newpath_p |
135 | # 1.4.4 <= V <= 1.6.3 |
136 | stdruby = "$prefix/lib/ruby/#{version}" |
137 | siteruby = subprefix.call(c['sitedir']) |
138 | versite = siteruby + '/' + version |
139 | sodir = "$site-ruby/#{c['arch']}" |
140 | else |
141 | # V < 1.4.4 |
142 | stdruby = "$prefix/lib/ruby/#{version}" |
143 | siteruby = "$prefix/lib/ruby/#{version}/site_ruby" |
144 | versite = siteruby |
145 | sodir = "$site-ruby/#{c['arch']}" |
146 | end |
147 | |
148 | if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } |
149 | makeprog = arg.sub(/'/, '').split(/=/, 2)[1] |
150 | else |
151 | makeprog = 'make' |
152 | end |
153 | |
154 | common_descripters = [ |
155 | [ 'prefix', [ c['prefix'], |
156 | 'path', |
157 | 'path prefix of target environment' ] ], |
158 | [ 'std-ruby', [ stdruby, |
159 | 'path', |
160 | 'the directory for standard ruby libraries' ] ], |
161 | [ 'site-ruby-common', [ siteruby, |
162 | 'path', |
163 | 'the directory for version-independent non-standard ruby libraries' ] ], |
164 | [ 'site-ruby', [ versite, |
165 | 'path', |
166 | 'the directory for non-standard ruby libraries' ] ], |
167 | [ 'bin-dir', [ '$prefix/bin', |
168 | 'path', |
169 | 'the directory for commands' ] ], |
170 | [ 'rb-dir', [ '$site-ruby', |
171 | 'path', |
172 | 'the directory for ruby scripts' ] ], |
173 | [ 'so-dir', [ sodir, |
174 | 'path', |
175 | 'the directory for ruby extentions' ] ], |
176 | [ 'data-dir', [ '$prefix/share', |
177 | 'path', |
178 | 'the directory for shared data' ] ], |
179 | [ 'ruby-path', [ rubypath, |
180 | 'path', |
181 | 'path to set to #! line' ] ], |
182 | [ 'ruby-prog', [ rubypath, |
183 | 'name', |
184 | 'the ruby program using for installation' ] ], |
185 | [ 'make-prog', [ makeprog, |
186 | 'name', |
187 | 'the make program to compile ruby extentions' ] ], |
188 | [ 'without-ext', [ 'no', |
189 | 'yes/no', |
190 | 'does not compile/install ruby extentions' ] ] |
191 | ] |
192 | multipackage_descripters = [ |
193 | [ 'with', [ '', |
194 | 'name,name...', |
195 | 'package names that you want to install', |
196 | 'ALL' ] ], |
197 | [ 'without', [ '', |
198 | 'name,name...', |
199 | 'package names that you do not want to install', |
200 | 'NONE' ] ] |
201 | ] |
202 | if multipackage_install? |
203 | DESCRIPTER = common_descripters + multipackage_descripters |
204 | else |
205 | DESCRIPTER = common_descripters |
206 | end |
207 | |
208 | SAVE_FILE = 'config.save' |
209 | |
210 | def ConfigTable.each_name(&block) |
211 | keys().each(&block) |
212 | end |
213 | |
214 | def ConfigTable.keys |
215 | DESCRIPTER.map {|name, *dummy| name } |
216 | end |
217 | |
218 | def ConfigTable.each_definition(&block) |
219 | DESCRIPTER.each(&block) |
220 | end |
221 | |
222 | def ConfigTable.get_entry(name) |
223 | name, ent = DESCRIPTER.assoc(name) |
224 | ent |
225 | end |
226 | |
227 | def ConfigTable.get_entry!(name) |
228 | get_entry(name) or raise ArgumentError, "no such config: #{name}" |
229 | end |
230 | |
231 | def ConfigTable.add_entry(name, vals) |
232 | ConfigTable::DESCRIPTER.push [name,vals] |
233 | end |
234 | |
235 | def ConfigTable.remove_entry(name) |
236 | get_entry(name) or raise ArgumentError, "no such config: #{name}" |
237 | DESCRIPTER.delete_if {|n, arr| n == name } |
238 | end |
239 | |
240 | def ConfigTable.config_key?(name) |
241 | get_entry(name) ? true : false |
242 | end |
243 | |
244 | def ConfigTable.bool_config?(name) |
245 | ent = get_entry(name) or return false |
246 | ent[1] == 'yes/no' |
247 | end |
248 | |
249 | def ConfigTable.value_config?(name) |
250 | ent = get_entry(name) or return false |
251 | ent[1] != 'yes/no' |
252 | end |
253 | |
254 | def ConfigTable.path_config?(name) |
255 | ent = get_entry(name) or return false |
256 | ent[1] == 'path' |
257 | end |
258 | |
259 | |
260 | class << self |
261 | alias newobj new |
262 | end |
263 | |
264 | def ConfigTable.new |
265 | c = newobj() |
266 | c.initialize_from_table |
267 | c |
268 | end |
269 | |
270 | def ConfigTable.load |
271 | c = newobj() |
272 | c.initialize_from_file |
273 | c |
274 | end |
275 | |
276 | def initialize_from_table |
277 | @table = {} |
278 | DESCRIPTER.each do |k, (default, vname, desc, default2)| |
279 | @table[k] = default |
280 | end |
281 | end |
282 | |
283 | def initialize_from_file |
284 | raise InstallError, "#{File.basename $0} config first"\ |
285 | unless File.file?(SAVE_FILE) |
286 | @table = {} |
287 | File.foreach(SAVE_FILE) do |line| |
288 | k, v = line.split(/=/, 2) |
289 | @table[k] = v.strip |
290 | end |
291 | end |
292 | |
293 | def save |
294 | File.open(SAVE_FILE, 'w') {|f| |
295 | @table.each do |k, v| |
296 | f.printf "%s=%s\n", k, v if v |
297 | end |
298 | } |
299 | end |
300 | |
301 | def []=(k, v) |
302 | raise InstallError, "unknown config option #{k}"\ |
303 | unless ConfigTable.config_key?(k) |
304 | @table[k] = v |
305 | end |
306 | |
307 | def [](key) |
308 | return nil unless @table[key] |
309 | @table[key].gsub(%r<\$([^/]+)>) { self[$1] } |
310 | end |
311 | |
312 | def set_raw(key, val) |
313 | @table[key] = val |
314 | end |
315 | |
316 | def get_raw(key) |
317 | @table[key] |
318 | end |
319 | |
320 | end |
321 | |
322 | |
323 | module MetaConfigAPI |
324 | |
325 | def eval_file_ifexist(fname) |
326 | instance_eval File.read(fname), fname, 1 if File.file?(fname) |
327 | end |
328 | |
329 | def config_names |
330 | ConfigTable.keys |
331 | end |
332 | |
333 | def config?(name) |
334 | ConfigTable.config_key?(name) |
335 | end |
336 | |
337 | def bool_config?(name) |
338 | ConfigTable.bool_config?(name) |
339 | end |
340 | |
341 | def value_config?(name) |
342 | ConfigTable.value_config?(name) |
343 | end |
344 | |
345 | def path_config?(name) |
346 | ConfigTable.path_config?(name) |
347 | end |
348 | |
349 | def add_config(name, argname, default, desc) |
350 | ConfigTable.add_entry name,[default,argname,desc] |
351 | end |
352 | |
353 | def add_path_config(name, default, desc) |
354 | add_config name, 'path', default, desc |
355 | end |
356 | |
357 | def add_bool_config(name, default, desc) |
358 | add_config name, 'yes/no', default ? 'yes' : 'no', desc |
359 | end |
360 | |
361 | def set_config_default(name, default) |
362 | if bool_config?(name) |
363 | ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no') |
364 | else |
365 | ConfigTable.get_entry!(name)[0] = default |
366 | end |
367 | end |
368 | |
369 | def remove_config(name) |
370 | ent = ConfigTable.get_entry(name) |
371 | ConfigTable.remove_entry name |
372 | ent |
373 | end |
374 | |
375 | end |
376 | |
377 | # |
378 | # File Operations |
379 | # |
380 | |
381 | module FileOperations |
382 | |
383 | def mkdir_p(dirname, prefix = nil) |
384 | dirname = prefix + dirname if prefix |
385 | $stderr.puts "mkdir -p #{dirname}" if verbose? |
386 | return if no_harm? |
387 | |
388 | # does not check '/'... it's too abnormal case |
389 | dirs = dirname.split(%r<(?=/)>) |
390 | if /\A[a-z]:\z/i =~ dirs[0] |
391 | disk = dirs.shift |
392 | dirs[0] = disk + dirs[0] |
393 | end |
394 | dirs.each_index do |idx| |
395 | path = dirs[0..idx].join('') |
396 | Dir.mkdir path unless File.dir?(path) |
397 | end |
398 | end |
399 | |
400 | def rm_f(fname) |
401 | $stderr.puts "rm -f #{fname}" if verbose? |
402 | return if no_harm? |
403 | |
404 | if File.exist?(fname) or File.symlink?(fname) |
405 | File.chmod 0777, fname |
406 | File.unlink fname |
407 | end |
408 | end |
409 | |
410 | def rm_rf(dn) |
411 | $stderr.puts "rm -rf #{dn}" if verbose? |
412 | return if no_harm? |
413 | |
414 | Dir.chdir dn |
415 | Dir.foreach('.') do |fn| |
416 | next if fn == '.' |
417 | next if fn == '..' |
418 | if File.dir?(fn) |
419 | verbose_off { |
420 | rm_rf fn |
421 | } |
422 | else |
423 | verbose_off { |
424 | rm_f fn |
425 | } |
426 | end |
427 | end |
428 | Dir.chdir '..' |
429 | Dir.rmdir dn |
430 | end |
431 | |
432 | def move_file(src, dest) |
433 | File.unlink dest if File.exist?(dest) |
434 | begin |
435 | File.rename src, dest |
436 | rescue |
437 | File.open(dest, 'wb') {|f| f.write File.binread(src) } |
438 | File.chmod File.stat(src).mode, dest |
439 | File.unlink src |
440 | end |
441 | end |
442 | |
443 | def install(from, dest, mode, prefix = nil) |
444 | $stderr.puts "install #{from} #{dest}" if verbose? |
445 | return if no_harm? |
446 | |
447 | realdest = prefix + dest if prefix |
448 | realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) |
449 | str = File.binread(from) |
450 | if diff?(str, realdest) |
451 | verbose_off { |
452 | rm_f realdest if File.exist?(realdest) |
453 | } |
454 | File.open(realdest, 'wb') {|f| |
455 | f.write str |
456 | } |
457 | File.chmod mode, realdest |
458 | |
459 | File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| |
460 | if prefix |
461 | f.puts realdest.sub(prefix, '') |
462 | else |
463 | f.puts realdest |
464 | end |
465 | } |
466 | end |
467 | end |
468 | |
469 | def diff?(new_content, path) |
470 | return true unless File.exist?(path) |
471 | new_content != File.binread(path) |
472 | end |
473 | |
474 | def command(str) |
475 | $stderr.puts str if verbose? |
476 | system str or raise RuntimeError, "'system #{str}' failed" |
477 | end |
478 | |
479 | def ruby(str) |
480 | command config('ruby-prog') + ' ' + str |
481 | end |
482 | |
483 | def make(task = '') |
484 | command config('make-prog') + ' ' + task |
485 | end |
486 | |
487 | def extdir?(dir) |
488 | File.exist?(dir + '/MANIFEST') |
489 | end |
490 | |
491 | def all_files_in(dirname) |
492 | Dir.open(dirname) {|d| |
493 | return d.select {|ent| File.file?("#{dirname}/#{ent}") } |
494 | } |
495 | end |
496 | |
497 | REJECT_DIRS = %w( |
498 | CVS SCCS RCS CVS.adm |
499 | ) |
500 | |
501 | def all_dirs_in(dirname) |
502 | Dir.open(dirname) {|d| |
503 | return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS |
504 | } |
505 | end |
506 | |
507 | end |
508 | |
509 | # |
510 | # Main Installer |
511 | # |
512 | |
513 | class InstallError < StandardError; end |
514 | |
515 | |
516 | module HookUtils |
517 | |
518 | def run_hook(name) |
519 | try_run_hook "#{curr_srcdir()}/#{name}" or |
520 | try_run_hook "#{curr_srcdir()}/#{name}.rb" |
521 | end |
522 | |
523 | def try_run_hook(fname) |
524 | return false unless File.file?(fname) |
525 | begin |
526 | instance_eval File.read(fname), fname, 1 |
527 | rescue |
528 | raise InstallError, "hook #{fname} failed:\n" + $!.message |
529 | end |
530 | true |
531 | end |
532 | |
533 | end |
534 | |
535 | |
536 | module HookScriptAPI |
537 | |
538 | def get_config(key) |
539 | @config[key] |
540 | end |
541 | |
542 | alias config get_config |
543 | |
544 | def set_config(key, val) |
545 | @config[key] = val |
546 | end |
547 | |
548 | # |
549 | # srcdir/objdir (works only in the package directory) |
550 | # |
551 | |
552 | #abstract srcdir_root |
553 | #abstract objdir_root |
554 | #abstract relpath |
555 | |
556 | def curr_srcdir |
557 | "#{srcdir_root()}/#{relpath()}" |
558 | end |
559 | |
560 | def curr_objdir |
561 | "#{objdir_root()}/#{relpath()}" |
562 | end |
563 | |
564 | def srcfile(path) |
565 | "#{curr_srcdir()}/#{path}" |
566 | end |
567 | |
568 | def srcexist?(path) |
569 | File.exist?(srcfile(path)) |
570 | end |
571 | |
572 | def srcdirectory?(path) |
573 | File.dir?(srcfile(path)) |
574 | end |
575 | |
576 | def srcfile?(path) |
577 | File.file? srcfile(path) |
578 | end |
579 | |
580 | def srcentries(path = '.') |
581 | Dir.open("#{curr_srcdir()}/#{path}") {|d| |
582 | return d.to_a - %w(. ..) |
583 | } |
584 | end |
585 | |
586 | def srcfiles(path = '.') |
587 | srcentries(path).select {|fname| |
588 | File.file?(File.join(curr_srcdir(), path, fname)) |
589 | } |
590 | end |
591 | |
592 | def srcdirectories(path = '.') |
593 | srcentries(path).select {|fname| |
594 | File.dir?(File.join(curr_srcdir(), path, fname)) |
595 | } |
596 | end |
597 | |
598 | end |
599 | |
600 | |
601 | class ToplevelInstaller |
602 | |
603 | Version = '3.2.4' |
604 | Copyright = 'Copyright (c) 2000-2004 Minero Aoki' |
605 | |
606 | TASKS = [ |
607 | [ 'config', 'saves your configurations' ], |
608 | [ 'show', 'shows current configuration' ], |
609 | [ 'setup', 'compiles ruby extentions and others' ], |
610 | [ 'install', 'installs files' ], |
611 | [ 'clean', "does `make clean' for each extention" ], |
612 | [ 'distclean',"does `make distclean' for each extention" ] |
613 | ] |
614 | |
615 | def ToplevelInstaller.invoke |
616 | instance().invoke |
617 | end |
618 | |
619 | @singleton = nil |
620 | |
621 | def ToplevelInstaller.instance |
622 | @singleton ||= new(File.dirname($0)) |
623 | @singleton |
624 | end |
625 | |
626 | include MetaConfigAPI |
627 | |
628 | def initialize(ardir_root) |
629 | @config = nil |
630 | @options = { 'verbose' => true } |
631 | @ardir = File.expand_path(ardir_root) |
632 | end |
633 | |
634 | def inspect |
635 | "#<#{self.class} #{__id__()}>" |
636 | end |
637 | |
638 | def invoke |
639 | run_metaconfigs |
640 | task = parsearg_global() |
641 | @config = load_config(task) |
642 | __send__ "parsearg_#{task}" |
643 | init_installers |
644 | __send__ "exec_#{task}" |
645 | end |
646 | |
647 | def run_metaconfigs |
648 | eval_file_ifexist "#{@ardir}/metaconfig" |
649 | end |
650 | |
651 | def load_config(task) |
652 | case task |
653 | when 'config' |
654 | ConfigTable.new |
655 | when 'clean', 'distclean' |
656 | if File.exist?('config.save') |
657 | then ConfigTable.load |
658 | else ConfigTable.new |
659 | end |
660 | else |
661 | ConfigTable.load |
662 | end |
663 | end |
664 | |
665 | def init_installers |
666 | @installer = Installer.new(@config, @options, @ardir, File.expand_path('.')) |
667 | end |
668 | |
669 | # |
670 | # Hook Script API bases |
671 | # |
672 | |
673 | def srcdir_root |
674 | @ardir |
675 | end |
676 | |
677 | def objdir_root |
678 | '.' |
679 | end |
680 | |
681 | def relpath |
682 | '.' |
683 | end |
684 | |
685 | # |
686 | # Option Parsing |
687 | # |
688 | |
689 | def parsearg_global |
690 | valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/ |
691 | |
692 | while arg = ARGV.shift |
693 | case arg |
694 | when /\A\w+\z/ |
695 | raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg |
696 | return arg |
697 | |
698 | when '-q', '--quiet' |
699 | @options['verbose'] = false |
700 | |
701 | when '--verbose' |
702 | @options['verbose'] = true |
703 | |
704 | when '-h', '--help' |
705 | print_usage $stdout |
706 | exit 0 |
707 | |
708 | when '-v', '--version' |
709 | puts "#{File.basename($0)} version #{Version}" |
710 | exit 0 |
711 | |
712 | when '--copyright' |
713 | puts Copyright |
714 | exit 0 |
715 | |
716 | else |
717 | raise InstallError, "unknown global option '#{arg}'" |
718 | end |
719 | end |
720 | |
721 | raise InstallError, <<EOS |
722 | No task or global option given. |
723 | Typical installation procedure is: |
724 | $ ruby #{File.basename($0)} config |
725 | $ ruby #{File.basename($0)} setup |
726 | # ruby #{File.basename($0)} install (may require root privilege) |
727 | EOS |
728 | end |
729 | |
730 | |
731 | def parsearg_no_options |
732 | raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\ |
733 | unless ARGV.empty? |
734 | end |
735 | |
736 | alias parsearg_show parsearg_no_options |
737 | alias parsearg_setup parsearg_no_options |
738 | alias parsearg_clean parsearg_no_options |
739 | alias parsearg_distclean parsearg_no_options |
740 | |
741 | def parsearg_config |
742 | re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/ |
743 | @options['config-opt'] = [] |
744 | |
745 | while i = ARGV.shift |
746 | if /\A--?\z/ =~ i |
747 | @options['config-opt'] = ARGV.dup |
748 | break |
749 | end |
750 | m = re.match(i) or raise InstallError, "config: unknown option #{i}" |
751 | name, value = m.to_a[1,2] |
752 | if value |
753 | if ConfigTable.bool_config?(name) |
754 | raise InstallError, "config: --#{name} allows only yes/no for argument"\ |
755 | unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value |
756 | value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no' |
757 | end |
758 | else |
759 | raise InstallError, "config: --#{name} requires argument"\ |
760 | unless ConfigTable.bool_config?(name) |
761 | value = 'yes' |
762 | end |
763 | @config[name] = value |
764 | end |
765 | end |
766 | |
767 | def parsearg_install |
768 | @options['no-harm'] = false |
769 | @options['install-prefix'] = '' |
770 | while a = ARGV.shift |
771 | case a |
772 | when /\A--no-harm\z/ |
773 | @options['no-harm'] = true |
774 | when /\A--prefix=(.*)\z/ |
775 | path = $1 |
776 | path = File.expand_path(path) unless path[0,1] == '/' |
777 | @options['install-prefix'] = path |
778 | else |
779 | raise InstallError, "install: unknown option #{a}" |
780 | end |
781 | end |
782 | end |
783 | |
784 | def print_usage(out) |
785 | out.puts 'Typical Installation Procedure:' |
786 | out.puts " $ ruby #{File.basename $0} config" |
787 | out.puts " $ ruby #{File.basename $0} setup" |
788 | out.puts " # ruby #{File.basename $0} install (may require root privilege)" |
789 | out.puts |
790 | out.puts 'Detailed Usage:' |
791 | out.puts " ruby #{File.basename $0} <global option>" |
792 | out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]" |
793 | |
794 | fmt = " %-20s %s\n" |
795 | out.puts |
796 | out.puts 'Global options:' |
797 | out.printf fmt, '-q,--quiet', 'suppress message outputs' |
798 | out.printf fmt, ' --verbose', 'output messages verbosely' |
799 | out.printf fmt, '-h,--help', 'print this message' |
800 | out.printf fmt, '-v,--version', 'print version and quit' |
801 | out.printf fmt, ' --copyright', 'print copyright and quit' |
802 | |
803 | out.puts |
804 | out.puts 'Tasks:' |
805 | TASKS.each do |name, desc| |
806 | out.printf " %-10s %s\n", name, desc |
807 | end |
808 | |
809 | out.puts |
810 | out.puts 'Options for config:' |
811 | ConfigTable.each_definition do |name, (default, arg, desc, default2)| |
812 | out.printf " %-20s %s [%s]\n", |
813 | '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg), |
814 | desc, |
815 | default2 || default |
816 | end |
817 | out.printf " %-20s %s [%s]\n", |
818 | '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's" |
819 | |
820 | out.puts |
821 | out.puts 'Options for install:' |
822 | out.printf " %-20s %s [%s]\n", |
823 | '--no-harm', 'only display what to do if given', 'off' |
824 | out.printf " %-20s %s [%s]\n", |
825 | '--prefix', 'install path prefix', '$prefix' |
826 | |
827 | out.puts |
828 | end |
829 | |
830 | # |
831 | # Task Handlers |
832 | # |
833 | |
834 | def exec_config |
835 | @installer.exec_config |
836 | @config.save # must be final |
837 | end |
838 | |
839 | def exec_setup |
840 | @installer.exec_setup |
841 | end |
842 | |
843 | def exec_install |
844 | @installer.exec_install |
845 | end |
846 | |
847 | def exec_show |
848 | ConfigTable.each_name do |k| |
849 | v = @config.get_raw(k) |
850 | if not v or v.empty? |
851 | v = '(not specified)' |
852 | end |
853 | printf "%-10s %s\n", k, v |
854 | end |
855 | end |
856 | |
857 | def exec_clean |
858 | @installer.exec_clean |
859 | end |
860 | |
861 | def exec_distclean |
862 | @installer.exec_distclean |
863 | end |
864 | |
865 | end |
866 | |
867 | |
868 | class ToplevelInstallerMulti < ToplevelInstaller |
869 | |
870 | include HookUtils |
871 | include HookScriptAPI |
872 | include FileOperations |
873 | |
874 | def initialize(ardir) |
875 | super |
876 | @packages = all_dirs_in("#{@ardir}/packages") |
877 | raise 'no package exists' if @packages.empty? |
878 | end |
879 | |
880 | def run_metaconfigs |
881 | eval_file_ifexist "#{@ardir}/metaconfig" |
882 | @packages.each do |name| |
883 | eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig" |
884 | end |
885 | end |
886 | |
887 | def init_installers |
888 | @installers = {} |
889 | @packages.each do |pack| |
890 | @installers[pack] = Installer.new(@config, @options, |
891 | "#{@ardir}/packages/#{pack}", |
892 | "packages/#{pack}") |
893 | end |
894 | |
895 | with = extract_selection(config('with')) |
896 | without = extract_selection(config('without')) |
897 | @selected = @installers.keys.select {|name| |
898 | (with.empty? or with.include?(name)) \ |
899 | and not without.include?(name) |
900 | } |
901 | end |
902 | |
903 | def extract_selection(list) |
904 | a = list.split(/,/) |
905 | a.each do |name| |
906 | raise InstallError, "no such package: #{name}" \ |
907 | unless @installers.key?(name) |
908 | end |
909 | a |
910 | end |
911 | |
912 | def print_usage(f) |
913 | super |
914 | f.puts 'Inluded packages:' |
915 | f.puts ' ' + @packages.sort.join(' ') |
916 | f.puts |
917 | end |
918 | |
919 | # |
920 | # multi-package metaconfig API |
921 | # |
922 | |
923 | attr_reader :packages |
924 | |
925 | def declare_packages(list) |
926 | raise 'package list is empty' if list.empty? |
927 | list.each do |name| |
928 | raise "directory packages/#{name} does not exist"\ |
929 | unless File.dir?("#{@ardir}/packages/#{name}") |
930 | end |
931 | @packages = list |
932 | end |
933 | |
934 | # |
935 | # Task Handlers |
936 | # |
937 | |
938 | def exec_config |
939 | run_hook 'pre-config' |
940 | each_selected_installers {|inst| inst.exec_config } |
941 | run_hook 'post-config' |
942 | @config.save # must be final |
943 | end |
944 | |
945 | def exec_setup |
946 | run_hook 'pre-setup' |
947 | each_selected_installers {|inst| inst.exec_setup } |
948 | run_hook 'post-setup' |
949 | end |
950 | |
951 | def exec_install |
952 | run_hook 'pre-install' |
953 | each_selected_installers {|inst| inst.exec_install } |
954 | run_hook 'post-install' |
955 | end |
956 | |
957 | def exec_clean |
958 | rm_f 'config.save' |
959 | run_hook 'pre-clean' |
960 | each_selected_installers {|inst| inst.exec_clean } |
961 | run_hook 'post-clean' |
962 | end |
963 | |
964 | def exec_distclean |
965 | rm_f 'config.save' |
966 | run_hook 'pre-distclean' |
967 | each_selected_installers {|inst| inst.exec_distclean } |
968 | run_hook 'post-distclean' |
969 | end |
970 | |
971 | # |
972 | # lib |
973 | # |
974 | |
975 | def each_selected_installers |
976 | Dir.mkdir 'packages' unless File.dir?('packages') |
977 | @selected.each do |pack| |
978 | $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose'] |
979 | Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") |
980 | Dir.chdir "packages/#{pack}" |
981 | yield @installers[pack] |
982 | Dir.chdir '../..' |
983 | end |
984 | end |
985 | |
986 | def verbose? |
987 | @options['verbose'] |
988 | end |
989 | |
990 | def no_harm? |
991 | @options['no-harm'] |
992 | end |
993 | |
994 | end |
995 | |
996 | |
997 | class Installer |
998 | |
999 | FILETYPES = %w( bin lib ext data ) |
1000 | |
1001 | include HookScriptAPI |
1002 | include HookUtils |
1003 | include FileOperations |
1004 | |
1005 | def initialize(config, opt, srcroot, objroot) |
1006 | @config = config |
1007 | @options = opt |
1008 | @srcdir = File.expand_path(srcroot) |
1009 | @objdir = File.expand_path(objroot) |
1010 | @currdir = '.' |
1011 | end |
1012 | |
1013 | def inspect |
1014 | "#<#{self.class} #{File.basename(@srcdir)}>" |
1015 | end |
1016 | |
1017 | # |
1018 | # Hook Script API bases |
1019 | # |
1020 | |
1021 | def srcdir_root |
1022 | @srcdir |
1023 | end |
1024 | |
1025 | def objdir_root |
1026 | @objdir |
1027 | end |
1028 | |
1029 | def relpath |
1030 | @currdir |
1031 | end |
1032 | |
1033 | # |
1034 | # configs/options |
1035 | # |
1036 | |
1037 | def no_harm? |
1038 | @options['no-harm'] |
1039 | end |
1040 | |
1041 | def verbose? |
1042 | @options['verbose'] |
1043 | end |
1044 | |
1045 | def verbose_off |
1046 | begin |
1047 | save, @options['verbose'] = @options['verbose'], false |
1048 | yield |
1049 | ensure |
1050 | @options['verbose'] = save |
1051 | end |
1052 | end |
1053 | |
1054 | # |
1055 | # TASK config |
1056 | # |
1057 | |
1058 | def exec_config |
1059 | exec_task_traverse 'config' |
1060 | end |
1061 | |
1062 | def config_dir_bin(rel) |
1063 | end |
1064 | |
1065 | def config_dir_lib(rel) |
1066 | end |
1067 | |
1068 | def config_dir_ext(rel) |
1069 | extconf if extdir?(curr_srcdir()) |
1070 | end |
1071 | |
1072 | def extconf |
1073 | opt = @options['config-opt'].join(' ') |
1074 | command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}" |
1075 | end |
1076 | |
1077 | def config_dir_data(rel) |
1078 | end |
1079 | |
1080 | # |
1081 | # TASK setup |
1082 | # |
1083 | |
1084 | def exec_setup |
1085 | exec_task_traverse 'setup' |
1086 | end |
1087 | |
1088 | def setup_dir_bin(rel) |
1089 | all_files_in(curr_srcdir()).each do |fname| |
1090 | adjust_shebang "#{curr_srcdir()}/#{fname}" |
1091 | end |
1092 | end |
1093 | |
1094 | # modify: #!/usr/bin/ruby |
1095 | # modify: #! /usr/bin/ruby |
1096 | # modify: #!ruby |
1097 | # not modify: #!/usr/bin/env ruby |
1098 | SHEBANG_RE = /\A\#!\s*\S*ruby\S*/ |
1099 | |
1100 | def adjust_shebang(path) |
1101 | return if no_harm? |
1102 | |
1103 | tmpfile = File.basename(path) + '.tmp' |
1104 | begin |
1105 | File.open(path, 'rb') {|r| |
1106 | File.open(tmpfile, 'wb') {|w| |
1107 | first = r.gets |
1108 | return unless SHEBANG_RE =~ first |
1109 | |
1110 | $stderr.puts "adjusting shebang: #{File.basename path}" if verbose? |
1111 | w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path')) |
1112 | w.write r.read |
1113 | } |
1114 | } |
1115 | move_file tmpfile, File.basename(path) |
1116 | ensure |
1117 | File.unlink tmpfile if File.exist?(tmpfile) |
1118 | end |
1119 | end |
1120 | |
1121 | def setup_dir_lib(rel) |
1122 | end |
1123 | |
1124 | def setup_dir_ext(rel) |
1125 | make if extdir?(curr_srcdir()) |
1126 | end |
1127 | |
1128 | def setup_dir_data(rel) |
1129 | end |
1130 | |
1131 | # |
1132 | # TASK install |
1133 | # |
1134 | |
1135 | def exec_install |
1136 | exec_task_traverse 'install' |
1137 | end |
1138 | |
1139 | def install_dir_bin(rel) |
1140 | install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755 |
1141 | end |
1142 | |
1143 | def install_dir_lib(rel) |
1144 | install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644 |
1145 | end |
1146 | |
1147 | def install_dir_ext(rel) |
1148 | return unless extdir?(curr_srcdir()) |
1149 | install_files ruby_extentions('.'), |
1150 | "#{config('so-dir')}/#{File.dirname(rel)}", |
1151 | 0555 |
1152 | end |
1153 | |
1154 | def install_dir_data(rel) |
1155 | install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644 |
1156 | end |
1157 | |
1158 | def install_files(list, dest, mode) |
1159 | mkdir_p dest, @options['install-prefix'] |
1160 | list.each do |fname| |
1161 | install fname, dest, mode, @options['install-prefix'] |
1162 | end |
1163 | end |
1164 | |
1165 | def ruby_scripts |
1166 | collect_filenames_auto().select {|n| /\.rb\z/ =~ n } |
1167 | end |
1168 | |
1169 | # picked up many entries from cvs-1.11.1/src/ignore.c |
1170 | reject_patterns = %w( |
1171 | core RCSLOG tags TAGS .make.state |
1172 | .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb |
1173 | *~ *.old *.bak *.BAK *.orig *.rej _$* *$ |
1174 | |
1175 | *.org *.in .* |
1176 | ) |
1177 | mapping = { |
1178 | '.' => '\.', |
1179 | '$' => '\$', |
1180 | '#' => '\#', |
1181 | '*' => '.*' |
1182 | } |
1183 | REJECT_PATTERNS = Regexp.new('\A(?:' + |
1184 | reject_patterns.map {|pat| |
1185 | pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] } |
1186 | }.join('|') + |
1187 | ')\z') |
1188 | |
1189 | def collect_filenames_auto |
1190 | mapdir((existfiles() - hookfiles()).reject {|fname| |
1191 | REJECT_PATTERNS =~ fname |
1192 | }) |
1193 | end |
1194 | |
1195 | def existfiles |
1196 | all_files_in(curr_srcdir()) | all_files_in('.') |
1197 | end |
1198 | |
1199 | def hookfiles |
1200 | %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| |
1201 | %w( config setup install clean ).map {|t| sprintf(fmt, t) } |
1202 | }.flatten |
1203 | end |
1204 | |
1205 | def mapdir(filelist) |
1206 | filelist.map {|fname| |
1207 | if File.exist?(fname) # objdir |
1208 | fname |
1209 | else # srcdir |
1210 | File.join(curr_srcdir(), fname) |
1211 | end |
1212 | } |
1213 | end |
1214 | |
1215 | def ruby_extentions(dir) |
1216 | _ruby_extentions(dir) or |
1217 | raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first" |
1218 | end |
1219 | |
1220 | DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/ |
1221 | |
1222 | def _ruby_extentions(dir) |
1223 | Dir.open(dir) {|d| |
1224 | return d.select {|fname| DLEXT =~ fname } |
1225 | } |
1226 | end |
1227 | |
1228 | # |
1229 | # TASK clean |
1230 | # |
1231 | |
1232 | def exec_clean |
1233 | exec_task_traverse 'clean' |
1234 | rm_f 'config.save' |
1235 | rm_f 'InstalledFiles' |
1236 | end |
1237 | |
1238 | def clean_dir_bin(rel) |
1239 | end |
1240 | |
1241 | def clean_dir_lib(rel) |
1242 | end |
1243 | |
1244 | def clean_dir_ext(rel) |
1245 | return unless extdir?(curr_srcdir()) |
1246 | make 'clean' if File.file?('Makefile') |
1247 | end |
1248 | |
1249 | def clean_dir_data(rel) |
1250 | end |
1251 | |
1252 | # |
1253 | # TASK distclean |
1254 | # |
1255 | |
1256 | def exec_distclean |
1257 | exec_task_traverse 'distclean' |
1258 | rm_f 'config.save' |
1259 | rm_f 'InstalledFiles' |
1260 | end |
1261 | |
1262 | def distclean_dir_bin(rel) |
1263 | end |
1264 | |
1265 | def distclean_dir_lib(rel) |
1266 | end |
1267 | |
1268 | def distclean_dir_ext(rel) |
1269 | return unless extdir?(curr_srcdir()) |
1270 | make 'distclean' if File.file?('Makefile') |
1271 | end |
1272 | |
1273 | # |
1274 | # lib |
1275 | # |
1276 | |
1277 | def exec_task_traverse(task) |
1278 | run_hook "pre-#{task}" |
1279 | FILETYPES.each do |type| |
1280 | if config('without-ext') == 'yes' and type == 'ext' |
1281 | $stderr.puts 'skipping ext/* by user option' if verbose? |
1282 | next |
1283 | end |
1284 | traverse task, type, "#{task}_dir_#{type}" |
1285 | end |
1286 | run_hook "post-#{task}" |
1287 | end |
1288 | |
1289 | def traverse(task, rel, mid) |
1290 | dive_into(rel) { |
1291 | run_hook "pre-#{task}" |
1292 | __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') |
1293 | all_dirs_in(curr_srcdir()).each do |d| |
1294 | traverse task, "#{rel}/#{d}", mid |
1295 | end |
1296 | run_hook "post-#{task}" |
1297 | } |
1298 | end |
1299 | |
1300 | def dive_into(rel) |
1301 | return unless File.dir?("#{@srcdir}/#{rel}") |
1302 | |
1303 | dir = File.basename(rel) |
1304 | Dir.mkdir dir unless File.dir?(dir) |
1305 | prevdir = Dir.pwd |
1306 | Dir.chdir dir |
1307 | $stderr.puts '---> ' + rel if verbose? |
1308 | @currdir = rel |
1309 | yield |
1310 | Dir.chdir prevdir |
1311 | $stderr.puts '<--- ' + rel if verbose? |
1312 | @currdir = File.dirname(rel) |
1313 | end |
1314 | |
1315 | end |
1316 | |
1317 | |
1318 | if $0 == __FILE__ |
1319 | begin |
1320 | if multipackage_install? |
1321 | ToplevelInstallerMulti.invoke |
1322 | else |
1323 | ToplevelInstaller.invoke |
1324 | end |
1325 | rescue |
1326 | raise if $DEBUG |
1327 | $stderr.puts $!.message |
1328 | $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." |
1329 | exit 1 |
1330 | end |
1331 | end |