glyph_imager 0.0.3

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