kwalify 0.6.1 → 0.7.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 (219) hide show
  1. data/CHANGES.txt +232 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.txt +16 -19
  4. data/bin/kwalify +3 -3
  5. data/contrib/inline-require +6 -4
  6. data/contrib/kwalify +3719 -2427
  7. data/doc-api/classes/CommandOptionError.html +17 -17
  8. data/doc-api/classes/CommandOptionParser.html +63 -63
  9. data/doc-api/classes/Kwalify.html +29 -7
  10. data/doc-api/classes/Kwalify/AssertionError.html +9 -9
  11. data/doc-api/classes/Kwalify/BaseError.html +72 -71
  12. data/doc-api/classes/Kwalify/BaseParser.html +461 -0
  13. data/doc-api/classes/Kwalify/CommandOptionError.html +11 -11
  14. data/doc-api/classes/Kwalify/ErrorHelper.html +51 -46
  15. data/doc-api/classes/Kwalify/HashInterface.html +13 -135
  16. data/doc-api/classes/Kwalify/Json.html +105 -0
  17. data/doc-api/classes/Kwalify/Main.html +129 -126
  18. data/doc-api/classes/Kwalify/MetaValidator.html +248 -232
  19. data/doc-api/classes/Kwalify/Parser.html +12 -12
  20. data/doc-api/classes/Kwalify/PlainYamlParser.html +166 -163
  21. data/doc-api/classes/Kwalify/PlainYamlParser/Alias.html +11 -11
  22. data/doc-api/classes/Kwalify/Rule.html +152 -130
  23. data/doc-api/classes/Kwalify/SchemaError.html +10 -10
  24. data/doc-api/classes/Kwalify/SyntaxError.html +185 -0
  25. data/doc-api/classes/Kwalify/Types.html +26 -25
  26. data/doc-api/classes/Kwalify/Util.html +389 -0
  27. data/doc-api/classes/Kwalify/Util/HashLike.html +246 -0
  28. data/doc-api/classes/Kwalify/Util/OrderedHash.html +330 -0
  29. data/doc-api/classes/Kwalify/ValidationError.html +10 -10
  30. data/doc-api/classes/Kwalify/Validator.html +153 -86
  31. data/doc-api/classes/Kwalify/Yaml.html +181 -0
  32. data/doc-api/classes/Kwalify/Yaml/Parser.html +1538 -0
  33. data/doc-api/classes/Kwalify/YamlParser.html +190 -183
  34. data/doc-api/classes/Kwalify/YamlSyntaxError.html +8 -57
  35. data/doc-api/created.rid +1 -1
  36. data/doc-api/files/__/README_txt.html +17 -22
  37. data/doc-api/files/kwalify/errors_rb.html +2 -2
  38. data/doc-api/files/kwalify/main_rb.html +4 -3
  39. data/doc-api/files/kwalify/messages_rb.html +2 -2
  40. data/doc-api/files/kwalify/meta-validator_rb.html +3 -3
  41. data/doc-api/files/kwalify/{util/yaml-helper_rb.html → parser/base_rb.html} +8 -6
  42. data/doc-api/files/kwalify/parser/yaml_rb.html +117 -0
  43. data/doc-api/files/kwalify/rule_rb.html +2 -2
  44. data/doc-api/files/kwalify/types_rb.html +2 -2
  45. data/doc-api/files/kwalify/util/assert-text-equal_rb.html +2 -2
  46. data/doc-api/files/kwalify/util/hash-interface_rb.html +9 -2
  47. data/doc-api/files/kwalify/util/hashlike_rb.html +107 -0
  48. data/doc-api/files/kwalify/util/option-parser_rb.html +2 -2
  49. data/doc-api/files/kwalify/util/ordered-hash_rb.html +107 -0
  50. data/doc-api/files/kwalify/util/testcase-helper_rb.html +2 -2
  51. data/doc-api/files/kwalify/util_rb.html +107 -0
  52. data/doc-api/files/kwalify/validator_rb.html +2 -2
  53. data/doc-api/files/kwalify/yaml-parser_rb.html +2 -2
  54. data/doc-api/files/kwalify_rb.html +3 -2
  55. data/doc-api/fr_class_index.html +8 -1
  56. data/doc-api/fr_file_index.html +5 -1
  57. data/doc-api/fr_method_index.html +128 -69
  58. data/doc/img/fig01.png +0 -0
  59. data/doc/users-guide.html +882 -717
  60. data/examples/address-book/address-book.schema.yaml +2 -2
  61. data/examples/data-binding/BABEL.data.yaml +63 -0
  62. data/examples/data-binding/BABEL.schema.yaml +31 -0
  63. data/examples/data-binding/Makefile +8 -0
  64. data/examples/data-binding/Rakefile +13 -0
  65. data/examples/data-binding/main.rb +27 -0
  66. data/examples/invoice/invoice.schema.yaml +3 -3
  67. data/examples/tapkit/tapkit.schema.yaml +2 -2
  68. data/lib/kwalify.rb +41 -4
  69. data/lib/kwalify/errors.rb +118 -96
  70. data/lib/kwalify/kwalify.schema.yaml +58 -0
  71. data/lib/kwalify/main.rb +384 -377
  72. data/lib/kwalify/messages.rb +41 -27
  73. data/lib/kwalify/meta-validator.rb +251 -331
  74. data/lib/kwalify/parser/base.rb +127 -0
  75. data/lib/kwalify/parser/yaml.rb +837 -0
  76. data/lib/kwalify/rule.rb +545 -487
  77. data/lib/kwalify/templates/genclass-java.eruby +189 -162
  78. data/lib/kwalify/templates/genclass-php.eruby +104 -0
  79. data/lib/kwalify/templates/genclass-ruby.eruby +95 -66
  80. data/lib/kwalify/types.rb +107 -106
  81. data/lib/kwalify/util.rb +157 -0
  82. data/lib/kwalify/util/assert-text-equal.rb +33 -31
  83. data/lib/kwalify/util/hash-interface.rb +11 -30
  84. data/lib/kwalify/util/hashlike.rb +51 -0
  85. data/lib/kwalify/util/option-parser.rb +144 -144
  86. data/lib/kwalify/util/ordered-hash.rb +57 -0
  87. data/lib/kwalify/util/testcase-helper.rb +3 -3
  88. data/lib/kwalify/validator.rb +267 -212
  89. data/lib/kwalify/yaml-parser.rb +822 -768
  90. data/setup.rb +861 -607
  91. data/test/Rookbook.yaml +10 -0
  92. data/test/{tmp.dir/Context.java → data/users-guide/AddressBook.java.expected} +11 -11
  93. data/test/data/users-guide/BABEL.data.yaml +24 -0
  94. data/test/data/users-guide/BABEL.schema.yaml +30 -0
  95. data/test/data/users-guide/ExampleAddressBook.java +47 -0
  96. data/test/{tmp.dir/Group.java → data/users-guide/Group.java.expected} +2 -11
  97. data/test/data/users-guide/Person.java.expected +44 -0
  98. data/test/data/users-guide/address_book.rb +52 -0
  99. data/test/data/users-guide/address_book.schema.yaml +28 -0
  100. data/test/data/users-guide/address_book.yaml +27 -0
  101. data/test/data/users-guide/answers-schema.yaml +12 -0
  102. data/test/data/users-guide/answers-validator.rb +52 -0
  103. data/test/data/users-guide/babel_genclass.result +26 -0
  104. data/test/data/users-guide/config.schema.yaml +7 -0
  105. data/test/data/users-guide/config.yaml +4 -0
  106. data/test/{tmp.dir/silent1.document → data/users-guide/document01a.yaml} +0 -0
  107. data/test/data/users-guide/document01b.yaml +3 -0
  108. data/test/data/users-guide/document02a.yaml +4 -0
  109. data/test/data/users-guide/document02b.yaml +4 -0
  110. data/test/data/users-guide/document03a.yaml +6 -0
  111. data/test/data/users-guide/document03b.yaml +6 -0
  112. data/test/data/users-guide/document04a.yaml +9 -0
  113. data/test/data/users-guide/document04b.yaml +9 -0
  114. data/test/data/users-guide/document05a.yaml +11 -0
  115. data/test/data/users-guide/document05b.yaml +12 -0
  116. data/test/data/users-guide/document06a.yaml +15 -0
  117. data/test/data/users-guide/document06b.yaml +16 -0
  118. data/test/data/users-guide/document07a.yaml +9 -0
  119. data/test/data/users-guide/document07b.yaml +7 -0
  120. data/test/data/users-guide/document12a.json +10 -0
  121. data/test/data/users-guide/document12b.json +6 -0
  122. data/test/data/users-guide/document13a.yaml +17 -0
  123. data/test/data/users-guide/document14a.yaml +3 -0
  124. data/test/data/users-guide/document14b.yaml +3 -0
  125. data/test/data/users-guide/document15a.yaml +6 -0
  126. data/test/data/users-guide/document15b.yaml +5 -0
  127. data/test/data/users-guide/example_address_book.rb +10 -0
  128. data/test/data/users-guide/example_address_book_java.result +32 -0
  129. data/test/data/users-guide/example_address_book_ruby.result +31 -0
  130. data/test/data/users-guide/genclass_java.result +4 -0
  131. data/test/data/users-guide/howto-validation-with-parsing.rb +28 -0
  132. data/test/data/users-guide/howto-validation.rb +25 -0
  133. data/test/data/users-guide/howto3.rb +6 -0
  134. data/test/data/users-guide/howto3.result +5 -0
  135. data/test/data/users-guide/howto3.yaml +8 -0
  136. data/test/data/users-guide/howto5_databinding.result +111 -0
  137. data/test/data/users-guide/invalid01.result +3 -0
  138. data/test/data/users-guide/invalid02.result +5 -0
  139. data/test/data/users-guide/invalid03.result +5 -0
  140. data/test/data/users-guide/invalid04.result +4 -0
  141. data/test/data/users-guide/invalid05.result +11 -0
  142. data/test/data/users-guide/invalid06.result +4 -0
  143. data/test/data/users-guide/invalid07.result +3 -0
  144. data/test/data/users-guide/invalid08.result +3 -0
  145. data/test/data/users-guide/invalid12.json +8 -0
  146. data/test/data/users-guide/invalid14.result +4 -0
  147. data/test/data/users-guide/invalid15.result +4 -0
  148. data/test/data/users-guide/loadbabel.rb +27 -0
  149. data/test/data/users-guide/loadconfig.rb +15 -0
  150. data/test/data/users-guide/loadconfig.result +2 -0
  151. data/test/data/users-guide/models.rb +22 -0
  152. data/test/data/users-guide/option_ha.result +6 -0
  153. data/test/data/users-guide/option_ha_genclass_java.result +7 -0
  154. data/test/{tmp.dir/meta1.schema → data/users-guide/schema01.yaml} +0 -0
  155. data/test/data/users-guide/schema02.yaml +12 -0
  156. data/test/data/users-guide/schema03.yaml +9 -0
  157. data/test/data/users-guide/schema04.yaml +20 -0
  158. data/test/data/users-guide/schema05.yaml +29 -0
  159. data/test/data/users-guide/schema06.yaml +11 -0
  160. data/test/data/users-guide/schema12.json +12 -0
  161. data/test/data/users-guide/schema13.yaml +13 -0
  162. data/test/data/users-guide/schema14.yaml +5 -0
  163. data/test/data/users-guide/schema15.yaml +21 -0
  164. data/test/data/users-guide/valid01.result +2 -0
  165. data/test/data/users-guide/valid02.result +2 -0
  166. data/test/data/users-guide/valid03.result +2 -0
  167. data/test/data/users-guide/valid04.result +2 -0
  168. data/test/data/users-guide/valid05.result +2 -0
  169. data/test/data/users-guide/valid06.result +2 -0
  170. data/test/data/users-guide/valid07.result +2 -0
  171. data/test/data/users-guide/valid08.result +2 -0
  172. data/test/data/users-guide/valid12.result +2 -0
  173. data/test/data/users-guide/valid13.result +2 -0
  174. data/test/data/users-guide/valid14.result +2 -0
  175. data/test/data/users-guide/valid15.result +2 -0
  176. data/test/data/users-guide/validate08.rb +37 -0
  177. data/test/test-action.rb +78 -0
  178. data/test/test-action.yaml +738 -0
  179. data/test/test-databinding.rb +80 -0
  180. data/test/test-databinding.yaml +301 -0
  181. data/test/test-main.rb +129 -150
  182. data/test/test-main.yaml +126 -321
  183. data/test/test-metavalidator.rb +47 -47
  184. data/test/test-metavalidator.yaml +77 -21
  185. data/test/test-parser-yaml.rb +57 -0
  186. data/test/test-parser-yaml.yaml +1749 -0
  187. data/test/test-rule.rb +14 -15
  188. data/test/test-rule.yaml +6 -3
  189. data/test/test-users-guide.rb +75 -0
  190. data/test/test-validator.rb +77 -52
  191. data/test/test-validator.yaml +168 -6
  192. data/test/test-yaml-parser.rb +47 -0
  193. data/test/{test-yamlparser.yaml → test-yaml-parser.yaml} +159 -52
  194. data/test/test.rb +37 -21
  195. metadata +136 -37
  196. data/COPYING +0 -340
  197. data/ChangeLog +0 -70
  198. data/doc-api/classes/YamlHelper.html +0 -259
  199. data/lib/kwalify/util/yaml-helper.rb +0 -82
  200. data/test/test-yamlparser.rb +0 -58
  201. data/test/tmp.dir/User.java +0 -43
  202. data/test/tmp.dir/action1.document +0 -18
  203. data/test/tmp.dir/action1.schema +0 -32
  204. data/test/tmp.dir/action2.document +0 -18
  205. data/test/tmp.dir/action2.schema +0 -32
  206. data/test/tmp.dir/emacs.document +0 -6
  207. data/test/tmp.dir/emacs.schema +0 -6
  208. data/test/tmp.dir/meta1.document +0 -0
  209. data/test/tmp.dir/meta2.document +0 -0
  210. data/test/tmp.dir/meta2.schema +0 -3
  211. data/test/tmp.dir/silent1.schema +0 -3
  212. data/test/tmp.dir/silent2.document +0 -7
  213. data/test/tmp.dir/silent2.schema +0 -3
  214. data/test/tmp.dir/stream.invalid +0 -8
  215. data/test/tmp.dir/stream.schema +0 -3
  216. data/test/tmp.dir/stream.valid +0 -8
  217. data/test/tmp.dir/untabify.document +0 -5
  218. data/test/tmp.dir/untabify.schema +0 -10
  219. data/todo.txt +0 -34
data/setup.rb CHANGED
@@ -1,70 +1,20 @@
1
1
  #
2
2
  # setup.rb
3
3
  #
4
- # Copyright (c) 2000-2004 Minero Aoki
4
+ # Copyright (c) 2000-2005 Minero Aoki
5
5
  #
6
6
  # This program is free software.
7
7
  # You can distribute/modify this program under the terms of
8
- # the GNU Lesser General Public License version 2.1.
8
+ # the GNU LGPL, Lesser General Public License version 2.1.
9
9
  #
10
10
 
11
- #
12
- # For backward compatibility
13
- #
14
-
15
- unless Enumerable.method_defined?(:map)
11
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
16
12
  module Enumerable
17
13
  alias map collect
18
14
  end
19
15
  end
20
16
 
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)
17
+ unless File.respond_to?(:read) # Ruby 1.6
68
18
  def File.read(fname)
69
19
  open(fname) {|f|
70
20
  return f.read
@@ -72,9 +22,13 @@ unless File.respond_to?(:read)
72
22
  end
73
23
  end
74
24
 
75
- #
76
- # Application independent utilities
77
- #
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
78
32
 
79
33
  def File.binread(fname)
80
34
  open(fname, 'rb') {|f|
@@ -82,311 +36,512 @@ def File.binread(fname)
82
36
  }
83
37
  end
84
38
 
85
- # for corrupted windows stat(2)
39
+ # for corrupted Windows' stat(2)
86
40
  def File.dir?(path)
87
41
  File.directory?((path[-1,1] == '/') ? path : path + '/')
88
42
  end
89
43
 
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
44
 
107
45
  class ConfigTable
108
46
 
109
- c = ::Config::CONFIG
47
+ include Enumerable
110
48
 
111
- rubypath = c['bindir'] + '/' + c['ruby_install_name']
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
112
59
 
113
- major = c['MAJOR'].to_i
114
- minor = c['MINOR'].to_i
115
- teeny = c['TEENY'].to_i
116
- version = "#{major}.#{minor}"
60
+ attr_accessor :install_prefix
61
+ attr_accessor :config_opt
117
62
 
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
- }
63
+ attr_writer :verbose
127
64
 
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
65
+ def verbose?
66
+ @verbose
206
67
  end
207
68
 
208
- SAVE_FILE = 'config.save'
69
+ attr_writer :no_harm
209
70
 
210
- def ConfigTable.each_name(&block)
211
- keys().each(&block)
71
+ def no_harm?
72
+ @no_harm
212
73
  end
213
74
 
214
- def ConfigTable.keys
215
- DESCRIPTER.map {|name, *dummy| name }
75
+ def [](key)
76
+ lookup(key).resolve(self)
216
77
  end
217
78
 
218
- def ConfigTable.each_definition(&block)
219
- DESCRIPTER.each(&block)
79
+ def []=(key, val)
80
+ lookup(key).set val
220
81
  end
221
82
 
222
- def ConfigTable.get_entry(name)
223
- name, ent = DESCRIPTER.assoc(name)
224
- ent
83
+ def names
84
+ @items.map {|i| i.name }
225
85
  end
226
86
 
227
- def ConfigTable.get_entry!(name)
228
- get_entry(name) or raise ArgumentError, "no such config: #{name}"
87
+ def each(&block)
88
+ @items.each(&block)
229
89
  end
230
90
 
231
- def ConfigTable.add_entry(name, vals)
232
- ConfigTable::DESCRIPTER.push [name,vals]
91
+ def key?(name)
92
+ @table.key?(name)
233
93
  end
234
94
 
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 }
95
+ def lookup(name)
96
+ @table[name] or setup_rb_error "no such config item: #{name}"
238
97
  end
239
98
 
240
- def ConfigTable.config_key?(name)
241
- get_entry(name) ? true : false
99
+ def add(item)
100
+ @items.push item
101
+ @table[item.name] = item
242
102
  end
243
103
 
244
- def ConfigTable.bool_config?(name)
245
- ent = get_entry(name) or return false
246
- ent[1] == 'yes/no'
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
247
109
  end
248
110
 
249
- def ConfigTable.value_config?(name)
250
- ent = get_entry(name) or return false
251
- ent[1] != 'yes/no'
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
252
115
  end
253
116
 
254
- def ConfigTable.path_config?(name)
255
- ent = get_entry(name) or return false
256
- ent[1] == 'path'
117
+ def savefile
118
+ '.config'
257
119
  end
258
120
 
259
-
260
- class << self
261
- alias newobj new
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
262
130
  end
263
131
 
264
- def ConfigTable.new
265
- c = newobj()
266
- c.initialize_from_table
267
- c
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
+ }
268
139
  end
269
140
 
270
- def ConfigTable.load
271
- c = newobj()
272
- c.initialize_from_file
273
- c
141
+ def load_standard_entries
142
+ standard_entries(@rbconfig).each do |ent|
143
+ add ent
144
+ end
274
145
  end
275
146
 
276
- def initialize_from_table
277
- @table = {}
278
- DESCRIPTER.each do |k, (default, vname, desc, default2)|
279
- @table[k] = default
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']}"
280
187
  end
281
- end
188
+ parameterize = lambda {|path|
189
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
190
+ }
282
191
 
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
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'
290
196
  end
291
- end
292
197
 
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
- }
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
299
264
  end
300
265
 
301
- def []=(k, v)
302
- raise InstallError, "unknown config option #{k}"\
303
- unless ConfigTable.config_key?(k)
304
- @table[k] = v
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
+ @items.freeze
300
+ @table.freeze
301
+ @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
305
302
  end
306
-
307
- def [](key)
308
- return nil unless @table[key]
309
- @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
303
+
304
+ def parse_opt(opt)
305
+ m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
306
+ m.to_a[1,2]
310
307
  end
311
308
 
312
- def set_raw(key, val)
313
- @table[key] = val
309
+ def dllext
310
+ @rbconfig['DLEXT']
314
311
  end
315
312
 
316
- def get_raw(key)
317
- @table[key]
313
+ def value_config?(name)
314
+ lookup(name).value?
318
315
  end
319
316
 
320
- end
317
+ class Item
318
+ def initialize(name, template, default, desc)
319
+ @name = name.freeze
320
+ @template = template
321
+ @value = default
322
+ @default = default
323
+ @description = desc
324
+ end
321
325
 
326
+ attr_reader :name
327
+ attr_reader :description
322
328
 
323
- module MetaConfigAPI
329
+ attr_accessor :default
330
+ alias help_default default
324
331
 
325
- def eval_file_ifexist(fname)
326
- instance_eval File.read(fname), fname, 1 if File.file?(fname)
327
- end
332
+ def help_opt
333
+ "--#{@name}=#{@template}"
334
+ end
328
335
 
329
- def config_names
330
- ConfigTable.keys
331
- end
336
+ def value?
337
+ true
338
+ end
332
339
 
333
- def config?(name)
334
- ConfigTable.config_key?(name)
335
- end
340
+ def value
341
+ @value
342
+ end
343
+
344
+ def resolve(table)
345
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
346
+ end
336
347
 
337
- def bool_config?(name)
338
- ConfigTable.bool_config?(name)
348
+ def set(val)
349
+ @value = check(val)
350
+ end
351
+
352
+ private
353
+
354
+ def check(val)
355
+ setup_rb_error "config: --#{name} requires argument" unless val
356
+ val
357
+ end
339
358
  end
340
359
 
341
- def value_config?(name)
342
- ConfigTable.value_config?(name)
360
+ class BoolItem < Item
361
+ def config_type
362
+ 'bool'
363
+ end
364
+
365
+ def help_opt
366
+ "--#{@name}"
367
+ end
368
+
369
+ private
370
+
371
+ def check(val)
372
+ return 'yes' unless val
373
+ case val
374
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
375
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
376
+ else
377
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378
+ end
379
+ end
343
380
  end
344
381
 
345
- def path_config?(name)
346
- ConfigTable.path_config?(name)
382
+ class PathItem < Item
383
+ def config_type
384
+ 'path'
385
+ end
386
+
387
+ private
388
+
389
+ def check(path)
390
+ setup_rb_error "config: --#{@name} requires argument" unless path
391
+ path[0,1] == '$' ? path : File.expand_path(path)
392
+ end
347
393
  end
348
394
 
349
- def add_config(name, argname, default, desc)
350
- ConfigTable.add_entry name,[default,argname,desc]
395
+ class ProgramItem < Item
396
+ def config_type
397
+ 'program'
398
+ end
351
399
  end
352
400
 
353
- def add_path_config(name, default, desc)
354
- add_config name, 'path', default, desc
401
+ class SelectItem < Item
402
+ def initialize(name, selection, default, desc)
403
+ super
404
+ @ok = selection.split('/')
405
+ end
406
+
407
+ def config_type
408
+ 'select'
409
+ end
410
+
411
+ private
412
+
413
+ def check(val)
414
+ unless @ok.include?(val.strip)
415
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
416
+ end
417
+ val.strip
418
+ end
355
419
  end
356
420
 
357
- def add_bool_config(name, default, desc)
358
- add_config name, 'yes/no', default ? 'yes' : 'no', desc
421
+ class ExecItem < Item
422
+ def initialize(name, selection, desc, &block)
423
+ super name, selection, nil, desc
424
+ @ok = selection.split('/')
425
+ @action = block
426
+ end
427
+
428
+ def config_type
429
+ 'exec'
430
+ end
431
+
432
+ def value?
433
+ false
434
+ end
435
+
436
+ def resolve(table)
437
+ setup_rb_error "$#{name()} wrongly used as option value"
438
+ end
439
+
440
+ undef set
441
+
442
+ def evaluate(val, table)
443
+ v = val.strip.downcase
444
+ unless @ok.include?(v)
445
+ setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
446
+ end
447
+ @action.call v, table
448
+ end
359
449
  end
360
450
 
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
451
+ class PackageSelectionItem < Item
452
+ def initialize(name, template, default, help_default, desc)
453
+ super name, template, default, desc
454
+ @help_default = help_default
455
+ end
456
+
457
+ attr_reader :help_default
458
+
459
+ def config_type
460
+ 'package'
461
+ end
462
+
463
+ private
464
+
465
+ def check(val)
466
+ unless File.dir?("packages/#{val}")
467
+ setup_rb_error "config: no such package: #{val}"
468
+ end
469
+ val
366
470
  end
367
471
  end
368
472
 
369
- def remove_config(name)
370
- ent = ConfigTable.get_entry(name)
371
- ConfigTable.remove_entry name
372
- ent
473
+ class MetaConfigEnvironment
474
+ def initialize(config, installer)
475
+ @config = config
476
+ @installer = installer
477
+ end
478
+
479
+ def config_names
480
+ @config.names
481
+ end
482
+
483
+ def config?(name)
484
+ @config.key?(name)
485
+ end
486
+
487
+ def bool_config?(name)
488
+ @config.lookup(name).config_type == 'bool'
489
+ end
490
+
491
+ def path_config?(name)
492
+ @config.lookup(name).config_type == 'path'
493
+ end
494
+
495
+ def value_config?(name)
496
+ @config.lookup(name).config_type != 'exec'
497
+ end
498
+
499
+ def add_config(item)
500
+ @config.add item
501
+ end
502
+
503
+ def add_bool_config(name, default, desc)
504
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
505
+ end
506
+
507
+ def add_path_config(name, default, desc)
508
+ @config.add PathItem.new(name, 'path', default, desc)
509
+ end
510
+
511
+ def set_config_default(name, default)
512
+ @config.lookup(name).default = default
513
+ end
514
+
515
+ def remove_config(name)
516
+ @config.remove(name)
517
+ end
518
+
519
+ # For only multipackage
520
+ def packages
521
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
522
+ @installer.packages
523
+ end
524
+
525
+ # For only multipackage
526
+ def declare_packages(list)
527
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
528
+ @installer.packages = list
529
+ end
373
530
  end
374
531
 
375
- end
532
+ end # class ConfigTable
376
533
 
377
- #
378
- # File Operations
379
- #
380
534
 
535
+ # This module requires: #verbose?, #no_harm?
381
536
  module FileOperations
382
537
 
383
538
  def mkdir_p(dirname, prefix = nil)
384
- dirname = prefix + dirname if prefix
539
+ dirname = prefix + File.expand_path(dirname) if prefix
385
540
  $stderr.puts "mkdir -p #{dirname}" if verbose?
386
541
  return if no_harm?
387
542
 
388
- # does not check '/'... it's too abnormal case
389
- dirs = dirname.split(%r<(?=/)>)
543
+ # Does not check '/', it's too abnormal.
544
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
390
545
  if /\A[a-z]:\z/i =~ dirs[0]
391
546
  disk = dirs.shift
392
547
  dirs[0] = disk + dirs[0]
@@ -397,54 +552,78 @@ module FileOperations
397
552
  end
398
553
  end
399
554
 
400
- def rm_f(fname)
401
- $stderr.puts "rm -f #{fname}" if verbose?
555
+ def rm_f(path)
556
+ $stderr.puts "rm -f #{path}" if verbose?
402
557
  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
558
+ force_remove_file path
408
559
  end
409
560
 
410
- def rm_rf(dn)
411
- $stderr.puts "rm -rf #{dn}" if verbose?
561
+ def rm_rf(path)
562
+ $stderr.puts "rm -rf #{path}" if verbose?
412
563
  return if no_harm?
564
+ remove_tree path
565
+ end
566
+
567
+ def remove_tree(path)
568
+ if File.symlink?(path)
569
+ remove_file path
570
+ elsif File.dir?(path)
571
+ remove_tree0 path
572
+ else
573
+ force_remove_file path
574
+ end
575
+ end
413
576
 
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
- }
577
+ def remove_tree0(path)
578
+ Dir.foreach(path) do |ent|
579
+ next if ent == '.'
580
+ next if ent == '..'
581
+ entpath = "#{path}/#{ent}"
582
+ if File.symlink?(entpath)
583
+ remove_file entpath
584
+ elsif File.dir?(entpath)
585
+ remove_tree0 entpath
422
586
  else
423
- verbose_off {
424
- rm_f fn
425
- }
587
+ force_remove_file entpath
426
588
  end
427
589
  end
428
- Dir.chdir '..'
429
- Dir.rmdir dn
590
+ begin
591
+ Dir.rmdir path
592
+ rescue Errno::ENOTEMPTY
593
+ # directory may not be empty
594
+ end
430
595
  end
431
596
 
432
597
  def move_file(src, dest)
433
- File.unlink dest if File.exist?(dest)
598
+ force_remove_file dest
434
599
  begin
435
600
  File.rename src, dest
436
601
  rescue
437
- File.open(dest, 'wb') {|f| f.write File.binread(src) }
602
+ File.open(dest, 'wb') {|f|
603
+ f.write File.binread(src)
604
+ }
438
605
  File.chmod File.stat(src).mode, dest
439
606
  File.unlink src
440
607
  end
441
608
  end
442
609
 
610
+ def force_remove_file(path)
611
+ begin
612
+ remove_file path
613
+ rescue
614
+ end
615
+ end
616
+
617
+ def remove_file(path)
618
+ File.chmod 0777, path
619
+ File.unlink path
620
+ end
621
+
443
622
  def install(from, dest, mode, prefix = nil)
444
623
  $stderr.puts "install #{from} #{dest}" if verbose?
445
624
  return if no_harm?
446
625
 
447
- realdest = prefix + dest if prefix
626
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
448
627
  realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
449
628
  str = File.binread(from)
450
629
  if diff?(str, realdest)
@@ -471,68 +650,42 @@ module FileOperations
471
650
  new_content != File.binread(path)
472
651
  end
473
652
 
474
- def command(str)
475
- $stderr.puts str if verbose?
476
- system str or raise RuntimeError, "'system #{str}' failed"
653
+ def command(*args)
654
+ $stderr.puts args.join(' ') if verbose?
655
+ system(*args) or raise RuntimeError,
656
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
477
657
  end
478
658
 
479
- def ruby(str)
480
- command config('ruby-prog') + ' ' + str
659
+ def ruby(*args)
660
+ command config('rubyprog'), *args
481
661
  end
482
662
 
483
- def make(task = '')
484
- command config('make-prog') + ' ' + task
663
+ def make(task = nil)
664
+ command(*[config('makeprog'), task].compact)
485
665
  end
486
666
 
487
667
  def extdir?(dir)
488
- File.exist?(dir + '/MANIFEST')
668
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
489
669
  end
490
670
 
491
- def all_files_in(dirname)
492
- Dir.open(dirname) {|d|
493
- return d.select {|ent| File.file?("#{dirname}/#{ent}") }
671
+ def files_of(dir)
672
+ Dir.open(dir) {|d|
673
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
494
674
  }
495
675
  end
496
676
 
497
- REJECT_DIRS = %w(
498
- CVS SCCS RCS CVS.adm
499
- )
677
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
500
678
 
501
- def all_dirs_in(dirname)
502
- Dir.open(dirname) {|d|
503
- return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
679
+ def directories_of(dir)
680
+ Dir.open(dir) {|d|
681
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
504
682
  }
505
683
  end
506
684
 
507
685
  end
508
686
 
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
687
 
688
+ # This module requires: #srcdir_root, #objdir_root, #relpath
536
689
  module HookScriptAPI
537
690
 
538
691
  def get_config(key)
@@ -541,6 +694,7 @@ module HookScriptAPI
541
694
 
542
695
  alias config get_config
543
696
 
697
+ # obsolete: use metaconfig to change configuration
544
698
  def set_config(key, val)
545
699
  @config[key] = val
546
700
  end
@@ -549,10 +703,6 @@ module HookScriptAPI
549
703
  # srcdir/objdir (works only in the package directory)
550
704
  #
551
705
 
552
- #abstract srcdir_root
553
- #abstract objdir_root
554
- #abstract relpath
555
-
556
706
  def curr_srcdir
557
707
  "#{srcdir_root()}/#{relpath()}"
558
708
  end
@@ -574,7 +724,7 @@ module HookScriptAPI
574
724
  end
575
725
 
576
726
  def srcfile?(path)
577
- File.file? srcfile(path)
727
+ File.file?(srcfile(path))
578
728
  end
579
729
 
580
730
  def srcentries(path = '.')
@@ -600,35 +750,53 @@ end
600
750
 
601
751
  class ToplevelInstaller
602
752
 
603
- Version = '3.2.4'
604
- Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
753
+ Version = '3.4.1'
754
+ Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
605
755
 
606
756
  TASKS = [
757
+ [ 'all', 'do config, setup, then install' ],
607
758
  [ 'config', 'saves your configurations' ],
608
759
  [ 'show', 'shows current configuration' ],
609
760
  [ 'setup', 'compiles ruby extentions and others' ],
610
761
  [ 'install', 'installs files' ],
762
+ [ 'test', 'run all tests in test/' ],
611
763
  [ 'clean', "does `make clean' for each extention" ],
612
764
  [ 'distclean',"does `make distclean' for each extention" ]
613
765
  ]
614
766
 
615
767
  def ToplevelInstaller.invoke
616
- instance().invoke
768
+ config = ConfigTable.new(load_rbconfig())
769
+ config.load_standard_entries
770
+ config.load_multipackage_entries if multipackage?
771
+ config.fixup
772
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
773
+ klass.new(File.dirname($0), config).invoke
617
774
  end
618
775
 
619
- @singleton = nil
620
-
621
- def ToplevelInstaller.instance
622
- @singleton ||= new(File.dirname($0))
623
- @singleton
776
+ def ToplevelInstaller.multipackage?
777
+ File.dir?(File.dirname($0) + '/packages')
624
778
  end
625
779
 
626
- include MetaConfigAPI
780
+ def ToplevelInstaller.load_rbconfig
781
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
782
+ ARGV.delete(arg)
783
+ load File.expand_path(arg.split(/=/, 2)[1])
784
+ $".push 'rbconfig.rb'
785
+ else
786
+ require 'rbconfig'
787
+ end
788
+ ::Config::CONFIG
789
+ end
627
790
 
628
- def initialize(ardir_root)
629
- @config = nil
630
- @options = { 'verbose' => true }
791
+ def initialize(ardir_root, config)
631
792
  @ardir = File.expand_path(ardir_root)
793
+ @config = config
794
+ # cache
795
+ @valid_task_re = nil
796
+ end
797
+
798
+ def config(key)
799
+ @config[key]
632
800
  end
633
801
 
634
802
  def inspect
@@ -637,33 +805,34 @@ class ToplevelInstaller
637
805
 
638
806
  def invoke
639
807
  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
808
+ case task = parsearg_global()
809
+ when nil, 'all'
810
+ parsearg_config
811
+ init_installers
812
+ exec_config
813
+ exec_setup
814
+ exec_install
660
815
  else
661
- ConfigTable.load
816
+ case task
817
+ when 'config', 'test'
818
+ ;
819
+ when 'clean', 'distclean'
820
+ @config.load_savefile if File.exist?(@config.savefile)
821
+ else
822
+ @config.load_savefile
823
+ end
824
+ __send__ "parsearg_#{task}"
825
+ init_installers
826
+ __send__ "exec_#{task}"
662
827
  end
663
828
  end
829
+
830
+ def run_metaconfigs
831
+ @config.load_script "#{@ardir}/metaconfig"
832
+ end
664
833
 
665
834
  def init_installers
666
- @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
835
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
667
836
  end
668
837
 
669
838
  #
@@ -687,96 +856,91 @@ class ToplevelInstaller
687
856
  #
688
857
 
689
858
  def parsearg_global
690
- valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
691
-
692
859
  while arg = ARGV.shift
693
860
  case arg
694
861
  when /\A\w+\z/
695
- raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
862
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
696
863
  return arg
697
-
698
864
  when '-q', '--quiet'
699
- @options['verbose'] = false
700
-
701
- when '--verbose'
702
- @options['verbose'] = true
703
-
704
- when '-h', '--help'
865
+ @config.verbose = false
866
+ when '--verbose'
867
+ @config.verbose = true
868
+ when '--help'
705
869
  print_usage $stdout
706
870
  exit 0
707
-
708
- when '-v', '--version'
871
+ when '--version'
709
872
  puts "#{File.basename($0)} version #{Version}"
710
873
  exit 0
711
-
712
874
  when '--copyright'
713
875
  puts Copyright
714
876
  exit 0
715
-
716
877
  else
717
- raise InstallError, "unknown global option '#{arg}'"
878
+ setup_rb_error "unknown global option '#{arg}'"
718
879
  end
719
880
  end
881
+ nil
882
+ end
720
883
 
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
884
+ def valid_task?(t)
885
+ valid_task_re() =~ t
728
886
  end
729
887
 
888
+ def valid_task_re
889
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
890
+ end
730
891
 
731
892
  def parsearg_no_options
732
- raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
733
- unless ARGV.empty?
893
+ unless ARGV.empty?
894
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
895
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
896
+ end
734
897
  end
735
898
 
736
899
  alias parsearg_show parsearg_no_options
737
900
  alias parsearg_setup parsearg_no_options
901
+ alias parsearg_test parsearg_no_options
738
902
  alias parsearg_clean parsearg_no_options
739
903
  alias parsearg_distclean parsearg_no_options
740
904
 
741
905
  def parsearg_config
742
- re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
743
- @options['config-opt'] = []
744
-
906
+ evalopt = []
907
+ set = []
908
+ @config.config_opt = []
745
909
  while i = ARGV.shift
746
910
  if /\A--?\z/ =~ i
747
- @options['config-opt'] = ARGV.dup
911
+ @config.config_opt = ARGV.dup
748
912
  break
749
913
  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
914
+ name, value = *@config.parse_opt(i)
915
+ if @config.value_config?(name)
916
+ @config[name] = value
758
917
  else
759
- raise InstallError, "config: --#{name} requires argument"\
760
- unless ConfigTable.bool_config?(name)
761
- value = 'yes'
918
+ evalopt.push [name, value]
762
919
  end
763
- @config[name] = value
920
+ set.push name
921
+ end
922
+ evalopt.each do |name, value|
923
+ @config.lookup(name).evaluate value, @config
924
+ end
925
+ # Check if configuration is valid
926
+ set.each do |n|
927
+ @config[n] if @config.value_config?(n)
764
928
  end
765
929
  end
766
930
 
767
931
  def parsearg_install
768
- @options['no-harm'] = false
769
- @options['install-prefix'] = ''
932
+ @config.no_harm = false
933
+ @config.install_prefix = ''
770
934
  while a = ARGV.shift
771
935
  case a
772
- when /\A--no-harm\z/
773
- @options['no-harm'] = true
774
- when /\A--prefix=(.*)\z/
775
- path = $1
936
+ when '--no-harm'
937
+ @config.no_harm = true
938
+ when /\A--prefix=/
939
+ path = a.split(/=/, 2)[1]
776
940
  path = File.expand_path(path) unless path[0,1] == '/'
777
- @options['install-prefix'] = path
941
+ @config.install_prefix = path
778
942
  else
779
- raise InstallError, "install: unknown option #{a}"
943
+ setup_rb_error "install: unknown option #{a}"
780
944
  end
781
945
  end
782
946
  end
@@ -791,39 +955,31 @@ EOS
791
955
  out.puts " ruby #{File.basename $0} <global option>"
792
956
  out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
793
957
 
794
- fmt = " %-20s %s\n"
958
+ fmt = " %-24s %s\n"
795
959
  out.puts
796
960
  out.puts 'Global options:'
797
961
  out.printf fmt, '-q,--quiet', 'suppress message outputs'
798
962
  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'
963
+ out.printf fmt, ' --help', 'print this message'
964
+ out.printf fmt, ' --version', 'print version and quit'
801
965
  out.printf fmt, ' --copyright', 'print copyright and quit'
802
-
803
966
  out.puts
804
967
  out.puts 'Tasks:'
805
968
  TASKS.each do |name, desc|
806
- out.printf " %-10s %s\n", name, desc
969
+ out.printf fmt, name, desc
807
970
  end
808
971
 
972
+ fmt = " %-24s %s [%s]\n"
809
973
  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
974
+ out.puts 'Options for CONFIG or ALL:'
975
+ @config.each do |item|
976
+ out.printf fmt, item.help_opt, item.description, item.help_default
816
977
  end
817
- out.printf " %-20s %s [%s]\n",
818
- '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
819
-
978
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
820
979
  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
-
980
+ out.puts 'Options for INSTALL:'
981
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
982
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
827
983
  out.puts
828
984
  end
829
985
 
@@ -844,13 +1000,13 @@ EOS
844
1000
  @installer.exec_install
845
1001
  end
846
1002
 
1003
+ def exec_test
1004
+ @installer.exec_test
1005
+ end
1006
+
847
1007
  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
1008
+ @config.each do |i|
1009
+ printf "%-20s %s\n", i.name, i.value if i.value?
854
1010
  end
855
1011
  end
856
1012
 
@@ -862,36 +1018,45 @@ EOS
862
1018
  @installer.exec_distclean
863
1019
  end
864
1020
 
865
- end
1021
+ end # class ToplevelInstaller
866
1022
 
867
1023
 
868
1024
  class ToplevelInstallerMulti < ToplevelInstaller
869
1025
 
870
- include HookUtils
871
- include HookScriptAPI
872
1026
  include FileOperations
873
1027
 
874
- def initialize(ardir)
1028
+ def initialize(ardir_root, config)
875
1029
  super
876
- @packages = all_dirs_in("#{@ardir}/packages")
1030
+ @packages = directories_of("#{@ardir}/packages")
877
1031
  raise 'no package exists' if @packages.empty?
1032
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
878
1033
  end
879
1034
 
880
1035
  def run_metaconfigs
881
- eval_file_ifexist "#{@ardir}/metaconfig"
1036
+ @config.load_script "#{@ardir}/metaconfig", self
882
1037
  @packages.each do |name|
883
- eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
1038
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
884
1039
  end
885
1040
  end
886
1041
 
1042
+ attr_reader :packages
1043
+
1044
+ def packages=(list)
1045
+ raise 'package list is empty' if list.empty?
1046
+ list.each do |name|
1047
+ raise "directory packages/#{name} does not exist"\
1048
+ unless File.dir?("#{@ardir}/packages/#{name}")
1049
+ end
1050
+ @packages = list
1051
+ end
1052
+
887
1053
  def init_installers
888
1054
  @installers = {}
889
1055
  @packages.each do |pack|
890
- @installers[pack] = Installer.new(@config, @options,
1056
+ @installers[pack] = Installer.new(@config,
891
1057
  "#{@ardir}/packages/#{pack}",
892
1058
  "packages/#{pack}")
893
1059
  end
894
-
895
1060
  with = extract_selection(config('with'))
896
1061
  without = extract_selection(config('without'))
897
1062
  @selected = @installers.keys.select {|name|
@@ -903,8 +1068,7 @@ class ToplevelInstallerMulti < ToplevelInstaller
903
1068
  def extract_selection(list)
904
1069
  a = list.split(/,/)
905
1070
  a.each do |name|
906
- raise InstallError, "no such package: #{name}" \
907
- unless @installers.key?(name)
1071
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
908
1072
  end
909
1073
  a
910
1074
  end
@@ -916,21 +1080,6 @@ class ToplevelInstallerMulti < ToplevelInstaller
916
1080
  f.puts
917
1081
  end
918
1082
 
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
1083
  #
935
1084
  # Task Handlers
936
1085
  #
@@ -954,15 +1103,21 @@ class ToplevelInstallerMulti < ToplevelInstaller
954
1103
  run_hook 'post-install'
955
1104
  end
956
1105
 
1106
+ def exec_test
1107
+ run_hook 'pre-test'
1108
+ each_selected_installers {|inst| inst.exec_test }
1109
+ run_hook 'post-test'
1110
+ end
1111
+
957
1112
  def exec_clean
958
- rm_f 'config.save'
1113
+ rm_f @config.savefile
959
1114
  run_hook 'pre-clean'
960
1115
  each_selected_installers {|inst| inst.exec_clean }
961
1116
  run_hook 'post-clean'
962
1117
  end
963
1118
 
964
1119
  def exec_distclean
965
- rm_f 'config.save'
1120
+ rm_f @config.savefile
966
1121
  run_hook 'pre-distclean'
967
1122
  each_selected_installers {|inst| inst.exec_distclean }
968
1123
  run_hook 'post-distclean'
@@ -975,7 +1130,7 @@ class ToplevelInstallerMulti < ToplevelInstaller
975
1130
  def each_selected_installers
976
1131
  Dir.mkdir 'packages' unless File.dir?('packages')
977
1132
  @selected.each do |pack|
978
- $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1133
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
979
1134
  Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
980
1135
  Dir.chdir "packages/#{pack}"
981
1136
  yield @installers[pack]
@@ -983,28 +1138,32 @@ class ToplevelInstallerMulti < ToplevelInstaller
983
1138
  end
984
1139
  end
985
1140
 
1141
+ def run_hook(id)
1142
+ @root_installer.run_hook id
1143
+ end
1144
+
1145
+ # module FileOperations requires this
986
1146
  def verbose?
987
- @options['verbose']
1147
+ @config.verbose?
988
1148
  end
989
1149
 
1150
+ # module FileOperations requires this
990
1151
  def no_harm?
991
- @options['no-harm']
1152
+ @config.no_harm?
992
1153
  end
993
1154
 
994
- end
1155
+ end # class ToplevelInstallerMulti
995
1156
 
996
1157
 
997
1158
  class Installer
998
1159
 
999
- FILETYPES = %w( bin lib ext data )
1160
+ FILETYPES = %w( bin lib ext data conf man )
1000
1161
 
1001
- include HookScriptAPI
1002
- include HookUtils
1003
1162
  include FileOperations
1163
+ include HookScriptAPI
1004
1164
 
1005
- def initialize(config, opt, srcroot, objroot)
1165
+ def initialize(config, srcroot, objroot)
1006
1166
  @config = config
1007
- @options = opt
1008
1167
  @srcdir = File.expand_path(srcroot)
1009
1168
  @objdir = File.expand_path(objroot)
1010
1169
  @currdir = '.'
@@ -1014,8 +1173,11 @@ class Installer
1014
1173
  "#<#{self.class} #{File.basename(@srcdir)}>"
1015
1174
  end
1016
1175
 
1176
+ def noop(rel)
1177
+ end
1178
+
1017
1179
  #
1018
- # Hook Script API bases
1180
+ # Hook Script API base methods
1019
1181
  #
1020
1182
 
1021
1183
  def srcdir_root
@@ -1031,23 +1193,25 @@ class Installer
1031
1193
  end
1032
1194
 
1033
1195
  #
1034
- # configs/options
1196
+ # Config Access
1035
1197
  #
1036
1198
 
1037
- def no_harm?
1038
- @options['no-harm']
1199
+ # module FileOperations requires this
1200
+ def verbose?
1201
+ @config.verbose?
1039
1202
  end
1040
1203
 
1041
- def verbose?
1042
- @options['verbose']
1204
+ # module FileOperations requires this
1205
+ def no_harm?
1206
+ @config.no_harm?
1043
1207
  end
1044
1208
 
1045
1209
  def verbose_off
1046
1210
  begin
1047
- save, @options['verbose'] = @options['verbose'], false
1211
+ save, @config.verbose = @config.verbose?, false
1048
1212
  yield
1049
1213
  ensure
1050
- @options['verbose'] = save
1214
+ @config.verbose = save
1051
1215
  end
1052
1216
  end
1053
1217
 
@@ -1059,22 +1223,19 @@ class Installer
1059
1223
  exec_task_traverse 'config'
1060
1224
  end
1061
1225
 
1062
- def config_dir_bin(rel)
1063
- end
1064
-
1065
- def config_dir_lib(rel)
1066
- end
1226
+ alias config_dir_bin noop
1227
+ alias config_dir_lib noop
1067
1228
 
1068
1229
  def config_dir_ext(rel)
1069
1230
  extconf if extdir?(curr_srcdir())
1070
1231
  end
1071
1232
 
1072
- def extconf
1073
- opt = @options['config-opt'].join(' ')
1074
- command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
1075
- end
1233
+ alias config_dir_data noop
1234
+ alias config_dir_conf noop
1235
+ alias config_dir_man noop
1076
1236
 
1077
- def config_dir_data(rel)
1237
+ def extconf
1238
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1078
1239
  end
1079
1240
 
1080
1241
  #
@@ -1086,46 +1247,90 @@ class Installer
1086
1247
  end
1087
1248
 
1088
1249
  def setup_dir_bin(rel)
1089
- all_files_in(curr_srcdir()).each do |fname|
1090
- adjust_shebang "#{curr_srcdir()}/#{fname}"
1250
+ files_of(curr_srcdir()).each do |fname|
1251
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
1091
1252
  end
1092
1253
  end
1093
1254
 
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*/
1255
+ alias setup_dir_lib noop
1256
+
1257
+ def setup_dir_ext(rel)
1258
+ make if extdir?(curr_srcdir())
1259
+ end
1260
+
1261
+ alias setup_dir_data noop
1262
+ alias setup_dir_conf noop
1263
+ alias setup_dir_man noop
1099
1264
 
1100
- def adjust_shebang(path)
1265
+ def update_shebang_line(path)
1101
1266
  return if no_harm?
1267
+ return if config('shebang') == 'never'
1268
+ old = Shebang.load(path)
1269
+ if old
1270
+ $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
1271
+ new = new_shebang(old)
1272
+ return if new.to_s == old.to_s
1273
+ else
1274
+ return unless config('shebang') == 'all'
1275
+ new = Shebang.new(config('rubypath'))
1276
+ end
1277
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1278
+ open_atomic_writer(path) {|output|
1279
+ File.open(path, 'rb') {|f|
1280
+ f.gets if old # discard
1281
+ output.puts new.to_s
1282
+ output.print f.read
1283
+ }
1284
+ }
1285
+ end
1286
+
1287
+ def new_shebang(old)
1288
+ if /\Aruby/ =~ File.basename(old.cmd)
1289
+ Shebang.new(config('rubypath'), old.args)
1290
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1291
+ Shebang.new(config('rubypath'), old.args[1..-1])
1292
+ else
1293
+ return old unless config('shebang') == 'all'
1294
+ Shebang.new(config('rubypath'))
1295
+ end
1296
+ end
1102
1297
 
1298
+ def open_atomic_writer(path, &block)
1103
1299
  tmpfile = File.basename(path) + '.tmp'
1104
1300
  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)
1301
+ File.open(tmpfile, 'wb', &block)
1302
+ File.rename tmpfile, File.basename(path)
1116
1303
  ensure
1117
1304
  File.unlink tmpfile if File.exist?(tmpfile)
1118
1305
  end
1119
1306
  end
1120
1307
 
1121
- def setup_dir_lib(rel)
1122
- end
1308
+ class Shebang
1309
+ def Shebang.load(path)
1310
+ line = nil
1311
+ File.open(path) {|f|
1312
+ line = f.gets
1313
+ }
1314
+ return nil unless /\A#!/ =~ line
1315
+ parse(line)
1316
+ end
1123
1317
 
1124
- def setup_dir_ext(rel)
1125
- make if extdir?(curr_srcdir())
1126
- end
1318
+ def Shebang.parse(line)
1319
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1320
+ new(cmd, args)
1321
+ end
1322
+
1323
+ def initialize(cmd, args = [])
1324
+ @cmd = cmd
1325
+ @args = args
1326
+ end
1327
+
1328
+ attr_reader :cmd
1329
+ attr_reader :args
1127
1330
 
1128
- def setup_dir_data(rel)
1331
+ def to_s
1332
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1333
+ end
1129
1334
  end
1130
1335
 
1131
1336
  #
@@ -1133,67 +1338,82 @@ class Installer
1133
1338
  #
1134
1339
 
1135
1340
  def exec_install
1341
+ rm_f 'InstalledFiles'
1136
1342
  exec_task_traverse 'install'
1137
1343
  end
1138
1344
 
1139
1345
  def install_dir_bin(rel)
1140
- install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755
1346
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1141
1347
  end
1142
1348
 
1143
1349
  def install_dir_lib(rel)
1144
- install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
1350
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1145
1351
  end
1146
1352
 
1147
1353
  def install_dir_ext(rel)
1148
1354
  return unless extdir?(curr_srcdir())
1149
- install_files ruby_extentions('.'),
1150
- "#{config('so-dir')}/#{File.dirname(rel)}",
1355
+ install_files rubyextentions('.'),
1356
+ "#{config('sodir')}/#{File.dirname(rel)}",
1151
1357
  0555
1152
1358
  end
1153
1359
 
1154
1360
  def install_dir_data(rel)
1155
- install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
1361
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1362
+ end
1363
+
1364
+ def install_dir_conf(rel)
1365
+ # FIXME: should not remove current config files
1366
+ # (rename previous file to .old/.org)
1367
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1368
+ end
1369
+
1370
+ def install_dir_man(rel)
1371
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1156
1372
  end
1157
1373
 
1158
1374
  def install_files(list, dest, mode)
1159
- mkdir_p dest, @options['install-prefix']
1375
+ mkdir_p dest, @config.install_prefix
1160
1376
  list.each do |fname|
1161
- install fname, dest, mode, @options['install-prefix']
1377
+ install fname, dest, mode, @config.install_prefix
1162
1378
  end
1163
1379
  end
1164
1380
 
1165
- def ruby_scripts
1166
- collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1381
+ def libfiles
1382
+ glob_reject(%w(*.y *.output), targetfiles())
1167
1383
  end
1168
-
1384
+
1385
+ def rubyextentions(dir)
1386
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1387
+ if ents.empty?
1388
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1389
+ end
1390
+ ents
1391
+ end
1392
+
1393
+ def targetfiles
1394
+ mapdir(existfiles() - hookfiles())
1395
+ end
1396
+
1397
+ def mapdir(ents)
1398
+ ents.map {|ent|
1399
+ if File.exist?(ent)
1400
+ then ent # objdir
1401
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1402
+ end
1403
+ }
1404
+ end
1405
+
1169
1406
  # picked up many entries from cvs-1.11.1/src/ignore.c
1170
- reject_patterns = %w(
1407
+ JUNK_FILES = %w(
1171
1408
  core RCSLOG tags TAGS .make.state
1172
1409
  .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1173
1410
  *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1174
1411
 
1175
1412
  *.org *.in .*
1176
1413
  )
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
1414
 
1195
1415
  def existfiles
1196
- all_files_in(curr_srcdir()) | all_files_in('.')
1416
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1197
1417
  end
1198
1418
 
1199
1419
  def hookfiles
@@ -1202,27 +1422,49 @@ class Installer
1202
1422
  }.flatten
1203
1423
  end
1204
1424
 
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
- }
1425
+ def glob_select(pat, ents)
1426
+ re = globs2re([pat])
1427
+ ents.select {|ent| re =~ ent }
1213
1428
  end
1214
1429
 
1215
- def ruby_extentions(dir)
1216
- _ruby_extentions(dir) or
1217
- raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
1430
+ def glob_reject(pats, ents)
1431
+ re = globs2re(pats)
1432
+ ents.reject {|ent| re =~ ent }
1218
1433
  end
1219
1434
 
1220
- DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
1435
+ GLOB2REGEX = {
1436
+ '.' => '\.',
1437
+ '$' => '\$',
1438
+ '#' => '\#',
1439
+ '*' => '.*'
1440
+ }
1221
1441
 
1222
- def _ruby_extentions(dir)
1223
- Dir.open(dir) {|d|
1224
- return d.select {|fname| DLEXT =~ fname }
1225
- }
1442
+ def globs2re(pats)
1443
+ /\A(?:#{
1444
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1445
+ })\z/
1446
+ end
1447
+
1448
+ #
1449
+ # TASK test
1450
+ #
1451
+
1452
+ TESTDIR = 'test'
1453
+
1454
+ def exec_test
1455
+ unless File.directory?('test')
1456
+ $stderr.puts 'no test in this package' if verbose?
1457
+ return
1458
+ end
1459
+ $stderr.puts 'Running tests...' if verbose?
1460
+ begin
1461
+ require 'test/unit'
1462
+ rescue LoadError
1463
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1464
+ end
1465
+ runner = Test::Unit::AutoRunner.new(true)
1466
+ runner.to_run << TESTDIR
1467
+ runner.run
1226
1468
  end
1227
1469
 
1228
1470
  #
@@ -1231,53 +1473,51 @@ class Installer
1231
1473
 
1232
1474
  def exec_clean
1233
1475
  exec_task_traverse 'clean'
1234
- rm_f 'config.save'
1476
+ rm_f @config.savefile
1235
1477
  rm_f 'InstalledFiles'
1236
1478
  end
1237
1479
 
1238
- def clean_dir_bin(rel)
1239
- end
1240
-
1241
- def clean_dir_lib(rel)
1242
- end
1480
+ alias clean_dir_bin noop
1481
+ alias clean_dir_lib noop
1482
+ alias clean_dir_data noop
1483
+ alias clean_dir_conf noop
1484
+ alias clean_dir_man noop
1243
1485
 
1244
1486
  def clean_dir_ext(rel)
1245
1487
  return unless extdir?(curr_srcdir())
1246
1488
  make 'clean' if File.file?('Makefile')
1247
1489
  end
1248
1490
 
1249
- def clean_dir_data(rel)
1250
- end
1251
-
1252
1491
  #
1253
1492
  # TASK distclean
1254
1493
  #
1255
1494
 
1256
1495
  def exec_distclean
1257
1496
  exec_task_traverse 'distclean'
1258
- rm_f 'config.save'
1497
+ rm_f @config.savefile
1259
1498
  rm_f 'InstalledFiles'
1260
1499
  end
1261
1500
 
1262
- def distclean_dir_bin(rel)
1263
- end
1264
-
1265
- def distclean_dir_lib(rel)
1266
- end
1501
+ alias distclean_dir_bin noop
1502
+ alias distclean_dir_lib noop
1267
1503
 
1268
1504
  def distclean_dir_ext(rel)
1269
1505
  return unless extdir?(curr_srcdir())
1270
1506
  make 'distclean' if File.file?('Makefile')
1271
1507
  end
1272
1508
 
1509
+ alias distclean_dir_data noop
1510
+ alias distclean_dir_conf noop
1511
+ alias distclean_dir_man noop
1512
+
1273
1513
  #
1274
- # lib
1514
+ # Traversing
1275
1515
  #
1276
1516
 
1277
1517
  def exec_task_traverse(task)
1278
1518
  run_hook "pre-#{task}"
1279
1519
  FILETYPES.each do |type|
1280
- if config('without-ext') == 'yes' and type == 'ext'
1520
+ if type == 'ext' and config('without-ext') == 'yes'
1281
1521
  $stderr.puts 'skipping ext/* by user option' if verbose?
1282
1522
  next
1283
1523
  end
@@ -1290,7 +1530,7 @@ class Installer
1290
1530
  dive_into(rel) {
1291
1531
  run_hook "pre-#{task}"
1292
1532
  __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1293
- all_dirs_in(curr_srcdir()).each do |d|
1533
+ directories_of(curr_srcdir()).each do |d|
1294
1534
  traverse task, "#{rel}/#{d}", mid
1295
1535
  end
1296
1536
  run_hook "post-#{task}"
@@ -1312,17 +1552,31 @@ class Installer
1312
1552
  @currdir = File.dirname(rel)
1313
1553
  end
1314
1554
 
1315
- end
1555
+ def run_hook(id)
1556
+ path = [ "#{curr_srcdir()}/#{id}",
1557
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1558
+ return unless path
1559
+ begin
1560
+ instance_eval File.read(path), path, 1
1561
+ rescue
1562
+ raise if $DEBUG
1563
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1564
+ end
1565
+ end
1566
+
1567
+ end # class Installer
1316
1568
 
1317
1569
 
1570
+ class SetupError < StandardError; end
1571
+
1572
+ def setup_rb_error(msg)
1573
+ raise SetupError, msg
1574
+ end
1575
+
1318
1576
  if $0 == __FILE__
1319
1577
  begin
1320
- if multipackage_install?
1321
- ToplevelInstallerMulti.invoke
1322
- else
1323
- ToplevelInstaller.invoke
1324
- end
1325
- rescue
1578
+ ToplevelInstaller.invoke
1579
+ rescue SetupError
1326
1580
  raise if $DEBUG
1327
1581
  $stderr.puts $!.message
1328
1582
  $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."