langscan 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. data/AUTHORS.txt +19 -0
  2. data/History.txt +126 -0
  3. data/Manifest.txt +167 -0
  4. data/README.rdoc +89 -0
  5. data/Rakefile +40 -0
  6. data/ext/langscan/_make_c.rb +20 -0
  7. data/ext/langscan/_make_h.rb +30 -0
  8. data/ext/langscan/_template.c +134 -0
  9. data/ext/langscan/_template.h +53 -0
  10. data/ext/langscan/c/c/Makefile +157 -0
  11. data/ext/langscan/c/c/c.c +134 -0
  12. data/ext/langscan/c/c/c.h +66 -0
  13. data/ext/langscan/c/c/ctok.c +4622 -0
  14. data/ext/langscan/c/c/ctok.l +212 -0
  15. data/ext/langscan/c/c/extconf.rb +3 -0
  16. data/ext/langscan/c/c/modulename.txt +1 -0
  17. data/ext/langscan/c/c/tokenlist.txt +13 -0
  18. data/ext/langscan/csharp/csharp/Makefile +157 -0
  19. data/ext/langscan/csharp/csharp/csharp.c +134 -0
  20. data/ext/langscan/csharp/csharp/csharp.h +65 -0
  21. data/ext/langscan/csharp/csharp/csharptok.c +2965 -0
  22. data/ext/langscan/csharp/csharp/csharptok.l +200 -0
  23. data/ext/langscan/csharp/csharp/extconf.rb +3 -0
  24. data/ext/langscan/csharp/csharp/modulename.txt +1 -0
  25. data/ext/langscan/csharp/csharp/tokenlist.txt +12 -0
  26. data/ext/langscan/d/d/Makefile +157 -0
  27. data/ext/langscan/d/d/d.c +134 -0
  28. data/ext/langscan/d/d/d.h +64 -0
  29. data/ext/langscan/d/d/dtok.c +5461 -0
  30. data/ext/langscan/d/d/dtok.l +282 -0
  31. data/ext/langscan/d/d/extconf.rb +3 -0
  32. data/ext/langscan/d/d/modulename.txt +1 -0
  33. data/ext/langscan/d/d/tokenlist.txt +11 -0
  34. data/ext/langscan/elisp/elisp/Makefile +157 -0
  35. data/ext/langscan/elisp/elisp/elisp.c +134 -0
  36. data/ext/langscan/elisp/elisp/elisp.h +62 -0
  37. data/ext/langscan/elisp/elisp/elisptok.c +2101 -0
  38. data/ext/langscan/elisp/elisp/elisptok.l +151 -0
  39. data/ext/langscan/elisp/elisp/extconf.rb +3 -0
  40. data/ext/langscan/elisp/elisp/modulename.txt +1 -0
  41. data/ext/langscan/elisp/elisp/tokenlist.txt +9 -0
  42. data/ext/langscan/java/java/Makefile +157 -0
  43. data/ext/langscan/java/java/extconf.rb +3 -0
  44. data/ext/langscan/java/java/java.c +134 -0
  45. data/ext/langscan/java/java/java.h +64 -0
  46. data/ext/langscan/java/java/javatok.c +2090 -0
  47. data/ext/langscan/java/java/javatok.l +155 -0
  48. data/ext/langscan/java/java/modulename.txt +1 -0
  49. data/ext/langscan/java/java/tokenlist.txt +11 -0
  50. data/ext/langscan/javascript/javascript/Makefile +157 -0
  51. data/ext/langscan/javascript/javascript/extconf.rb +3 -0
  52. data/ext/langscan/javascript/javascript/javascript.c +134 -0
  53. data/ext/langscan/javascript/javascript/javascript.h +63 -0
  54. data/ext/langscan/javascript/javascript/javascripttok.c +2051 -0
  55. data/ext/langscan/javascript/javascript/javascripttok.l +147 -0
  56. data/ext/langscan/javascript/javascript/modulename.txt +1 -0
  57. data/ext/langscan/javascript/javascript/tokenlist.txt +10 -0
  58. data/ext/langscan/pairmatcher/pairmatcher/Makefile +157 -0
  59. data/ext/langscan/pairmatcher/pairmatcher/extconf.rb +3 -0
  60. data/ext/langscan/pairmatcher/pairmatcher/pairmatcher.c +890 -0
  61. data/ext/langscan/php/php/Makefile +157 -0
  62. data/ext/langscan/php/php/extconf.rb +3 -0
  63. data/ext/langscan/php/php/modulename.txt +1 -0
  64. data/ext/langscan/php/php/php.c +134 -0
  65. data/ext/langscan/php/php/php.h +64 -0
  66. data/ext/langscan/php/php/phptok.c +2406 -0
  67. data/ext/langscan/php/php/phptok.l +212 -0
  68. data/ext/langscan/php/php/tokenlist.txt +11 -0
  69. data/ext/langscan/post-distclean.rb +21 -0
  70. data/ext/langscan/pre-config.rb +57 -0
  71. data/ext/langscan/python/python/Makefile +157 -0
  72. data/ext/langscan/python/python/extconf.rb +3 -0
  73. data/ext/langscan/python/python/modulename.txt +1 -0
  74. data/ext/langscan/python/python/python.c +134 -0
  75. data/ext/langscan/python/python/python.h +61 -0
  76. data/ext/langscan/python/python/pythontok.c +2102 -0
  77. data/ext/langscan/python/python/pythontok.l +155 -0
  78. data/ext/langscan/python/python/tokenlist.txt +8 -0
  79. data/ext/langscan/ruby/compat/ripper/Makefile +158 -0
  80. data/ext/langscan/ruby/compat/ripper/depend +1 -0
  81. data/ext/langscan/ruby/compat/ripper/extconf.rb +4 -0
  82. data/ext/langscan/ruby/compat/ripper/include/eventids1.c +251 -0
  83. data/ext/langscan/ruby/compat/ripper/include/eventids2.c +277 -0
  84. data/ext/langscan/ruby/compat/ripper/include/lex.c +138 -0
  85. data/ext/langscan/ruby/compat/ripper/ripper.c +14420 -0
  86. data/ext/langscan/scheme/scheme/Makefile +157 -0
  87. data/ext/langscan/scheme/scheme/extconf.rb +3 -0
  88. data/ext/langscan/scheme/scheme/modulename.txt +1 -0
  89. data/ext/langscan/scheme/scheme/scheme.c +134 -0
  90. data/ext/langscan/scheme/scheme/scheme.h +60 -0
  91. data/ext/langscan/scheme/scheme/schemetok.c +2447 -0
  92. data/ext/langscan/scheme/scheme/schemetok.l +177 -0
  93. data/ext/langscan/scheme/scheme/tokenlist.txt +7 -0
  94. data/ext/langscan/sh/sh/Makefile +157 -0
  95. data/ext/langscan/sh/sh/extconf.rb +3 -0
  96. data/ext/langscan/sh/sh/modulename.txt +1 -0
  97. data/ext/langscan/sh/sh/sh.c +134 -0
  98. data/ext/langscan/sh/sh/sh.h +61 -0
  99. data/ext/langscan/sh/sh/shtok.c +2470 -0
  100. data/ext/langscan/sh/sh/shtok.l +325 -0
  101. data/ext/langscan/sh/sh/tokenlist.txt +8 -0
  102. data/lib/langscan.rb +124 -0
  103. data/lib/langscan/_common.rb +50 -0
  104. data/lib/langscan/_easyscanner.rb +78 -0
  105. data/lib/langscan/_pairmatcher.rb +46 -0
  106. data/lib/langscan/_type.rb +125 -0
  107. data/lib/langscan/autoconf.rb +51 -0
  108. data/lib/langscan/automake.rb +51 -0
  109. data/lib/langscan/brainfuck.rb +48 -0
  110. data/lib/langscan/c.rb +144 -0
  111. data/lib/langscan/csharp.rb +101 -0
  112. data/lib/langscan/css.rb +109 -0
  113. data/lib/langscan/d.rb +201 -0
  114. data/lib/langscan/eiffel.rb +167 -0
  115. data/lib/langscan/elisp.rb +132 -0
  116. data/lib/langscan/io.rb +84 -0
  117. data/lib/langscan/java.rb +95 -0
  118. data/lib/langscan/javascript.rb +97 -0
  119. data/lib/langscan/lua.rb +116 -0
  120. data/lib/langscan/ocaml.rb +298 -0
  121. data/lib/langscan/ocaml/camlexer.ml +28 -0
  122. data/lib/langscan/ocaml/lexer.mll +230 -0
  123. data/lib/langscan/ocaml/types.ml +36 -0
  124. data/lib/langscan/perl.rb +87 -0
  125. data/lib/langscan/perl/tokenizer.pl +231 -0
  126. data/lib/langscan/php.rb +80 -0
  127. data/lib/langscan/python.rb +101 -0
  128. data/lib/langscan/rpmspec.rb +71 -0
  129. data/lib/langscan/ruby.rb +164 -0
  130. data/lib/langscan/ruby/compat/README +5 -0
  131. data/lib/langscan/ruby/compat/ripper.rb +4 -0
  132. data/lib/langscan/ruby/compat/ripper/core.rb +918 -0
  133. data/lib/langscan/ruby/compat/ripper/filter.rb +70 -0
  134. data/lib/langscan/ruby/compat/ripper/lexer.rb +179 -0
  135. data/lib/langscan/ruby/compat/ripper/sexp.rb +100 -0
  136. data/lib/langscan/scheme.rb +160 -0
  137. data/lib/langscan/sh.rb +116 -0
  138. data/lib/langscan/text.rb +37 -0
  139. data/metaconfig +2 -0
  140. data/script/console +10 -0
  141. data/script/destroy +14 -0
  142. data/script/generate +14 -0
  143. data/script/makemanifest.rb +21 -0
  144. data/setup.rb +1604 -0
  145. data/tasks/extconf.rake +13 -0
  146. data/tasks/extconf/langscan.rake +42 -0
  147. data/test/langscan/brainfuck/test/test_scan.rb +55 -0
  148. data/test/langscan/c/test/test_scan.rb +216 -0
  149. data/test/langscan/c/test/test_token.rb +41 -0
  150. data/test/langscan/csharp/test/test_scan.rb +157 -0
  151. data/test/langscan/css/test/test_css.rb +79 -0
  152. data/test/langscan/d/test/test_scan.rb +233 -0
  153. data/test/langscan/d/test/test_token.rb +205 -0
  154. data/test/langscan/eiffel/test/test_eiffel.rb +95 -0
  155. data/test/langscan/elisp/test/test_elisp.rb +177 -0
  156. data/test/langscan/io/test/test_io.rb +79 -0
  157. data/test/langscan/java/test/test_java.rb +74 -0
  158. data/test/langscan/javascript/test/test_javascript.rb +39 -0
  159. data/test/langscan/lua/test/test_lua.rb +69 -0
  160. data/test/langscan/ocaml/test/test_ocaml.rb +161 -0
  161. data/test/langscan/php/test/test_scan.rb +138 -0
  162. data/test/langscan/python/test/test_scan.rb +105 -0
  163. data/test/langscan/rpmspec/test/test_rpmspec.rb +51 -0
  164. data/test/langscan/ruby/test/test_scan.rb +71 -0
  165. data/test/langscan/scheme/test/test_scan.rb +198 -0
  166. data/test/test_helper.rb +7 -0
  167. data/test/test_langscan.rb +123 -0
  168. metadata +296 -0
@@ -0,0 +1,116 @@
1
+ #
2
+ # sh.rb - a shellscript module of LangScan
3
+ #
4
+ # Copyright (C) 2005 Kenichi Ishibashi <bashi at dream.ie.ariake-nct.ac.jp>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ require 'langscan/sh/sh'
13
+ require 'langscan/_common'
14
+
15
+ module LangScan
16
+ module Shell
17
+ module_function
18
+ def name
19
+ "shell script"
20
+ end
21
+
22
+ def abbrev
23
+ "sh"
24
+ end
25
+
26
+ def extnames
27
+ [".sh"]
28
+ end
29
+
30
+ # LangScan::Shell.scan iterates over shell scripts.
31
+ # It yields for each Fragment.
32
+ def scan(input, &block)
33
+ last_token = nil
34
+ each_fragment(input) {|t|
35
+ if t.type == :ident
36
+ if t.text[0] == ?$
37
+ yield Fragment.new(:punct, '$', t.beg_lineno, t.beg_byteno)
38
+ yield Fragment.new(:ident, t.text[1 .. -1], t.beg_lineno,
39
+ t.beg_byteno + 1)
40
+ last_token = nil
41
+ next
42
+ end
43
+
44
+ if last_token != nil && last_token.type == :keyword && last_token.text == "function"
45
+ t.type = :fundef
46
+ end
47
+ end
48
+
49
+ if t.type == :heredoc_end
50
+ blank_len = t.text.rindex(/\s/)
51
+ if blank_len == nil
52
+ yield Fragment.new(:ident, t.text, t.beg_lineno, t.beg_byteno)
53
+ else
54
+ yield Fragment.new(:space, t.text[0 .. blank_len],
55
+ t.beg_lineno, t.beg_byteno)
56
+ yield Fragment.new(:ident, t.text[blank_len+1 .. -1],
57
+ t.beg_lineno, t.beg_byteno+blank_len+1)
58
+ end
59
+ next
60
+ end
61
+
62
+ t.type = :ident if t.type == :heredoc_beg
63
+
64
+ yield t
65
+
66
+ if t.type != :space && t.type != :comment
67
+ last_token = t
68
+ end
69
+ }
70
+ end
71
+
72
+ # LangScan::Shell.each_fragment iterates over shell script fragments in _input_.
73
+ # The fragments contains tokens and inter-token spaces and comments.
74
+ #
75
+ # If a String is specified, the String itself is assumed as a shell script.
76
+ # If a IO is specified, the content of the IO is assumed as a shell script.
77
+ def each_fragment(input) # :yields: token
78
+ begin
79
+ tokenizer = Tokenizer.new(input)
80
+ while token_info = tokenizer.get_token
81
+ type, text, beg_lineno, beg_columnno, beg_byteno, end_lineno, end_columnno, end_byteno = token_info
82
+ token = Fragment.new(type, text, beg_lineno, beg_byteno)
83
+ if token.type == :ident
84
+ if KeywordsHash[token.text]
85
+ token.type = :keyword
86
+ end
87
+ end
88
+
89
+ yield token
90
+ end
91
+ ensure
92
+ tokenizer.close
93
+ end
94
+ end
95
+
96
+ Keywords = %w(
97
+ case do done elif else esac fi for function if in select then until
98
+ while time
99
+ )
100
+ ShBuiltinCommands = %w(
101
+ break cd continue eval exec exit export getopts hash pwd readonly
102
+ return shift test times trap umask unset
103
+ )
104
+ BashBuiltinCommands = %w(
105
+ alias bind builtin caller command declare echo enable help let
106
+ local logout printf read shopt set source type typeset ulimit unalias
107
+ )
108
+
109
+ KeywordsHash = {}
110
+ Keywords.each {|k| KeywordsHash[k] = k }
111
+ ShBuiltinCommands.each {|k| KeywordsHash[k] = k }
112
+ BashBuiltinCommands.each {|k| KeywordsHash[k] = k }
113
+
114
+ LangScan.register(self)
115
+ end
116
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # text.rb - a text module of LangScan
3
+ #
4
+ # Copyright (C) 2004-2005 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the GNU General Public License version 2.
10
+ #
11
+
12
+ require 'langscan/_common'
13
+
14
+ module LangScan
15
+ module Text
16
+ module_function
17
+ def name
18
+ "Text"
19
+ end
20
+
21
+ def abbrev
22
+ "text"
23
+ end
24
+
25
+ def extnames
26
+ [".txt"]
27
+ end
28
+
29
+ # LangScan::Text.scan iterates over text file.
30
+ # It yields for each Fragment.
31
+ def scan(input, &block)
32
+ yield(Fragment.new(:text, input, 1, 0))
33
+ end
34
+
35
+ LangScan.register(self)
36
+ end
37
+ end
data/metaconfig ADDED
@@ -0,0 +1,2 @@
1
+ add_bool_config 'nounistd', false, 'do not include <unistd.h> for flex'
2
+ add_bool_config 'winstandalone', false, ''
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/langscan.rb'}"
9
+ puts "Loading langscan gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/ruby
2
+ # original: http://d.hatena.ne.jp/bellbind/20070605/1180979599
3
+
4
+ base_dir = File.join(File.dirname(__FILE__), "..")
5
+ glob_pattern = File.join("**", "*")
6
+ exclude_patterns = [
7
+ /^pkg/,/^doc/,
8
+ ]
9
+
10
+ Dir.chdir(base_dir)
11
+ files = Dir.glob(glob_pattern).delete_if do |fname|
12
+ File.directory?(fname) or
13
+ exclude_patterns.find do |pattern|
14
+ pattern =~ fname
15
+ end
16
+ end
17
+ manifest = File.new("Manifest.txt", "w")
18
+ manifest.puts files.sort.join("\n")
19
+ manifest.close
20
+
21
+ puts "Manifest.txt updated"
data/setup.rb ADDED
@@ -0,0 +1,1604 @@
1
+ #
2
+ # setup.rb
3
+ #
4
+ # Copyright (c) 2000-2006 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the terms of
8
+ # the GNU LGPL, Lesser General Public License version 2.1.
9
+ #
10
+
11
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12
+ module Enumerable
13
+ alias map collect
14
+ end
15
+ end
16
+
17
+ unless File.respond_to?(:read) # Ruby 1.6
18
+ def File.read(fname)
19
+ open(fname) {|f|
20
+ return f.read
21
+ }
22
+ end
23
+ end
24
+
25
+ unless Errno.const_defined?(:ENOTEMPTY) # Windows?
26
+ module Errno
27
+ class ENOTEMPTY
28
+ # We do not raise this exception, implementation is not needed.
29
+ end
30
+ end
31
+ end
32
+
33
+ def File.binread(fname)
34
+ open(fname, 'rb') {|f|
35
+ return f.read
36
+ }
37
+ end
38
+
39
+ # for corrupted Windows' stat(2)
40
+ def File.dir?(path)
41
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
42
+ end
43
+
44
+
45
+ class ConfigTable
46
+
47
+ include Enumerable
48
+
49
+ def initialize(rbconfig)
50
+ @rbconfig = rbconfig
51
+ @items = []
52
+ @table = {}
53
+ # options
54
+ @install_prefix = nil
55
+ @config_opt = nil
56
+ @verbose = true
57
+ @no_harm = false
58
+ end
59
+
60
+ attr_accessor :install_prefix
61
+ attr_accessor :config_opt
62
+
63
+ attr_writer :verbose
64
+
65
+ def verbose?
66
+ @verbose
67
+ end
68
+
69
+ attr_writer :no_harm
70
+
71
+ def no_harm?
72
+ @no_harm
73
+ end
74
+
75
+ def [](key)
76
+ lookup(key).resolve(self)
77
+ end
78
+
79
+ def []=(key, val)
80
+ lookup(key).set val
81
+ end
82
+
83
+ def names
84
+ @items.map {|i| i.name }
85
+ end
86
+
87
+ def each(&block)
88
+ @items.each(&block)
89
+ end
90
+
91
+ def key?(name)
92
+ @table.key?(name)
93
+ end
94
+
95
+ def lookup(name)
96
+ @table[name] or setup_rb_error "no such config item: #{name}"
97
+ end
98
+
99
+ def add(item)
100
+ @items.push item
101
+ @table[item.name] = item
102
+ end
103
+
104
+ def remove(name)
105
+ item = lookup(name)
106
+ @items.delete_if {|i| i.name == name }
107
+ @table.delete_if {|name, i| i.name == name }
108
+ item
109
+ end
110
+
111
+ def load_script(path, inst = nil)
112
+ if File.file?(path)
113
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
114
+ end
115
+ end
116
+
117
+ def savefile
118
+ '.config'
119
+ end
120
+
121
+ def load_savefile
122
+ begin
123
+ File.foreach(savefile()) do |line|
124
+ k, v = *line.split(/=/, 2)
125
+ self[k] = v.strip
126
+ end
127
+ rescue Errno::ENOENT
128
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
129
+ end
130
+ end
131
+
132
+ def save
133
+ @items.each {|i| i.value }
134
+ File.open(savefile(), 'w') {|f|
135
+ @items.each do |i|
136
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
137
+ end
138
+ }
139
+ end
140
+
141
+ def load_standard_entries
142
+ standard_entries(@rbconfig).each do |ent|
143
+ add ent
144
+ end
145
+ end
146
+
147
+ def standard_entries(rbconfig)
148
+ c = rbconfig
149
+
150
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
151
+
152
+ major = c['MAJOR'].to_i
153
+ minor = c['MINOR'].to_i
154
+ teeny = c['TEENY'].to_i
155
+ version = "#{major}.#{minor}"
156
+
157
+ # ruby ver. >= 1.4.4?
158
+ newpath_p = ((major >= 2) or
159
+ ((major == 1) and
160
+ ((minor >= 5) or
161
+ ((minor == 4) and (teeny >= 4)))))
162
+
163
+ if c['rubylibdir']
164
+ # V > 1.6.3
165
+ libruby = "#{c['prefix']}/lib/ruby"
166
+ librubyver = c['rubylibdir']
167
+ librubyverarch = c['archdir']
168
+ siteruby = c['sitedir']
169
+ siterubyver = c['sitelibdir']
170
+ siterubyverarch = c['sitearchdir']
171
+ elsif newpath_p
172
+ # 1.4.4 <= V <= 1.6.3
173
+ libruby = "#{c['prefix']}/lib/ruby"
174
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
175
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
176
+ siteruby = c['sitedir']
177
+ siterubyver = "$siteruby/#{version}"
178
+ siterubyverarch = "$siterubyver/#{c['arch']}"
179
+ else
180
+ # V < 1.4.4
181
+ libruby = "#{c['prefix']}/lib/ruby"
182
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
183
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
184
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
185
+ siterubyver = siteruby
186
+ siterubyverarch = "$siterubyver/#{c['arch']}"
187
+ end
188
+ parameterize = lambda {|path|
189
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
190
+ }
191
+
192
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
193
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
194
+ else
195
+ makeprog = 'make'
196
+ end
197
+
198
+ [
199
+ ExecItem.new('installdirs', 'std/site/home',
200
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
201
+ {|val, table|
202
+ case val
203
+ when 'std'
204
+ table['rbdir'] = '$librubyver'
205
+ table['sodir'] = '$librubyverarch'
206
+ when 'site'
207
+ table['rbdir'] = '$siterubyver'
208
+ table['sodir'] = '$siterubyverarch'
209
+ when 'home'
210
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
211
+ table['prefix'] = ENV['HOME']
212
+ table['rbdir'] = '$libdir/ruby'
213
+ table['sodir'] = '$libdir/ruby'
214
+ end
215
+ },
216
+ PathItem.new('prefix', 'path', c['prefix'],
217
+ 'path prefix of target environment'),
218
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
219
+ 'the directory for commands'),
220
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
221
+ 'the directory for libraries'),
222
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
223
+ 'the directory for shared data'),
224
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
225
+ 'the directory for man pages'),
226
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
227
+ 'the directory for system configuration files'),
228
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
229
+ 'the directory for local state data'),
230
+ PathItem.new('libruby', 'path', libruby,
231
+ 'the directory for ruby libraries'),
232
+ PathItem.new('librubyver', 'path', librubyver,
233
+ 'the directory for standard ruby libraries'),
234
+ PathItem.new('librubyverarch', 'path', librubyverarch,
235
+ 'the directory for standard ruby extensions'),
236
+ PathItem.new('siteruby', 'path', siteruby,
237
+ 'the directory for version-independent aux ruby libraries'),
238
+ PathItem.new('siterubyver', 'path', siterubyver,
239
+ 'the directory for aux ruby libraries'),
240
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
241
+ 'the directory for aux ruby binaries'),
242
+ PathItem.new('rbdir', 'path', '$siterubyver',
243
+ 'the directory for ruby scripts'),
244
+ PathItem.new('sodir', 'path', '$siterubyverarch',
245
+ 'the directory for ruby extentions'),
246
+ PathItem.new('rubypath', 'path', rubypath,
247
+ 'the path to set to #! line'),
248
+ ProgramItem.new('rubyprog', 'name', rubypath,
249
+ 'the ruby program using for installation'),
250
+ ProgramItem.new('makeprog', 'name', makeprog,
251
+ 'the make program to compile ruby extentions'),
252
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
253
+ 'shebang line (#!) editing mode'),
254
+ BoolItem.new('without-ext', 'yes/no', 'no',
255
+ 'does not compile/install ruby extentions')
256
+ ]
257
+ end
258
+ private :standard_entries
259
+
260
+ def load_multipackage_entries
261
+ multipackage_entries().each do |ent|
262
+ add ent
263
+ end
264
+ end
265
+
266
+ def multipackage_entries
267
+ [
268
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
269
+ 'package names that you want to install'),
270
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
271
+ 'package names that you do not want to install')
272
+ ]
273
+ end
274
+ private :multipackage_entries
275
+
276
+ ALIASES = {
277
+ 'std-ruby' => 'librubyver',
278
+ 'stdruby' => 'librubyver',
279
+ 'rubylibdir' => 'librubyver',
280
+ 'archdir' => 'librubyverarch',
281
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
282
+ 'site-ruby' => 'siterubyver', # For backward compatibility
283
+ 'bin-dir' => 'bindir',
284
+ 'bin-dir' => 'bindir',
285
+ 'rb-dir' => 'rbdir',
286
+ 'so-dir' => 'sodir',
287
+ 'data-dir' => 'datadir',
288
+ 'ruby-path' => 'rubypath',
289
+ 'ruby-prog' => 'rubyprog',
290
+ 'ruby' => 'rubyprog',
291
+ 'make-prog' => 'makeprog',
292
+ 'make' => 'makeprog'
293
+ }
294
+
295
+ def fixup
296
+ ALIASES.each do |ali, name|
297
+ @table[ali] = @table[name]
298
+ end
299
+ end
300
+
301
+ def options_re
302
+ /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
303
+ end
304
+
305
+ def parse_opt(opt)
306
+ m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}"
307
+ m.to_a[1,2]
308
+ end
309
+
310
+ def dllext
311
+ @rbconfig['DLEXT']
312
+ end
313
+
314
+ def value_config?(name)
315
+ lookup(name).value?
316
+ end
317
+
318
+ class Item
319
+ def initialize(name, template, default, desc)
320
+ @name = name.freeze
321
+ @template = template
322
+ @value = default
323
+ @default = default
324
+ @description = desc
325
+ end
326
+
327
+ attr_reader :name
328
+ attr_reader :description
329
+
330
+ attr_accessor :default
331
+ alias help_default default
332
+
333
+ def help_opt
334
+ "--#{@name}=#{@template}"
335
+ end
336
+
337
+ def value?
338
+ true
339
+ end
340
+
341
+ def value
342
+ @value
343
+ end
344
+
345
+ def resolve(table)
346
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
347
+ end
348
+
349
+ def set(val)
350
+ @value = check(val)
351
+ end
352
+
353
+ private
354
+
355
+ def check(val)
356
+ setup_rb_error "config: --#{name} requires argument" unless val
357
+ val
358
+ end
359
+ end
360
+
361
+ class BoolItem < Item
362
+ def config_type
363
+ 'bool'
364
+ end
365
+
366
+ def help_opt
367
+ "--#{@name}"
368
+ end
369
+
370
+ private
371
+
372
+ def check(val)
373
+ return 'yes' unless val
374
+ case val
375
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
376
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
377
+ else
378
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
379
+ end
380
+ end
381
+ end
382
+
383
+ class PathItem < Item
384
+ def config_type
385
+ 'path'
386
+ end
387
+
388
+ private
389
+
390
+ def check(path)
391
+ setup_rb_error "config: --#{@name} requires argument" unless path
392
+ # path[0,1] == '$' ? path : File.expand_path(path)
393
+ path[0,1] == '$' ? 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
+ dirname = prefix + dirname if prefix
543
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
544
+ return if no_harm?
545
+
546
+ # Does not check '/', it's too abnormal.
547
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
548
+ if /\A[a-z]:\z/i =~ dirs[0]
549
+ disk = dirs.shift
550
+ dirs[0] = disk + dirs[0]
551
+ end
552
+ dirs.each_index do |idx|
553
+ path = dirs[0..idx].join('')
554
+ Dir.mkdir path unless File.dir?(path)
555
+ end
556
+ end
557
+
558
+ def rm_f(path)
559
+ $stderr.puts "rm -f #{path}" if verbose?
560
+ return if no_harm?
561
+ force_remove_file path
562
+ end
563
+
564
+ def rm_rf(path)
565
+ $stderr.puts "rm -rf #{path}" if verbose?
566
+ return if no_harm?
567
+ remove_tree path
568
+ end
569
+
570
+ def remove_tree(path)
571
+ if File.symlink?(path)
572
+ remove_file path
573
+ elsif File.dir?(path)
574
+ remove_tree0 path
575
+ else
576
+ force_remove_file path
577
+ end
578
+ end
579
+
580
+ def remove_tree0(path)
581
+ Dir.foreach(path) do |ent|
582
+ next if ent == '.'
583
+ next if ent == '..'
584
+ entpath = "#{path}/#{ent}"
585
+ if File.symlink?(entpath)
586
+ remove_file entpath
587
+ elsif File.dir?(entpath)
588
+ remove_tree0 entpath
589
+ else
590
+ force_remove_file entpath
591
+ end
592
+ end
593
+ begin
594
+ Dir.rmdir path
595
+ rescue Errno::ENOTEMPTY
596
+ # directory may not be empty
597
+ end
598
+ end
599
+
600
+ def move_file(src, dest)
601
+ force_remove_file dest
602
+ begin
603
+ File.rename src, dest
604
+ rescue
605
+ File.open(dest, 'wb') {|f|
606
+ f.write File.binread(src)
607
+ }
608
+ File.chmod File.stat(src).mode, dest
609
+ File.unlink src
610
+ end
611
+ end
612
+
613
+ def force_remove_file(path)
614
+ begin
615
+ remove_file path
616
+ rescue
617
+ end
618
+ end
619
+
620
+ def remove_file(path)
621
+ File.chmod 0777, path
622
+ File.unlink path
623
+ end
624
+
625
+ def install(from, dest, mode, prefix = nil)
626
+ $stderr.puts "install #{from} #{dest}" if verbose?
627
+ return if no_harm?
628
+
629
+ # realdest = prefix ? prefix + File.expand_path(dest) : dest
630
+ realdest = prefix ? prefix + dest : dest
631
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
632
+ str = File.binread(from)
633
+ if diff?(str, realdest)
634
+ verbose_off {
635
+ rm_f realdest if File.exist?(realdest)
636
+ }
637
+ File.open(realdest, 'wb') {|f|
638
+ f.write str
639
+ }
640
+ File.chmod mode, realdest
641
+
642
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
643
+ if prefix
644
+ f.puts realdest.sub(prefix, '')
645
+ else
646
+ f.puts realdest
647
+ end
648
+ }
649
+ end
650
+ end
651
+
652
+ def diff?(new_content, path)
653
+ return true unless File.exist?(path)
654
+ new_content != File.binread(path)
655
+ end
656
+
657
+ def command(*args)
658
+ $stderr.puts args.join(' ') if verbose?
659
+ system(*args) or raise RuntimeError,
660
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
661
+ end
662
+
663
+ def ruby(*args)
664
+ command config('rubyprog'), *args
665
+ end
666
+
667
+ def make(task = nil)
668
+ command(*[config('makeprog'), task].compact)
669
+ end
670
+
671
+ def extdir?(dir)
672
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
673
+ end
674
+
675
+ def files_of(dir)
676
+ Dir.open(dir) {|d|
677
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
678
+ }
679
+ end
680
+
681
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
682
+
683
+ def directories_of(dir)
684
+ Dir.open(dir) {|d|
685
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
686
+ }
687
+ end
688
+
689
+ end
690
+
691
+
692
+ # This module requires: #srcdir_root, #objdir_root, #relpath
693
+ module HookScriptAPI
694
+
695
+ def get_config(key)
696
+ @config[key]
697
+ end
698
+
699
+ alias config get_config
700
+
701
+ # obsolete: use metaconfig to change configuration
702
+ def set_config(key, val)
703
+ @config[key] = val
704
+ end
705
+
706
+ #
707
+ # srcdir/objdir (works only in the package directory)
708
+ #
709
+
710
+ def curr_srcdir
711
+ "#{srcdir_root()}/#{relpath()}"
712
+ end
713
+
714
+ def curr_objdir
715
+ "#{objdir_root()}/#{relpath()}"
716
+ end
717
+
718
+ def srcfile(path)
719
+ "#{curr_srcdir()}/#{path}"
720
+ end
721
+
722
+ def srcexist?(path)
723
+ File.exist?(srcfile(path))
724
+ end
725
+
726
+ def srcdirectory?(path)
727
+ File.dir?(srcfile(path))
728
+ end
729
+
730
+ def srcfile?(path)
731
+ File.file?(srcfile(path))
732
+ end
733
+
734
+ def srcentries(path = '.')
735
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
736
+ return d.to_a - %w(. ..)
737
+ }
738
+ end
739
+
740
+ def srcfiles(path = '.')
741
+ srcentries(path).select {|fname|
742
+ File.file?(File.join(curr_srcdir(), path, fname))
743
+ }
744
+ end
745
+
746
+ def srcdirectories(path = '.')
747
+ srcentries(path).select {|fname|
748
+ File.dir?(File.join(curr_srcdir(), path, fname))
749
+ }
750
+ end
751
+
752
+ end
753
+
754
+
755
+ class ToplevelInstaller
756
+
757
+ Version = '3.4.1'
758
+ Copyright = 'Copyright (c) 2000-2006 Minero Aoki'
759
+
760
+ TASKS = [
761
+ [ 'all', 'do config, setup, then install' ],
762
+ [ 'config', 'saves your configurations' ],
763
+ [ 'show', 'shows current configuration' ],
764
+ [ 'setup', 'compiles ruby extentions and others' ],
765
+ [ 'install', 'installs files' ],
766
+ [ 'test', 'run all tests in test/' ],
767
+ [ 'clean', "does `make clean' for each extention" ],
768
+ [ 'distclean',"does `make distclean' for each extention" ]
769
+ ]
770
+
771
+ def ToplevelInstaller.invoke
772
+ config = ConfigTable.new(load_rbconfig())
773
+ config.load_standard_entries
774
+ config.load_multipackage_entries if multipackage?
775
+ config.fixup
776
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
777
+ klass.new(File.dirname($0), config).invoke
778
+ end
779
+
780
+ def ToplevelInstaller.multipackage?
781
+ File.dir?(File.dirname($0) + '/packages')
782
+ end
783
+
784
+ def ToplevelInstaller.load_rbconfig
785
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
786
+ ARGV.delete(arg)
787
+ load File.expand_path(arg.split(/=/, 2)[1])
788
+ $".push 'rbconfig.rb'
789
+ else
790
+ require 'rbconfig'
791
+ end
792
+ ::Config::CONFIG
793
+ end
794
+
795
+ def initialize(ardir_root, config)
796
+ @ardir = File.expand_path(ardir_root)
797
+ @config = config
798
+ # cache
799
+ @valid_task_re = nil
800
+ end
801
+
802
+ def config(key)
803
+ @config[key]
804
+ end
805
+
806
+ def inspect
807
+ "#<#{self.class} #{__id__()}>"
808
+ end
809
+
810
+ def invoke
811
+ run_metaconfigs
812
+ case task = parsearg_global()
813
+ when nil, 'all'
814
+ parsearg_config
815
+ init_installers
816
+ exec_config
817
+ exec_setup
818
+ exec_install
819
+ else
820
+ case task
821
+ when 'config', 'test'
822
+ ;
823
+ when 'clean', 'distclean'
824
+ @config.load_savefile if File.exist?(@config.savefile)
825
+ else
826
+ @config.load_savefile
827
+ end
828
+ __send__ "parsearg_#{task}"
829
+ init_installers
830
+ __send__ "exec_#{task}"
831
+ end
832
+ end
833
+
834
+ def run_metaconfigs
835
+ @config.load_script "#{@ardir}/metaconfig"
836
+ end
837
+
838
+ def init_installers
839
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
840
+ end
841
+
842
+ #
843
+ # Hook Script API bases
844
+ #
845
+
846
+ def srcdir_root
847
+ @ardir
848
+ end
849
+
850
+ def objdir_root
851
+ '.'
852
+ end
853
+
854
+ def relpath
855
+ '.'
856
+ end
857
+
858
+ #
859
+ # Option Parsing
860
+ #
861
+
862
+ def parsearg_global
863
+ while arg = ARGV.shift
864
+ case arg
865
+ when /\A\w+\z/
866
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
867
+ return arg
868
+ when '-q', '--quiet'
869
+ @config.verbose = false
870
+ when '--verbose'
871
+ @config.verbose = true
872
+ when '--help'
873
+ print_usage $stdout
874
+ exit 0
875
+ when '--version'
876
+ puts "#{File.basename($0)} version #{Version}"
877
+ exit 0
878
+ when '--copyright'
879
+ puts Copyright
880
+ exit 0
881
+ else
882
+ setup_rb_error "unknown global option '#{arg}'"
883
+ end
884
+ end
885
+ nil
886
+ end
887
+
888
+ def valid_task?(t)
889
+ valid_task_re() =~ t
890
+ end
891
+
892
+ def valid_task_re
893
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
894
+ end
895
+
896
+ def parsearg_no_options
897
+ unless ARGV.empty?
898
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
899
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
900
+ end
901
+ end
902
+
903
+ alias parsearg_show parsearg_no_options
904
+ alias parsearg_setup parsearg_no_options
905
+ alias parsearg_test parsearg_no_options
906
+ alias parsearg_clean parsearg_no_options
907
+ alias parsearg_distclean parsearg_no_options
908
+
909
+ def parsearg_config
910
+ evalopt = []
911
+ set = []
912
+ @config.config_opt = []
913
+ while i = ARGV.shift
914
+ if /\A--?\z/ =~ i
915
+ @config.config_opt = ARGV.dup
916
+ break
917
+ end
918
+ name, value = *@config.parse_opt(i)
919
+ if @config.value_config?(name)
920
+ @config[name] = value
921
+ else
922
+ evalopt.push [name, value]
923
+ end
924
+ set.push name
925
+ end
926
+ evalopt.each do |name, value|
927
+ @config.lookup(name).evaluate value, @config
928
+ end
929
+ # Check if configuration is valid
930
+ set.each do |n|
931
+ @config[n] if @config.value_config?(n)
932
+ end
933
+ end
934
+
935
+ def parsearg_install
936
+ @config.no_harm = false
937
+ @config.install_prefix = ''
938
+ while a = ARGV.shift
939
+ case a
940
+ when '--no-harm'
941
+ @config.no_harm = true
942
+ when /\A--prefix=/
943
+ path = a.split(/=/, 2)[1]
944
+ path = File.expand_path(path) unless path[0,1] == '/'
945
+ @config.install_prefix = path
946
+ else
947
+ setup_rb_error "install: unknown option #{a}"
948
+ end
949
+ end
950
+ end
951
+
952
+ def print_usage(out)
953
+ out.puts 'Typical Installation Procedure:'
954
+ out.puts " $ ruby #{File.basename $0} config"
955
+ out.puts " $ ruby #{File.basename $0} setup"
956
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
957
+ out.puts
958
+ out.puts 'Detailed Usage:'
959
+ out.puts " ruby #{File.basename $0} <global option>"
960
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
961
+
962
+ fmt = " %-24s %s\n"
963
+ out.puts
964
+ out.puts 'Global options:'
965
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
966
+ out.printf fmt, ' --verbose', 'output messages verbosely'
967
+ out.printf fmt, ' --help', 'print this message'
968
+ out.printf fmt, ' --version', 'print version and quit'
969
+ out.printf fmt, ' --copyright', 'print copyright and quit'
970
+ out.puts
971
+ out.puts 'Tasks:'
972
+ TASKS.each do |name, desc|
973
+ out.printf fmt, name, desc
974
+ end
975
+
976
+ fmt = " %-24s %s [%s]\n"
977
+ out.puts
978
+ out.puts 'Options for CONFIG or ALL:'
979
+ @config.each do |item|
980
+ out.printf fmt, item.help_opt, item.description, item.help_default
981
+ end
982
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
983
+ out.puts
984
+ out.puts 'Options for INSTALL:'
985
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
986
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
987
+ out.puts
988
+ end
989
+
990
+ #
991
+ # Task Handlers
992
+ #
993
+
994
+ def exec_config
995
+ @installer.exec_config
996
+ @config.save # must be final
997
+ end
998
+
999
+ def exec_setup
1000
+ @installer.exec_setup
1001
+ end
1002
+
1003
+ def exec_install
1004
+ @installer.exec_install
1005
+ end
1006
+
1007
+ def exec_test
1008
+ @installer.exec_test
1009
+ end
1010
+
1011
+ def exec_show
1012
+ @config.each do |i|
1013
+ printf "%-20s %s\n", i.name, i.value if i.value?
1014
+ end
1015
+ end
1016
+
1017
+ def exec_clean
1018
+ @installer.exec_clean
1019
+ end
1020
+
1021
+ def exec_distclean
1022
+ @installer.exec_distclean
1023
+ end
1024
+
1025
+ end # class ToplevelInstaller
1026
+
1027
+
1028
+ class ToplevelInstallerMulti < ToplevelInstaller
1029
+
1030
+ include FileOperations
1031
+
1032
+ def initialize(ardir_root, config)
1033
+ super
1034
+ @packages = directories_of("#{@ardir}/packages")
1035
+ raise 'no package exists' if @packages.empty?
1036
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1037
+ end
1038
+
1039
+ def run_metaconfigs
1040
+ @config.load_script "#{@ardir}/metaconfig", self
1041
+ @packages.each do |name|
1042
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1043
+ end
1044
+ end
1045
+
1046
+ attr_reader :packages
1047
+
1048
+ def packages=(list)
1049
+ raise 'package list is empty' if list.empty?
1050
+ list.each do |name|
1051
+ raise "directory packages/#{name} does not exist"\
1052
+ unless File.dir?("#{@ardir}/packages/#{name}")
1053
+ end
1054
+ @packages = list
1055
+ end
1056
+
1057
+ def init_installers
1058
+ @installers = {}
1059
+ @packages.each do |pack|
1060
+ @installers[pack] = Installer.new(@config,
1061
+ "#{@ardir}/packages/#{pack}",
1062
+ "packages/#{pack}")
1063
+ end
1064
+ with = extract_selection(config('with'))
1065
+ without = extract_selection(config('without'))
1066
+ @selected = @installers.keys.select {|name|
1067
+ (with.empty? or with.include?(name)) \
1068
+ and not without.include?(name)
1069
+ }
1070
+ end
1071
+
1072
+ def extract_selection(list)
1073
+ a = list.split(/,/)
1074
+ a.each do |name|
1075
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1076
+ end
1077
+ a
1078
+ end
1079
+
1080
+ def print_usage(f)
1081
+ super
1082
+ f.puts 'Inluded packages:'
1083
+ f.puts ' ' + @packages.sort.join(' ')
1084
+ f.puts
1085
+ end
1086
+
1087
+ #
1088
+ # Task Handlers
1089
+ #
1090
+
1091
+ def exec_config
1092
+ run_hook 'pre-config'
1093
+ each_selected_installers {|inst| inst.exec_config }
1094
+ run_hook 'post-config'
1095
+ @config.save # must be final
1096
+ end
1097
+
1098
+ def exec_setup
1099
+ run_hook 'pre-setup'
1100
+ each_selected_installers {|inst| inst.exec_setup }
1101
+ run_hook 'post-setup'
1102
+ end
1103
+
1104
+ def exec_install
1105
+ run_hook 'pre-install'
1106
+ each_selected_installers {|inst| inst.exec_install }
1107
+ run_hook 'post-install'
1108
+ end
1109
+
1110
+ def exec_test
1111
+ run_hook 'pre-test'
1112
+ each_selected_installers {|inst| inst.exec_test }
1113
+ run_hook 'post-test'
1114
+ end
1115
+
1116
+ def exec_clean
1117
+ rm_f @config.savefile
1118
+ run_hook 'pre-clean'
1119
+ each_selected_installers {|inst| inst.exec_clean }
1120
+ run_hook 'post-clean'
1121
+ end
1122
+
1123
+ def exec_distclean
1124
+ rm_f @config.savefile
1125
+ run_hook 'pre-distclean'
1126
+ each_selected_installers {|inst| inst.exec_distclean }
1127
+ run_hook 'post-distclean'
1128
+ end
1129
+
1130
+ #
1131
+ # lib
1132
+ #
1133
+
1134
+ def each_selected_installers
1135
+ Dir.mkdir 'packages' unless File.dir?('packages')
1136
+ @selected.each do |pack|
1137
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1138
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1139
+ Dir.chdir "packages/#{pack}"
1140
+ yield @installers[pack]
1141
+ Dir.chdir '../..'
1142
+ end
1143
+ end
1144
+
1145
+ def run_hook(id)
1146
+ @root_installer.run_hook id
1147
+ end
1148
+
1149
+ # module FileOperations requires this
1150
+ def verbose?
1151
+ @config.verbose?
1152
+ end
1153
+
1154
+ # module FileOperations requires this
1155
+ def no_harm?
1156
+ @config.no_harm?
1157
+ end
1158
+
1159
+ end # class ToplevelInstallerMulti
1160
+
1161
+
1162
+ class Installer
1163
+
1164
+ FILETYPES = %w( bin lib ext data conf man )
1165
+
1166
+ include FileOperations
1167
+ include HookScriptAPI
1168
+
1169
+ def initialize(config, srcroot, objroot)
1170
+ @config = config
1171
+ @srcdir = File.expand_path(srcroot)
1172
+ @objdir = File.expand_path(objroot)
1173
+ @currdir = '.'
1174
+ end
1175
+
1176
+ def inspect
1177
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1178
+ end
1179
+
1180
+ def noop(rel)
1181
+ end
1182
+
1183
+ #
1184
+ # Hook Script API base methods
1185
+ #
1186
+
1187
+ def srcdir_root
1188
+ @srcdir
1189
+ end
1190
+
1191
+ def objdir_root
1192
+ @objdir
1193
+ end
1194
+
1195
+ def relpath
1196
+ @currdir
1197
+ end
1198
+
1199
+ #
1200
+ # Config Access
1201
+ #
1202
+
1203
+ # module FileOperations requires this
1204
+ def verbose?
1205
+ @config.verbose?
1206
+ end
1207
+
1208
+ # module FileOperations requires this
1209
+ def no_harm?
1210
+ @config.no_harm?
1211
+ end
1212
+
1213
+ def verbose_off
1214
+ begin
1215
+ save, @config.verbose = @config.verbose?, false
1216
+ yield
1217
+ ensure
1218
+ @config.verbose = save
1219
+ end
1220
+ end
1221
+
1222
+ #
1223
+ # TASK config
1224
+ #
1225
+
1226
+ def exec_config
1227
+ exec_task_traverse 'config'
1228
+ end
1229
+
1230
+ alias config_dir_bin noop
1231
+ alias config_dir_lib noop
1232
+
1233
+ def config_dir_ext(rel)
1234
+ extconf if extdir?(curr_srcdir())
1235
+ end
1236
+
1237
+ alias config_dir_data noop
1238
+ alias config_dir_conf noop
1239
+ alias config_dir_man noop
1240
+
1241
+ def extconf
1242
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1243
+ end
1244
+
1245
+ #
1246
+ # TASK setup
1247
+ #
1248
+
1249
+ def exec_setup
1250
+ exec_task_traverse 'setup'
1251
+ end
1252
+
1253
+ def setup_dir_bin(rel)
1254
+ files_of(curr_srcdir()).each do |fname|
1255
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
1256
+ end
1257
+ end
1258
+
1259
+ alias setup_dir_lib noop
1260
+
1261
+ def setup_dir_ext(rel)
1262
+ make if extdir?(curr_srcdir())
1263
+ end
1264
+
1265
+ alias setup_dir_data noop
1266
+ alias setup_dir_conf noop
1267
+ alias setup_dir_man noop
1268
+
1269
+ def update_shebang_line(path)
1270
+ return if no_harm?
1271
+ return if config('shebang') == 'never'
1272
+ old = Shebang.load(path)
1273
+ if old
1274
+ $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
1275
+ new = new_shebang(old)
1276
+ return if new.to_s == old.to_s
1277
+ else
1278
+ return unless config('shebang') == 'all'
1279
+ new = Shebang.new(config('rubypath'))
1280
+ end
1281
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1282
+ open_atomic_writer(path) {|output|
1283
+ File.open(path, 'rb') {|f|
1284
+ f.gets if old # discard
1285
+ output.puts new.to_s
1286
+ output.print f.read
1287
+ }
1288
+ }
1289
+ end
1290
+
1291
+ def new_shebang(old)
1292
+ if /\Aruby/ =~ File.basename(old.cmd)
1293
+ Shebang.new(config('rubypath'), old.args)
1294
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1295
+ Shebang.new(config('rubypath'), old.args[1..-1])
1296
+ else
1297
+ return old unless config('shebang') == 'all'
1298
+ Shebang.new(config('rubypath'))
1299
+ end
1300
+ end
1301
+
1302
+ def open_atomic_writer(path, &block)
1303
+ tmpfile = File.basename(path) + '.tmp'
1304
+ begin
1305
+ File.open(tmpfile, 'wb', &block)
1306
+ File.rename tmpfile, File.basename(path)
1307
+ ensure
1308
+ File.unlink tmpfile if File.exist?(tmpfile)
1309
+ end
1310
+ end
1311
+
1312
+ class Shebang
1313
+ def Shebang.load(path)
1314
+ line = nil
1315
+ File.open(path) {|f|
1316
+ line = f.gets
1317
+ }
1318
+ return nil unless /\A#!/ =~ line
1319
+ parse(line)
1320
+ end
1321
+
1322
+ def Shebang.parse(line)
1323
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1324
+ new(cmd, args)
1325
+ end
1326
+
1327
+ def initialize(cmd, args = [])
1328
+ @cmd = cmd
1329
+ @args = args
1330
+ end
1331
+
1332
+ attr_reader :cmd
1333
+ attr_reader :args
1334
+
1335
+ def to_s
1336
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1337
+ end
1338
+ end
1339
+
1340
+ #
1341
+ # TASK install
1342
+ #
1343
+
1344
+ def exec_install
1345
+ rm_f 'InstalledFiles'
1346
+ exec_task_traverse 'install'
1347
+ end
1348
+
1349
+ def install_dir_bin(rel)
1350
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755, strip_ext?
1351
+ end
1352
+
1353
+ def strip_ext?
1354
+ /mswin|mingw/ !~ RUBY_PLATFORM
1355
+ end
1356
+
1357
+ def install_dir_lib(rel)
1358
+ # install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1359
+ install_files libfiles(), "#{config('libdir')}/#{rel}", 0644
1360
+ end
1361
+
1362
+ def install_dir_ext(rel)
1363
+ return unless extdir?(curr_srcdir())
1364
+ install_files rubyextentions('.'),
1365
+ "#{config('sodir')}/#{File.dirname(rel)}",
1366
+ 0555
1367
+ end
1368
+
1369
+ def install_dir_data(rel)
1370
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1371
+ end
1372
+
1373
+ def install_dir_conf(rel)
1374
+ # FIXME: should not remove current config files
1375
+ # (rename previous file to .old/.org)
1376
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1377
+ end
1378
+
1379
+ def install_dir_man(rel)
1380
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1381
+ end
1382
+
1383
+ def install_files(list, dest, mode, stripext = false)
1384
+ mkdir_p dest, @config.install_prefix
1385
+ list.each do |fname|
1386
+ if stripext
1387
+ install fname, "#{dest}/#{File.basename(fname, '.*')}",
1388
+ mode, @config.install_prefix
1389
+ else
1390
+ install fname, dest, mode, @config.install_prefix
1391
+ end
1392
+ end
1393
+ end
1394
+
1395
+ def libfiles
1396
+ glob_reject(%w(*.y *.output), targetfiles())
1397
+ end
1398
+
1399
+ def rubyextentions(dir)
1400
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1401
+ if ents.empty?
1402
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1403
+ end
1404
+ ents
1405
+ end
1406
+
1407
+ def targetfiles
1408
+ mapdir(existfiles() - hookfiles())
1409
+ end
1410
+
1411
+ def mapdir(ents)
1412
+ ents.map {|ent|
1413
+ if File.exist?(ent)
1414
+ then ent # objdir
1415
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1416
+ end
1417
+ }
1418
+ end
1419
+
1420
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1421
+ JUNK_FILES = %w(
1422
+ core RCSLOG tags TAGS .make.state
1423
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1424
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1425
+
1426
+ *.org *.in .*
1427
+ )
1428
+
1429
+ def existfiles
1430
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1431
+ end
1432
+
1433
+ def hookfiles
1434
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1435
+ %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) }
1436
+ }.flatten
1437
+ end
1438
+
1439
+ def glob_select(pat, ents)
1440
+ re = globs2re([pat])
1441
+ ents.select {|ent| re =~ ent }
1442
+ end
1443
+
1444
+ def glob_reject(pats, ents)
1445
+ re = globs2re(pats)
1446
+ ents.reject {|ent| re =~ ent }
1447
+ end
1448
+
1449
+ GLOB2REGEX = {
1450
+ '.' => '\.',
1451
+ '$' => '\$',
1452
+ '#' => '\#',
1453
+ '*' => '.*'
1454
+ }
1455
+
1456
+ def globs2re(pats)
1457
+ /\A(?:#{
1458
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1459
+ })\z/
1460
+ end
1461
+
1462
+ #
1463
+ # TASK test
1464
+ #
1465
+
1466
+ TESTDIR = 'test'
1467
+
1468
+ def exec_test
1469
+ unless File.directory?(TESTDIR)
1470
+ $stderr.puts 'no test in this package' if verbose?
1471
+ return
1472
+ end
1473
+ $stderr.puts 'Running tests...' if verbose?
1474
+ begin
1475
+ require 'test/unit'
1476
+ rescue LoadError
1477
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1478
+ end
1479
+ dive_into(TESTDIR) {
1480
+ run_hook "pre-test"
1481
+ runner = Test::Unit::AutoRunner.new(true)
1482
+ runner.to_run #<< TESTDIR
1483
+ runner.run
1484
+ run_hook "post-test"
1485
+ }
1486
+ end
1487
+
1488
+ #
1489
+ # TASK clean
1490
+ #
1491
+
1492
+ def exec_clean
1493
+ exec_task_traverse 'clean'
1494
+ rm_f @config.savefile
1495
+ rm_f 'InstalledFiles'
1496
+ end
1497
+
1498
+ alias clean_dir_bin noop
1499
+ alias clean_dir_lib noop
1500
+ alias clean_dir_data noop
1501
+ alias clean_dir_conf noop
1502
+ alias clean_dir_man noop
1503
+
1504
+ def clean_dir_ext(rel)
1505
+ return unless extdir?(curr_srcdir())
1506
+ make 'clean' if File.file?('Makefile')
1507
+ end
1508
+
1509
+ #
1510
+ # TASK distclean
1511
+ #
1512
+
1513
+ def exec_distclean
1514
+ exec_task_traverse 'distclean'
1515
+ rm_f @config.savefile
1516
+ rm_f 'InstalledFiles'
1517
+ end
1518
+
1519
+ alias distclean_dir_bin noop
1520
+ alias distclean_dir_lib noop
1521
+
1522
+ def distclean_dir_ext(rel)
1523
+ return unless extdir?(curr_srcdir())
1524
+ make 'distclean' if File.file?('Makefile')
1525
+ end
1526
+
1527
+ alias distclean_dir_data noop
1528
+ alias distclean_dir_conf noop
1529
+ alias distclean_dir_man noop
1530
+
1531
+ #
1532
+ # Traversing
1533
+ #
1534
+
1535
+ def exec_task_traverse(task)
1536
+ run_hook "pre-#{task}"
1537
+ FILETYPES.each do |type|
1538
+ if type == 'ext' and config('without-ext') == 'yes'
1539
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1540
+ next
1541
+ end
1542
+ traverse task, type, "#{task}_dir_#{type}"
1543
+ end
1544
+ run_hook "post-#{task}"
1545
+ end
1546
+
1547
+ def traverse(task, rel, mid)
1548
+ dive_into(rel) {
1549
+ run_hook "pre-#{task}"
1550
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1551
+ directories_of(curr_srcdir()).each do |d|
1552
+ traverse task, "#{rel}/#{d}", mid
1553
+ end
1554
+ run_hook "post-#{task}"
1555
+ }
1556
+ end
1557
+
1558
+ def dive_into(rel)
1559
+ return unless File.dir?("#{@srcdir}/#{rel}")
1560
+
1561
+ dir = File.basename(rel)
1562
+ Dir.mkdir dir unless File.dir?(dir)
1563
+ prevdir = Dir.pwd
1564
+ Dir.chdir dir
1565
+ $stderr.puts '---> ' + rel if verbose?
1566
+ @currdir = rel
1567
+ yield
1568
+ Dir.chdir prevdir
1569
+ $stderr.puts '<--- ' + rel if verbose?
1570
+ @currdir = File.dirname(rel)
1571
+ end
1572
+
1573
+ def run_hook(id)
1574
+ path = [ "#{curr_srcdir()}/#{id}",
1575
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1576
+ return unless path
1577
+ $stderr.puts "invoking hook script #{path}" if verbose?
1578
+ begin
1579
+ instance_eval File.read(path), path, 1
1580
+ rescue
1581
+ raise if $DEBUG
1582
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1583
+ end
1584
+ end
1585
+
1586
+ end # class Installer
1587
+
1588
+
1589
+ class SetupError < StandardError; end
1590
+
1591
+ def setup_rb_error(msg)
1592
+ raise SetupError, msg
1593
+ end
1594
+
1595
+ if $0 == __FILE__
1596
+ begin
1597
+ ToplevelInstaller.invoke
1598
+ rescue SetupError
1599
+ raise if $DEBUG
1600
+ $stderr.puts $!.message
1601
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1602
+ exit 1
1603
+ end
1604
+ end