glyph_imager 0.0.3

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 (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