dawanda-sqlite3 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gemtest +0 -0
  2. data/API_CHANGES.rdoc +50 -0
  3. data/CHANGELOG.rdoc +184 -0
  4. data/ChangeLog.cvs +88 -0
  5. data/LICENSE +27 -0
  6. data/Manifest.txt +50 -0
  7. data/README.rdoc +95 -0
  8. data/Rakefile +10 -0
  9. data/ext/sqlite3/backup.c +168 -0
  10. data/ext/sqlite3/backup.h +15 -0
  11. data/ext/sqlite3/database.c +762 -0
  12. data/ext/sqlite3/database.h +15 -0
  13. data/ext/sqlite3/exception.c +94 -0
  14. data/ext/sqlite3/exception.h +8 -0
  15. data/ext/sqlite3/extconf.rb +41 -0
  16. data/ext/sqlite3/sqlite3.c +40 -0
  17. data/ext/sqlite3/sqlite3_ruby.h +44 -0
  18. data/ext/sqlite3/statement.c +418 -0
  19. data/ext/sqlite3/statement.h +16 -0
  20. data/faq/faq.rb +145 -0
  21. data/faq/faq.yml +426 -0
  22. data/lib/sqlite3.rb +10 -0
  23. data/lib/sqlite3/constants.rb +49 -0
  24. data/lib/sqlite3/database.rb +587 -0
  25. data/lib/sqlite3/errors.rb +44 -0
  26. data/lib/sqlite3/pragmas.rb +280 -0
  27. data/lib/sqlite3/resultset.rb +126 -0
  28. data/lib/sqlite3/statement.rb +148 -0
  29. data/lib/sqlite3/translator.rb +118 -0
  30. data/lib/sqlite3/value.rb +57 -0
  31. data/lib/sqlite3/version.rb +25 -0
  32. data/setup.rb +1333 -0
  33. data/tasks/faq.rake +9 -0
  34. data/tasks/gem.rake +31 -0
  35. data/tasks/native.rake +61 -0
  36. data/tasks/vendor_sqlite3.rake +104 -0
  37. data/test/helper.rb +3 -0
  38. data/test/test_backup.rb +33 -0
  39. data/test/test_collation.rb +82 -0
  40. data/test/test_database.rb +319 -0
  41. data/test/test_database_readonly.rb +29 -0
  42. data/test/test_deprecated.rb +37 -0
  43. data/test/test_encoding.rb +119 -0
  44. data/test/test_integration.rb +544 -0
  45. data/test/test_integration_open_close.rb +30 -0
  46. data/test/test_integration_pending.rb +115 -0
  47. data/test/test_integration_resultset.rb +156 -0
  48. data/test/test_integration_statement.rb +194 -0
  49. data/test/test_sqlite3.rb +9 -0
  50. data/test/test_statement.rb +213 -0
  51. data/test/test_statement_execute.rb +35 -0
  52. metadata +184 -0
@@ -0,0 +1,148 @@
1
+ require 'sqlite3/errors'
2
+ require 'sqlite3/resultset'
3
+
4
+ class String
5
+ def to_blob
6
+ SQLite3::Blob.new( self )
7
+ end
8
+ end
9
+
10
+ module SQLite3
11
+ # A statement represents a prepared-but-unexecuted SQL query. It will rarely
12
+ # (if ever) be instantiated directly by a client, and is most often obtained
13
+ # via the Database#prepare method.
14
+ class Statement
15
+ include Enumerable
16
+
17
+ # This is any text that followed the first valid SQL statement in the text
18
+ # with which the statement was initialized. If there was no trailing text,
19
+ # this will be the empty string.
20
+ attr_reader :remainder
21
+
22
+ # Binds the given variables to the corresponding placeholders in the SQL
23
+ # text.
24
+ #
25
+ # See Database#execute for a description of the valid placeholder
26
+ # syntaxes.
27
+ #
28
+ # Example:
29
+ #
30
+ # stmt = db.prepare( "select * from table where a=? and b=?" )
31
+ # stmt.bind_params( 15, "hello" )
32
+ #
33
+ # See also #execute, #bind_param, Statement#bind_param, and
34
+ # Statement#bind_params.
35
+ def bind_params( *bind_vars )
36
+ index = 1
37
+ bind_vars.flatten.each do |var|
38
+ if Hash === var
39
+ var.each { |key, val| bind_param key, val }
40
+ else
41
+ bind_param index, var
42
+ index += 1
43
+ end
44
+ end
45
+ end
46
+
47
+ # Execute the statement. This creates a new ResultSet object for the
48
+ # statement's virtual machine. If a block was given, the new ResultSet will
49
+ # be yielded to it; otherwise, the ResultSet will be returned.
50
+ #
51
+ # Any parameters will be bound to the statement using #bind_params.
52
+ #
53
+ # Example:
54
+ #
55
+ # stmt = db.prepare( "select * from table" )
56
+ # stmt.execute do |result|
57
+ # ...
58
+ # end
59
+ #
60
+ # See also #bind_params, #execute!.
61
+ def execute( *bind_vars )
62
+ reset! if active? || done?
63
+
64
+ bind_params(*bind_vars) unless bind_vars.empty?
65
+ @results = ResultSet.new(@connection, self)
66
+
67
+ step if 0 == column_count
68
+
69
+ yield @results if block_given?
70
+ @results
71
+ end
72
+
73
+ # Execute the statement. If no block was given, this returns an array of
74
+ # rows returned by executing the statement. Otherwise, each row will be
75
+ # yielded to the block.
76
+ #
77
+ # Any parameters will be bound to the statement using #bind_params.
78
+ #
79
+ # Example:
80
+ #
81
+ # stmt = db.prepare( "select * from table" )
82
+ # stmt.execute! do |row|
83
+ # ...
84
+ # end
85
+ #
86
+ # See also #bind_params, #execute.
87
+ def execute!( *bind_vars, &block )
88
+ execute(*bind_vars)
89
+ block_given? ? each(&block) : to_a
90
+ end
91
+
92
+ # Returns true if the statement is currently active, meaning it has an
93
+ # open result set.
94
+ def active?
95
+ !done?
96
+ end
97
+
98
+ # Return an array of the column names for this statement. Note that this
99
+ # may execute the statement in order to obtain the metadata; this makes it
100
+ # a (potentially) expensive operation.
101
+ def columns
102
+ get_metadata unless @columns
103
+ return @columns
104
+ end
105
+
106
+ def each
107
+ loop do
108
+ val = step
109
+ break self if done?
110
+ yield val
111
+ end
112
+ end
113
+
114
+ # Return an array of the data types for each column in this statement. Note
115
+ # that this may execute the statement in order to obtain the metadata; this
116
+ # makes it a (potentially) expensive operation.
117
+ def types
118
+ must_be_open!
119
+ get_metadata unless @types
120
+ @types
121
+ end
122
+
123
+ # A convenience method for obtaining the metadata about the query. Note
124
+ # that this will actually execute the SQL, which means it can be a
125
+ # (potentially) expensive operation.
126
+ def get_metadata
127
+ @columns = []
128
+ @types = []
129
+
130
+ column_count.times do |column|
131
+ @columns << column_name(column)
132
+ @types << column_decltype(column)
133
+ end
134
+
135
+ @columns.freeze
136
+ @types.freeze
137
+ end
138
+ private :get_metadata
139
+
140
+ # Performs a sanity check to ensure that the statement is not
141
+ # closed. If it is, an exception is raised.
142
+ def must_be_open! # :nodoc:
143
+ if closed?
144
+ raise SQLite3::Exception, "cannot use a closed statement"
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,118 @@
1
+ require 'time'
2
+ require 'date'
3
+
4
+ module SQLite3
5
+
6
+ # The Translator class encapsulates the logic and callbacks necessary for
7
+ # converting string data to a value of some specified type. Every Database
8
+ # instance may have a Translator instance, in order to assist in type
9
+ # translation (Database#type_translation).
10
+ #
11
+ # Further, applications may define their own custom type translation logic
12
+ # by registering translator blocks with the corresponding database's
13
+ # translator instance (Database#translator).
14
+ class Translator
15
+
16
+ # Create a new Translator instance. It will be preinitialized with default
17
+ # translators for most SQL data types.
18
+ def initialize
19
+ @translators = Hash.new( proc { |type,value| value } )
20
+ @type_name_cache = {}
21
+ register_default_translators
22
+ end
23
+
24
+ # Add a new translator block, which will be invoked to process type
25
+ # translations to the given type. The type should be an SQL datatype, and
26
+ # may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
27
+ # information is stripped off and discarded, so type translation decisions
28
+ # are made solely on the "base" type name.
29
+ #
30
+ # The translator block itself should accept two parameters, "type" and
31
+ # "value". In this case, the "type" is the full type name (including
32
+ # parentheses), so the block itself may include logic for changing how a
33
+ # type is translated based on the additional data. The "value" parameter
34
+ # is the (string) data to convert.
35
+ #
36
+ # The block should return the translated value.
37
+ def add_translator( type, &block ) # :yields: type, value
38
+ warn(<<-eowarn) if $VERBOSE
39
+ #{caller[0]} is calling `add_translator`.
40
+ Built in translators are deprecated and will be removed in version 2.0.0
41
+ eowarn
42
+ @translators[ type_name( type ) ] = block
43
+ end
44
+
45
+ # Translate the given string value to a value of the given type. In the
46
+ # absense of an installed translator block for the given type, the value
47
+ # itself is always returned. Further, +nil+ values are never translated,
48
+ # and are always passed straight through regardless of the type parameter.
49
+ def translate( type, value )
50
+ unless value.nil?
51
+ # FIXME: this is a hack to support Sequel
52
+ if type && %w{ datetime timestamp }.include?(type.downcase)
53
+ @translators[ type_name( type ) ].call( type, value.to_s )
54
+ else
55
+ @translators[ type_name( type ) ].call( type, value )
56
+ end
57
+ end
58
+ end
59
+
60
+ # A convenience method for working with type names. This returns the "base"
61
+ # type name, without any parenthetical data.
62
+ def type_name( type )
63
+ @type_name_cache[type] ||= begin
64
+ type = "" if type.nil?
65
+ type = $1 if type =~ /^(.*?)\(/
66
+ type.upcase
67
+ end
68
+ end
69
+ private :type_name
70
+
71
+ # Register the default translators for the current Translator instance.
72
+ # This includes translators for most major SQL data types.
73
+ def register_default_translators
74
+ [ "time",
75
+ "timestamp" ].each { |type| add_translator( type ) { |t, v| Time.parse( v ) } }
76
+
77
+ add_translator( "date" ) { |t,v| Date.parse(v) }
78
+ add_translator( "datetime" ) { |t,v| DateTime.parse(v) }
79
+
80
+ [ "decimal",
81
+ "float",
82
+ "numeric",
83
+ "double",
84
+ "real",
85
+ "dec",
86
+ "fixed" ].each { |type| add_translator( type ) { |t,v| v.to_f } }
87
+
88
+ [ "integer",
89
+ "smallint",
90
+ "mediumint",
91
+ "int",
92
+ "bigint" ].each { |type| add_translator( type ) { |t,v| v.to_i } }
93
+
94
+ [ "bit",
95
+ "bool",
96
+ "boolean" ].each do |type|
97
+ add_translator( type ) do |t,v|
98
+ !( v.strip.gsub(/00+/,"0") == "0" ||
99
+ v.downcase == "false" ||
100
+ v.downcase == "f" ||
101
+ v.downcase == "no" ||
102
+ v.downcase == "n" )
103
+ end
104
+ end
105
+
106
+ add_translator( "tinyint" ) do |type, value|
107
+ if type =~ /\(\s*1\s*\)/
108
+ value.to_i == 1
109
+ else
110
+ value.to_i
111
+ end
112
+ end
113
+ end
114
+ private :register_default_translators
115
+
116
+ end
117
+
118
+ end
@@ -0,0 +1,57 @@
1
+ require 'sqlite3/constants'
2
+
3
+ module SQLite3
4
+
5
+ class Value
6
+ attr_reader :handle
7
+
8
+ def initialize( db, handle )
9
+ @driver = db.driver
10
+ @handle = handle
11
+ end
12
+
13
+ def null?
14
+ type == :null
15
+ end
16
+
17
+ def to_blob
18
+ @driver.value_blob( @handle )
19
+ end
20
+
21
+ def length( utf16=false )
22
+ if utf16
23
+ @driver.value_bytes16( @handle )
24
+ else
25
+ @driver.value_bytes( @handle )
26
+ end
27
+ end
28
+
29
+ def to_f
30
+ @driver.value_double( @handle )
31
+ end
32
+
33
+ def to_i
34
+ @driver.value_int( @handle )
35
+ end
36
+
37
+ def to_int64
38
+ @driver.value_int64( @handle )
39
+ end
40
+
41
+ def to_s( utf16=false )
42
+ @driver.value_text( @handle, utf16 )
43
+ end
44
+
45
+ def type
46
+ case @driver.value_type( @handle )
47
+ when Constants::ColumnType::INTEGER then :int
48
+ when Constants::ColumnType::FLOAT then :float
49
+ when Constants::ColumnType::TEXT then :text
50
+ when Constants::ColumnType::BLOB then :blob
51
+ when Constants::ColumnType::NULL then :null
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,25 @@
1
+ module SQLite3
2
+
3
+ VERSION = '1.3.3'
4
+
5
+ module VersionProxy
6
+
7
+ MAJOR = 1
8
+ MINOR = 3
9
+ TINY = 1
10
+ BUILD = nil
11
+
12
+ STRING = [ MAJOR, MINOR, TINY, BUILD ].compact.join( "." )
13
+ #:beta-tag:
14
+
15
+ VERSION = ::SQLite3::VERSION
16
+ end
17
+
18
+ def self.const_missing(name)
19
+ return super unless name == :Version
20
+ warn(<<-eowarn) if $VERBOSE
21
+ #{caller[0]}: SQLite::Version will be removed in sqlite3-ruby version 2.0.0
22
+ eowarn
23
+ VersionProxy
24
+ end
25
+ end
@@ -0,0 +1,1333 @@
1
+ #
2
+ # setup.rb
3
+ #
4
+ # Copyright (c) 2000-2004 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
+ #
12
+ # For backward compatibility
13
+ #
14
+
15
+ unless Enumerable.method_defined?(:map)
16
+ module Enumerable
17
+ alias map collect
18
+ end
19
+ end
20
+
21
+ unless Enumerable.method_defined?(:detect)
22
+ module Enumerable
23
+ alias detect find
24
+ end
25
+ end
26
+
27
+ unless Enumerable.method_defined?(:select)
28
+ module Enumerable
29
+ alias select find_all
30
+ end
31
+ end
32
+
33
+ unless Enumerable.method_defined?(:reject)
34
+ module Enumerable
35
+ def reject
36
+ select {|i| not yield(i) }
37
+ end
38
+ end
39
+ end
40
+
41
+ unless Enumerable.method_defined?(:inject)
42
+ module Enumerable
43
+ def inject(result)
44
+ each do |i|
45
+ result = yield(result, i)
46
+ end
47
+ result
48
+ end
49
+ end
50
+ end
51
+
52
+ unless Enumerable.method_defined?(:any?)
53
+ module Enumerable
54
+ def any?
55
+ each do |i|
56
+ return true if yield(i)
57
+ end
58
+ false
59
+ end
60
+ end
61
+ end
62
+
63
+ unless File.respond_to?(:read)
64
+ def File.read(fname)
65
+ open(fname) {|f|
66
+ return f.read
67
+ }
68
+ end
69
+ end
70
+
71
+ #
72
+ # Application independent utilities
73
+ #
74
+
75
+ def File.binread(fname)
76
+ open(fname, 'rb') {|f|
77
+ return f.read
78
+ }
79
+ end
80
+
81
+ # for corrupted windows stat(2)
82
+ def File.dir?(path)
83
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
84
+ end
85
+
86
+ #
87
+ # Config
88
+ #
89
+
90
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
91
+ ARGV.delete(arg)
92
+ require arg.split(/=/, 2)[1]
93
+ $".push 'rbconfig.rb'
94
+ else
95
+ require 'rbconfig'
96
+ end
97
+
98
+ def multipackage_install?
99
+ FileTest.directory?(File.dirname($0) + '/packages')
100
+ end
101
+
102
+
103
+ class ConfigTable
104
+
105
+ c = ::Config::CONFIG
106
+
107
+ rubypath = c['bindir'] + '/' + c['ruby_install_name']
108
+
109
+ major = c['MAJOR'].to_i
110
+ minor = c['MINOR'].to_i
111
+ teeny = c['TEENY'].to_i
112
+ version = "#{major}.#{minor}"
113
+
114
+ # ruby ver. >= 1.4.4?
115
+ newpath_p = ((major >= 2) or
116
+ ((major == 1) and
117
+ ((minor >= 5) or
118
+ ((minor == 4) and (teeny >= 4)))))
119
+
120
+ subprefix = lambda {|path|
121
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
122
+ }
123
+
124
+ if c['rubylibdir']
125
+ # V < 1.6.3
126
+ stdruby = subprefix.call(c['rubylibdir'])
127
+ siteruby = subprefix.call(c['sitedir'])
128
+ versite = subprefix.call(c['sitelibdir'])
129
+ sodir = subprefix.call(c['sitearchdir'])
130
+ elsif newpath_p
131
+ # 1.4.4 <= V <= 1.6.3
132
+ stdruby = "$prefix/lib/ruby/#{version}"
133
+ siteruby = subprefix.call(c['sitedir'])
134
+ versite = siteruby + '/' + version
135
+ sodir = "$site-ruby/#{c['arch']}"
136
+ else
137
+ # V < 1.4.4
138
+ stdruby = "$prefix/lib/ruby/#{version}"
139
+ siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
140
+ versite = siteruby
141
+ sodir = "$site-ruby/#{c['arch']}"
142
+ end
143
+
144
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
145
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
146
+ else
147
+ makeprog = 'make'
148
+ end
149
+
150
+ common_descripters = [
151
+ [ 'prefix', [ c['prefix'],
152
+ 'path',
153
+ 'path prefix of target environment' ] ],
154
+ [ 'std-ruby', [ stdruby,
155
+ 'path',
156
+ 'the directory for standard ruby libraries' ] ],
157
+ [ 'site-ruby-common', [ siteruby,
158
+ 'path',
159
+ 'the directory for version-independent non-standard ruby libraries' ] ],
160
+ [ 'site-ruby', [ versite,
161
+ 'path',
162
+ 'the directory for non-standard ruby libraries' ] ],
163
+ [ 'bin-dir', [ '$prefix/bin',
164
+ 'path',
165
+ 'the directory for commands' ] ],
166
+ [ 'rb-dir', [ '$site-ruby',
167
+ 'path',
168
+ 'the directory for ruby scripts' ] ],
169
+ [ 'so-dir', [ sodir,
170
+ 'path',
171
+ 'the directory for ruby extentions' ] ],
172
+ [ 'data-dir', [ '$prefix/share',
173
+ 'path',
174
+ 'the directory for shared data' ] ],
175
+ [ 'ruby-path', [ rubypath,
176
+ 'path',
177
+ 'path to set to #! line' ] ],
178
+ [ 'ruby-prog', [ rubypath,
179
+ 'name',
180
+ 'the ruby program using for installation' ] ],
181
+ [ 'make-prog', [ makeprog,
182
+ 'name',
183
+ 'the make program to compile ruby extentions' ] ],
184
+ [ 'without-ext', [ 'no',
185
+ 'yes/no',
186
+ 'does not compile/install ruby extentions' ] ]
187
+ ]
188
+ multipackage_descripters = [
189
+ [ 'with', [ '',
190
+ 'name,name...',
191
+ 'package names that you want to install',
192
+ 'ALL' ] ],
193
+ [ 'without', [ '',
194
+ 'name,name...',
195
+ 'package names that you do not want to install',
196
+ 'NONE' ] ]
197
+ ]
198
+ if multipackage_install?
199
+ DESCRIPTER = common_descripters + multipackage_descripters
200
+ else
201
+ DESCRIPTER = common_descripters
202
+ end
203
+
204
+ SAVE_FILE = '.config'
205
+
206
+ def ConfigTable.each_name(&block)
207
+ keys().each(&block)
208
+ end
209
+
210
+ def ConfigTable.keys
211
+ DESCRIPTER.map {|name, *dummy| name }
212
+ end
213
+
214
+ def ConfigTable.each_definition(&block)
215
+ DESCRIPTER.each(&block)
216
+ end
217
+
218
+ def ConfigTable.get_entry(name)
219
+ name, ent = DESCRIPTER.assoc(name)
220
+ ent
221
+ end
222
+
223
+ def ConfigTable.get_entry!(name)
224
+ get_entry(name) or raise ArgumentError, "no such config: #{name}"
225
+ end
226
+
227
+ def ConfigTable.add_entry(name, vals)
228
+ ConfigTable::DESCRIPTER.push [name,vals]
229
+ end
230
+
231
+ def ConfigTable.remove_entry(name)
232
+ get_entry(name) or raise ArgumentError, "no such config: #{name}"
233
+ DESCRIPTER.delete_if {|n, arr| n == name }
234
+ end
235
+
236
+ def ConfigTable.config_key?(name)
237
+ get_entry(name) ? true : false
238
+ end
239
+
240
+ def ConfigTable.bool_config?(name)
241
+ ent = get_entry(name) or return false
242
+ ent[1] == 'yes/no'
243
+ end
244
+
245
+ def ConfigTable.value_config?(name)
246
+ ent = get_entry(name) or return false
247
+ ent[1] != 'yes/no'
248
+ end
249
+
250
+ def ConfigTable.path_config?(name)
251
+ ent = get_entry(name) or return false
252
+ ent[1] == 'path'
253
+ end
254
+
255
+
256
+ class << self
257
+ alias newobj new
258
+ end
259
+
260
+ def ConfigTable.new
261
+ c = newobj()
262
+ c.initialize_from_table
263
+ c
264
+ end
265
+
266
+ def ConfigTable.load
267
+ c = newobj()
268
+ c.initialize_from_file
269
+ c
270
+ end
271
+
272
+ def initialize_from_table
273
+ @table = {}
274
+ DESCRIPTER.each do |k, (default, vname, desc, default2)|
275
+ @table[k] = default
276
+ end
277
+ end
278
+
279
+ def initialize_from_file
280
+ raise InstallError, "#{File.basename $0} config first"\
281
+ unless File.file?(SAVE_FILE)
282
+ @table = {}
283
+ File.foreach(SAVE_FILE) do |line|
284
+ k, v = line.split(/=/, 2)
285
+ @table[k] = v.strip
286
+ end
287
+ end
288
+
289
+ def save
290
+ File.open(SAVE_FILE, 'w') {|f|
291
+ @table.each do |k, v|
292
+ f.printf "%s=%s\n", k, v if v
293
+ end
294
+ }
295
+ end
296
+
297
+ def []=(k, v)
298
+ raise InstallError, "unknown config option #{k}"\
299
+ unless ConfigTable.config_key?(k)
300
+ @table[k] = v
301
+ end
302
+
303
+ def [](key)
304
+ return nil unless @table[key]
305
+ @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
306
+ end
307
+
308
+ def set_raw(key, val)
309
+ @table[key] = val
310
+ end
311
+
312
+ def get_raw(key)
313
+ @table[key]
314
+ end
315
+
316
+ end
317
+
318
+
319
+ module MetaConfigAPI
320
+
321
+ def eval_file_ifexist(fname)
322
+ instance_eval File.read(fname), fname, 1 if File.file?(fname)
323
+ end
324
+
325
+ def config_names
326
+ ConfigTable.keys
327
+ end
328
+
329
+ def config?(name)
330
+ ConfigTable.config_key?(name)
331
+ end
332
+
333
+ def bool_config?(name)
334
+ ConfigTable.bool_config?(name)
335
+ end
336
+
337
+ def value_config?(name)
338
+ ConfigTable.value_config?(name)
339
+ end
340
+
341
+ def path_config?(name)
342
+ ConfigTable.path_config?(name)
343
+ end
344
+
345
+ def add_config(name, argname, default, desc)
346
+ ConfigTable.add_entry name,[default,argname,desc]
347
+ end
348
+
349
+ def add_path_config(name, default, desc)
350
+ add_config name, 'path', default, desc
351
+ end
352
+
353
+ def add_bool_config(name, default, desc)
354
+ add_config name, 'yes/no', default ? 'yes' : 'no', desc
355
+ end
356
+
357
+ def set_config_default(name, default)
358
+ if bool_config?(name)
359
+ ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
360
+ else
361
+ ConfigTable.get_entry!(name)[0] = default
362
+ end
363
+ end
364
+
365
+ def remove_config(name)
366
+ ent = ConfigTable.get_entry(name)
367
+ ConfigTable.remove_entry name
368
+ ent
369
+ end
370
+
371
+ end
372
+
373
+ #
374
+ # File Operations
375
+ #
376
+
377
+ module FileOperations
378
+
379
+ def mkdir_p(dirname, prefix = nil)
380
+ dirname = prefix + dirname if prefix
381
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
382
+ return if no_harm?
383
+
384
+ # does not check '/'... it's too abnormal case
385
+ dirs = dirname.split(%r<(?=/)>)
386
+ if /\A[a-z]:\z/i =~ dirs[0]
387
+ disk = dirs.shift
388
+ dirs[0] = disk + dirs[0]
389
+ end
390
+ dirs.each_index do |idx|
391
+ path = dirs[0..idx].join('')
392
+ Dir.mkdir path unless File.dir?(path)
393
+ end
394
+ end
395
+
396
+ def rm_f(fname)
397
+ $stderr.puts "rm -f #{fname}" if verbose?
398
+ return if no_harm?
399
+
400
+ if File.exist?(fname) or File.symlink?(fname)
401
+ File.chmod 0777, fname
402
+ File.unlink fname
403
+ end
404
+ end
405
+
406
+ def rm_rf(dn)
407
+ $stderr.puts "rm -rf #{dn}" if verbose?
408
+ return if no_harm?
409
+
410
+ Dir.chdir dn
411
+ Dir.foreach('.') do |fn|
412
+ next if fn == '.'
413
+ next if fn == '..'
414
+ if File.dir?(fn)
415
+ verbose_off {
416
+ rm_rf fn
417
+ }
418
+ else
419
+ verbose_off {
420
+ rm_f fn
421
+ }
422
+ end
423
+ end
424
+ Dir.chdir '..'
425
+ Dir.rmdir dn
426
+ end
427
+
428
+ def move_file(src, dest)
429
+ File.unlink dest if File.exist?(dest)
430
+ begin
431
+ File.rename src, dest
432
+ rescue
433
+ File.open(dest, 'wb') {|f| f.write File.binread(src) }
434
+ File.chmod File.stat(src).mode, dest
435
+ File.unlink src
436
+ end
437
+ end
438
+
439
+ def install(from, dest, mode, prefix = nil)
440
+ $stderr.puts "install #{from} #{dest}" if verbose?
441
+ return if no_harm?
442
+
443
+ realdest = prefix ? prefix + dest : dest
444
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
445
+ str = File.binread(from)
446
+ if diff?(str, realdest)
447
+ verbose_off {
448
+ rm_f realdest if File.exist?(realdest)
449
+ }
450
+ File.open(realdest, 'wb') {|f|
451
+ f.write str
452
+ }
453
+ File.chmod mode, realdest
454
+
455
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
456
+ if prefix
457
+ f.puts realdest.sub(prefix, '')
458
+ else
459
+ f.puts realdest
460
+ end
461
+ }
462
+ end
463
+ end
464
+
465
+ def diff?(new_content, path)
466
+ return true unless File.exist?(path)
467
+ new_content != File.binread(path)
468
+ end
469
+
470
+ def command(str)
471
+ $stderr.puts str if verbose?
472
+ system str or raise RuntimeError, "'system #{str}' failed"
473
+ end
474
+
475
+ def ruby(str)
476
+ command config('ruby-prog') + ' ' + str
477
+ end
478
+
479
+ def make(task = '')
480
+ command config('make-prog') + ' ' + task
481
+ end
482
+
483
+ def extdir?(dir)
484
+ File.exist?(dir + '/MANIFEST') or File.exist?("#{dir}/extconf.rb")
485
+ end
486
+
487
+ def all_files_in(dirname)
488
+ Dir.open(dirname) {|d|
489
+ return d.select {|ent| File.file?("#{dirname}/#{ent}") }
490
+ }
491
+ end
492
+
493
+ REJECT_DIRS = %w(
494
+ CVS SCCS RCS CVS.adm .svn
495
+ )
496
+
497
+ def all_dirs_in(dirname)
498
+ Dir.open(dirname) {|d|
499
+ return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
500
+ }
501
+ end
502
+
503
+ end
504
+
505
+ #
506
+ # Main Installer
507
+ #
508
+
509
+ class InstallError < StandardError; end
510
+
511
+
512
+ module HookUtils
513
+
514
+ def run_hook(name)
515
+ try_run_hook "#{curr_srcdir()}/#{name}" or
516
+ try_run_hook "#{curr_srcdir()}/#{name}.rb"
517
+ end
518
+
519
+ def try_run_hook(fname)
520
+ return false unless File.file?(fname)
521
+ begin
522
+ instance_eval File.read(fname), fname, 1
523
+ rescue
524
+ raise InstallError, "hook #{fname} failed:\n" + $!.message
525
+ end
526
+ true
527
+ end
528
+
529
+ end
530
+
531
+
532
+ module HookScriptAPI
533
+
534
+ def get_config(key)
535
+ @config[key]
536
+ end
537
+
538
+ alias config get_config
539
+
540
+ def set_config(key, val)
541
+ @config[key] = val
542
+ end
543
+
544
+ #
545
+ # srcdir/objdir (works only in the package directory)
546
+ #
547
+
548
+ #abstract srcdir_root
549
+ #abstract objdir_root
550
+ #abstract relpath
551
+
552
+ def curr_srcdir
553
+ "#{srcdir_root()}/#{relpath()}"
554
+ end
555
+
556
+ def curr_objdir
557
+ "#{objdir_root()}/#{relpath()}"
558
+ end
559
+
560
+ def srcfile(path)
561
+ "#{curr_srcdir()}/#{path}"
562
+ end
563
+
564
+ def srcexist?(path)
565
+ File.exist?(srcfile(path))
566
+ end
567
+
568
+ def srcdirectory?(path)
569
+ File.dir?(srcfile(path))
570
+ end
571
+
572
+ def srcfile?(path)
573
+ File.file? srcfile(path)
574
+ end
575
+
576
+ def srcentries(path = '.')
577
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
578
+ return d.to_a - %w(. ..)
579
+ }
580
+ end
581
+
582
+ def srcfiles(path = '.')
583
+ srcentries(path).select {|fname|
584
+ File.file?(File.join(curr_srcdir(), path, fname))
585
+ }
586
+ end
587
+
588
+ def srcdirectories(path = '.')
589
+ srcentries(path).select {|fname|
590
+ File.dir?(File.join(curr_srcdir(), path, fname))
591
+ }
592
+ end
593
+
594
+ end
595
+
596
+
597
+ class ToplevelInstaller
598
+
599
+ Version = '3.3.0'
600
+ Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
601
+
602
+ TASKS = [
603
+ [ 'all', 'do config, setup, then install' ],
604
+ [ 'config', 'saves your configurations' ],
605
+ [ 'show', 'shows current configuration' ],
606
+ [ 'setup', 'compiles ruby extentions and others' ],
607
+ [ 'install', 'installs files' ],
608
+ [ 'clean', "does `make clean' for each extention" ],
609
+ [ 'distclean',"does `make distclean' for each extention" ]
610
+ ]
611
+
612
+ def ToplevelInstaller.invoke
613
+ instance().invoke
614
+ end
615
+
616
+ @singleton = nil
617
+
618
+ def ToplevelInstaller.instance
619
+ @singleton ||= new(File.dirname($0))
620
+ @singleton
621
+ end
622
+
623
+ include MetaConfigAPI
624
+
625
+ def initialize(ardir_root)
626
+ @config = nil
627
+ @options = { 'verbose' => true }
628
+ @ardir = File.expand_path(ardir_root)
629
+ end
630
+
631
+ def inspect
632
+ "#<#{self.class} #{__id__()}>"
633
+ end
634
+
635
+ def invoke
636
+ run_metaconfigs
637
+ case task = parsearg_global()
638
+ when nil, 'all'
639
+ @config = load_config('config')
640
+ parsearg_config
641
+ init_installers
642
+ exec_config
643
+ exec_setup
644
+ exec_install
645
+ else
646
+ @config = load_config(task)
647
+ __send__ "parsearg_#{task}"
648
+ init_installers
649
+ __send__ "exec_#{task}"
650
+ end
651
+ end
652
+
653
+ def run_metaconfigs
654
+ eval_file_ifexist "#{@ardir}/metaconfig"
655
+ end
656
+
657
+ def load_config(task)
658
+ case task
659
+ when 'config'
660
+ ConfigTable.new
661
+ when 'clean', 'distclean'
662
+ if File.exist?(ConfigTable::SAVE_FILE)
663
+ then ConfigTable.load
664
+ else ConfigTable.new
665
+ end
666
+ else
667
+ ConfigTable.load
668
+ end
669
+ end
670
+
671
+ def init_installers
672
+ @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
673
+ end
674
+
675
+ #
676
+ # Hook Script API bases
677
+ #
678
+
679
+ def srcdir_root
680
+ @ardir
681
+ end
682
+
683
+ def objdir_root
684
+ '.'
685
+ end
686
+
687
+ def relpath
688
+ '.'
689
+ end
690
+
691
+ #
692
+ # Option Parsing
693
+ #
694
+
695
+ def parsearg_global
696
+ valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
697
+
698
+ while arg = ARGV.shift
699
+ case arg
700
+ when /\A\w+\z/
701
+ raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
702
+ return arg
703
+
704
+ when '-q', '--quiet'
705
+ @options['verbose'] = false
706
+
707
+ when '--verbose'
708
+ @options['verbose'] = true
709
+
710
+ when '-h', '--help'
711
+ print_usage $stdout
712
+ exit 0
713
+
714
+ when '-v', '--version'
715
+ puts "#{File.basename($0)} version #{Version}"
716
+ exit 0
717
+
718
+ when '--copyright'
719
+ puts Copyright
720
+ exit 0
721
+
722
+ else
723
+ raise InstallError, "unknown global option '#{arg}'"
724
+ end
725
+ end
726
+
727
+ nil
728
+ end
729
+
730
+
731
+ def parsearg_no_options
732
+ raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
733
+ unless ARGV.empty?
734
+ end
735
+
736
+ alias parsearg_show parsearg_no_options
737
+ alias parsearg_setup parsearg_no_options
738
+ alias parsearg_clean parsearg_no_options
739
+ alias parsearg_distclean parsearg_no_options
740
+
741
+ def parsearg_config
742
+ re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
743
+ @options['config-opt'] = []
744
+
745
+ while i = ARGV.shift
746
+ if /\A--?\z/ =~ i
747
+ @options['config-opt'] = ARGV.dup
748
+ break
749
+ end
750
+ m = re.match(i) or raise InstallError, "config: unknown option #{i}"
751
+ name, value = m.to_a[1,2]
752
+ if value
753
+ if ConfigTable.bool_config?(name)
754
+ raise InstallError, "config: --#{name} allows only yes/no for argument"\
755
+ unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value
756
+ value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
757
+ end
758
+ else
759
+ raise InstallError, "config: --#{name} requires argument"\
760
+ unless ConfigTable.bool_config?(name)
761
+ value = 'yes'
762
+ end
763
+ @config[name] = value
764
+ end
765
+ end
766
+
767
+ def parsearg_install
768
+ @options['no-harm'] = false
769
+ @options['install-prefix'] = ''
770
+ while a = ARGV.shift
771
+ case a
772
+ when /\A--no-harm\z/
773
+ @options['no-harm'] = true
774
+ when /\A--prefix=(.*)\z/
775
+ path = $1
776
+ path = File.expand_path(path) unless path[0,1] == '/'
777
+ @options['install-prefix'] = path
778
+ else
779
+ raise InstallError, "install: unknown option #{a}"
780
+ end
781
+ end
782
+ end
783
+
784
+ def print_usage(out)
785
+ out.puts 'Typical Installation Procedure:'
786
+ out.puts " $ ruby #{File.basename $0} config"
787
+ out.puts " $ ruby #{File.basename $0} setup"
788
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
789
+ out.puts
790
+ out.puts 'Detailed Usage:'
791
+ out.puts " ruby #{File.basename $0} <global option>"
792
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
793
+
794
+ fmt = " %-20s %s\n"
795
+ out.puts
796
+ out.puts 'Global options:'
797
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
798
+ out.printf fmt, ' --verbose', 'output messages verbosely'
799
+ out.printf fmt, '-h,--help', 'print this message'
800
+ out.printf fmt, '-v,--version', 'print version and quit'
801
+ out.printf fmt, ' --copyright', 'print copyright and quit'
802
+
803
+ out.puts
804
+ out.puts 'Tasks:'
805
+ TASKS.each do |name, desc|
806
+ out.printf " %-10s %s\n", name, desc
807
+ end
808
+
809
+ out.puts
810
+ out.puts 'Options for CONFIG or ALL:'
811
+ ConfigTable.each_definition do |name, (default, arg, desc, default2)|
812
+ out.printf " %-20s %s [%s]\n",
813
+ '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
814
+ desc,
815
+ default2 || default
816
+ end
817
+ out.printf " %-20s %s [%s]\n",
818
+ '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
819
+
820
+ out.puts
821
+ out.puts 'Options for INSTALL:'
822
+ out.printf " %-20s %s [%s]\n",
823
+ '--no-harm', 'only display what to do if given', 'off'
824
+ out.printf " %-20s %s [%s]\n",
825
+ '--prefix', 'install path prefix', '$prefix'
826
+
827
+ out.puts
828
+ end
829
+
830
+ #
831
+ # Task Handlers
832
+ #
833
+
834
+ def exec_config
835
+ @installer.exec_config
836
+ @config.save # must be final
837
+ end
838
+
839
+ def exec_setup
840
+ @installer.exec_setup
841
+ end
842
+
843
+ def exec_install
844
+ @installer.exec_install
845
+ end
846
+
847
+ def exec_show
848
+ ConfigTable.each_name do |k|
849
+ v = @config.get_raw(k)
850
+ if not v or v.empty?
851
+ v = '(not specified)'
852
+ end
853
+ printf "%-10s %s\n", k, v
854
+ end
855
+ end
856
+
857
+ def exec_clean
858
+ @installer.exec_clean
859
+ end
860
+
861
+ def exec_distclean
862
+ @installer.exec_distclean
863
+ end
864
+
865
+ end
866
+
867
+
868
+ class ToplevelInstallerMulti < ToplevelInstaller
869
+
870
+ include HookUtils
871
+ include HookScriptAPI
872
+ include FileOperations
873
+
874
+ def initialize(ardir)
875
+ super
876
+ @packages = all_dirs_in("#{@ardir}/packages")
877
+ raise 'no package exists' if @packages.empty?
878
+ end
879
+
880
+ def run_metaconfigs
881
+ eval_file_ifexist "#{@ardir}/metaconfig"
882
+ @packages.each do |name|
883
+ eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
884
+ end
885
+ end
886
+
887
+ def init_installers
888
+ @installers = {}
889
+ @packages.each do |pack|
890
+ @installers[pack] = Installer.new(@config, @options,
891
+ "#{@ardir}/packages/#{pack}",
892
+ "packages/#{pack}")
893
+ end
894
+
895
+ with = extract_selection(config('with'))
896
+ without = extract_selection(config('without'))
897
+ @selected = @installers.keys.select {|name|
898
+ (with.empty? or with.include?(name)) \
899
+ and not without.include?(name)
900
+ }
901
+ end
902
+
903
+ def extract_selection(list)
904
+ a = list.split(/,/)
905
+ a.each do |name|
906
+ raise InstallError, "no such package: #{name}" \
907
+ unless @installers.key?(name)
908
+ end
909
+ a
910
+ end
911
+
912
+ def print_usage(f)
913
+ super
914
+ f.puts 'Inluded packages:'
915
+ f.puts ' ' + @packages.sort.join(' ')
916
+ f.puts
917
+ end
918
+
919
+ #
920
+ # multi-package metaconfig API
921
+ #
922
+
923
+ attr_reader :packages
924
+
925
+ def declare_packages(list)
926
+ raise 'package list is empty' if list.empty?
927
+ list.each do |name|
928
+ raise "directory packages/#{name} does not exist"\
929
+ unless File.dir?("#{@ardir}/packages/#{name}")
930
+ end
931
+ @packages = list
932
+ end
933
+
934
+ #
935
+ # Task Handlers
936
+ #
937
+
938
+ def exec_config
939
+ run_hook 'pre-config'
940
+ each_selected_installers {|inst| inst.exec_config }
941
+ run_hook 'post-config'
942
+ @config.save # must be final
943
+ end
944
+
945
+ def exec_setup
946
+ run_hook 'pre-setup'
947
+ each_selected_installers {|inst| inst.exec_setup }
948
+ run_hook 'post-setup'
949
+ end
950
+
951
+ def exec_install
952
+ run_hook 'pre-install'
953
+ each_selected_installers {|inst| inst.exec_install }
954
+ run_hook 'post-install'
955
+ end
956
+
957
+ def exec_clean
958
+ rm_f ConfigTable::SAVE_FILE
959
+ run_hook 'pre-clean'
960
+ each_selected_installers {|inst| inst.exec_clean }
961
+ run_hook 'post-clean'
962
+ end
963
+
964
+ def exec_distclean
965
+ rm_f ConfigTable::SAVE_FILE
966
+ run_hook 'pre-distclean'
967
+ each_selected_installers {|inst| inst.exec_distclean }
968
+ run_hook 'post-distclean'
969
+ end
970
+
971
+ #
972
+ # lib
973
+ #
974
+
975
+ def each_selected_installers
976
+ Dir.mkdir 'packages' unless File.dir?('packages')
977
+ @selected.each do |pack|
978
+ $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
979
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
980
+ Dir.chdir "packages/#{pack}"
981
+ yield @installers[pack]
982
+ Dir.chdir '../..'
983
+ end
984
+ end
985
+
986
+ def verbose?
987
+ @options['verbose']
988
+ end
989
+
990
+ def no_harm?
991
+ @options['no-harm']
992
+ end
993
+
994
+ end
995
+
996
+
997
+ class Installer
998
+
999
+ FILETYPES = %w( bin lib ext data )
1000
+
1001
+ include HookScriptAPI
1002
+ include HookUtils
1003
+ include FileOperations
1004
+
1005
+ def initialize(config, opt, srcroot, objroot)
1006
+ @config = config
1007
+ @options = opt
1008
+ @srcdir = File.expand_path(srcroot)
1009
+ @objdir = File.expand_path(objroot)
1010
+ @currdir = '.'
1011
+ end
1012
+
1013
+ def inspect
1014
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1015
+ end
1016
+
1017
+ #
1018
+ # Hook Script API bases
1019
+ #
1020
+
1021
+ def srcdir_root
1022
+ @srcdir
1023
+ end
1024
+
1025
+ def objdir_root
1026
+ @objdir
1027
+ end
1028
+
1029
+ def relpath
1030
+ @currdir
1031
+ end
1032
+
1033
+ #
1034
+ # configs/options
1035
+ #
1036
+
1037
+ def no_harm?
1038
+ @options['no-harm']
1039
+ end
1040
+
1041
+ def verbose?
1042
+ @options['verbose']
1043
+ end
1044
+
1045
+ def verbose_off
1046
+ begin
1047
+ save, @options['verbose'] = @options['verbose'], false
1048
+ yield
1049
+ ensure
1050
+ @options['verbose'] = save
1051
+ end
1052
+ end
1053
+
1054
+ #
1055
+ # TASK config
1056
+ #
1057
+
1058
+ def exec_config
1059
+ exec_task_traverse 'config'
1060
+ end
1061
+
1062
+ def config_dir_bin(rel)
1063
+ end
1064
+
1065
+ def config_dir_lib(rel)
1066
+ end
1067
+
1068
+ def config_dir_ext(rel)
1069
+ extconf if extdir?(curr_srcdir())
1070
+ end
1071
+
1072
+ def extconf
1073
+ opt = @options['config-opt'].join(' ')
1074
+ command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
1075
+ end
1076
+
1077
+ def config_dir_data(rel)
1078
+ end
1079
+
1080
+ #
1081
+ # TASK setup
1082
+ #
1083
+
1084
+ def exec_setup
1085
+ exec_task_traverse 'setup'
1086
+ end
1087
+
1088
+ def setup_dir_bin(rel)
1089
+ all_files_in(curr_srcdir()).each do |fname|
1090
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
1091
+ end
1092
+ end
1093
+
1094
+ def adjust_shebang(path)
1095
+ return if no_harm?
1096
+ tmpfile = File.basename(path) + '.tmp'
1097
+ begin
1098
+ File.open(path, 'rb') {|r|
1099
+ File.open(tmpfile, 'wb') {|w|
1100
+ first = r.gets
1101
+ return unless should_modify_shebang?(first)
1102
+ $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1103
+ w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
1104
+ w.write r.read
1105
+ }
1106
+ }
1107
+ move_file tmpfile, File.basename(path)
1108
+ ensure
1109
+ File.unlink tmpfile if File.exist?(tmpfile)
1110
+ end
1111
+ end
1112
+
1113
+ def should_modify_shebang?(line)
1114
+ File.basename(config('ruby-path')) == 'ruby' or
1115
+ shebang_command(line) == 'ruby'
1116
+ end
1117
+
1118
+ def shebang_command(line)
1119
+ cmd, arg = *line.sub(/\A\#!/, '').strip.split(/\s+/, 2)
1120
+ cmd
1121
+ end
1122
+
1123
+ def setup_dir_lib(rel)
1124
+ end
1125
+
1126
+ def setup_dir_ext(rel)
1127
+ make if extdir?(curr_srcdir())
1128
+ end
1129
+
1130
+ def setup_dir_data(rel)
1131
+ end
1132
+
1133
+ #
1134
+ # TASK install
1135
+ #
1136
+
1137
+ def exec_install
1138
+ exec_task_traverse 'install'
1139
+ end
1140
+
1141
+ def install_dir_bin(rel)
1142
+ install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755
1143
+ end
1144
+
1145
+ def install_dir_lib(rel)
1146
+ install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
1147
+ end
1148
+
1149
+ def install_dir_ext(rel)
1150
+ return unless extdir?(curr_srcdir())
1151
+ install_files ruby_extentions('.'),
1152
+ "#{config('so-dir')}/#{rel}",
1153
+ 0555
1154
+ end
1155
+
1156
+ def install_dir_data(rel)
1157
+ install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
1158
+ end
1159
+
1160
+ def install_files(list, dest, mode)
1161
+ mkdir_p dest, @options['install-prefix']
1162
+ list.each do |fname|
1163
+ install fname, dest, mode, @options['install-prefix']
1164
+ end
1165
+ end
1166
+
1167
+ def ruby_scripts
1168
+ collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1169
+ end
1170
+
1171
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1172
+ reject_patterns = %w(
1173
+ core RCSLOG tags TAGS .make.state
1174
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1175
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1176
+
1177
+ *.org *.in .*
1178
+ )
1179
+ mapping = {
1180
+ '.' => '\.',
1181
+ '$' => '\$',
1182
+ '#' => '\#',
1183
+ '*' => '.*'
1184
+ }
1185
+ REJECT_PATTERNS = Regexp.new('\A(?:' +
1186
+ reject_patterns.map {|pat|
1187
+ pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1188
+ }.join('|') +
1189
+ ')\z')
1190
+
1191
+ def collect_filenames_auto
1192
+ mapdir((existfiles() - hookfiles()).reject {|fname|
1193
+ REJECT_PATTERNS =~ fname
1194
+ })
1195
+ end
1196
+
1197
+ def existfiles
1198
+ all_files_in(curr_srcdir()) | all_files_in('.')
1199
+ end
1200
+
1201
+ def hookfiles
1202
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1203
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1204
+ }.flatten
1205
+ end
1206
+
1207
+ def mapdir(filelist)
1208
+ filelist.map {|fname|
1209
+ if File.exist?(fname) # objdir
1210
+ fname
1211
+ else # srcdir
1212
+ File.join(curr_srcdir(), fname)
1213
+ end
1214
+ }
1215
+ end
1216
+
1217
+ def ruby_extentions(dir)
1218
+ _ruby_extentions(dir) or
1219
+ raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
1220
+ end
1221
+
1222
+ DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
1223
+
1224
+ def _ruby_extentions(dir)
1225
+ Dir.open(dir) {|d|
1226
+ return d.select {|fname| DLEXT =~ fname }
1227
+ }
1228
+ end
1229
+
1230
+ #
1231
+ # TASK clean
1232
+ #
1233
+
1234
+ def exec_clean
1235
+ exec_task_traverse 'clean'
1236
+ rm_f ConfigTable::SAVE_FILE
1237
+ rm_f 'InstalledFiles'
1238
+ end
1239
+
1240
+ def clean_dir_bin(rel)
1241
+ end
1242
+
1243
+ def clean_dir_lib(rel)
1244
+ end
1245
+
1246
+ def clean_dir_ext(rel)
1247
+ return unless extdir?(curr_srcdir())
1248
+ make 'clean' if File.file?('Makefile')
1249
+ end
1250
+
1251
+ def clean_dir_data(rel)
1252
+ end
1253
+
1254
+ #
1255
+ # TASK distclean
1256
+ #
1257
+
1258
+ def exec_distclean
1259
+ exec_task_traverse 'distclean'
1260
+ rm_f ConfigTable::SAVE_FILE
1261
+ rm_f 'InstalledFiles'
1262
+ end
1263
+
1264
+ def distclean_dir_bin(rel)
1265
+ end
1266
+
1267
+ def distclean_dir_lib(rel)
1268
+ end
1269
+
1270
+ def distclean_dir_ext(rel)
1271
+ return unless extdir?(curr_srcdir())
1272
+ make 'distclean' if File.file?('Makefile')
1273
+ end
1274
+
1275
+ #
1276
+ # lib
1277
+ #
1278
+
1279
+ def exec_task_traverse(task)
1280
+ run_hook "pre-#{task}"
1281
+ FILETYPES.each do |type|
1282
+ if config('without-ext') == 'yes' and type == 'ext'
1283
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1284
+ next
1285
+ end
1286
+ traverse task, type, "#{task}_dir_#{type}"
1287
+ end
1288
+ run_hook "post-#{task}"
1289
+ end
1290
+
1291
+ def traverse(task, rel, mid)
1292
+ dive_into(rel) {
1293
+ run_hook "pre-#{task}"
1294
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1295
+ all_dirs_in(curr_srcdir()).each do |d|
1296
+ traverse task, "#{rel}/#{d}", mid
1297
+ end
1298
+ run_hook "post-#{task}"
1299
+ }
1300
+ end
1301
+
1302
+ def dive_into(rel)
1303
+ return unless File.dir?("#{@srcdir}/#{rel}")
1304
+
1305
+ dir = File.basename(rel)
1306
+ Dir.mkdir dir unless File.dir?(dir)
1307
+ prevdir = Dir.pwd
1308
+ Dir.chdir dir
1309
+ $stderr.puts '---> ' + rel if verbose?
1310
+ @currdir = rel
1311
+ yield
1312
+ Dir.chdir prevdir
1313
+ $stderr.puts '<--- ' + rel if verbose?
1314
+ @currdir = File.dirname(rel)
1315
+ end
1316
+
1317
+ end
1318
+
1319
+
1320
+ if $0 == __FILE__
1321
+ begin
1322
+ if multipackage_install?
1323
+ ToplevelInstallerMulti.invoke
1324
+ else
1325
+ ToplevelInstaller.invoke
1326
+ end
1327
+ rescue
1328
+ raise if $DEBUG
1329
+ $stderr.puts $!.message
1330
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1331
+ exit 1
1332
+ end
1333
+ end