rkwalify 1.4.0.pre.preview1

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