xmpp4r 0.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/ChangeLog +10 -0
  2. data/README +12 -4
  3. data/Rakefile +1 -1
  4. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +2 -0
  5. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +2 -0
  6. data/data/doc/xmpp4r/examples/basic/client.rb +2 -2
  7. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +1 -0
  8. data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +1 -1
  9. data/lib/xmpp4r/bytestreams.rb +4 -0
  10. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +10 -4
  11. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +4 -0
  12. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +5 -1
  13. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +5 -1
  14. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +5 -1
  15. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +4 -0
  16. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +20 -6
  17. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +4 -0
  18. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +4 -0
  19. data/lib/xmpp4r/bytestreams/iq/si.rb +4 -0
  20. data/lib/xmpp4r/component.rb +1 -1
  21. data/lib/xmpp4r/dataforms.rb +4 -0
  22. data/lib/xmpp4r/delay.rb +4 -0
  23. data/lib/xmpp4r/discovery.rb +4 -0
  24. data/lib/xmpp4r/feature_negotiation.rb +4 -0
  25. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +4 -0
  26. data/lib/xmpp4r/iq.rb +1 -1
  27. data/lib/xmpp4r/message.rb +10 -3
  28. data/lib/xmpp4r/muc.rb +4 -0
  29. data/lib/xmpp4r/muc/helper/simplemucclient.rb +4 -0
  30. data/lib/xmpp4r/presence.rb +9 -5
  31. data/lib/xmpp4r/rexmladdons.rb +65 -3
  32. data/lib/xmpp4r/roster.rb +4 -0
  33. data/lib/xmpp4r/roster/helper/roster.rb +18 -3
  34. data/lib/xmpp4r/sasl.rb +59 -10
  35. data/lib/xmpp4r/stream.rb +22 -2
  36. data/lib/xmpp4r/vcard.rb +4 -0
  37. data/lib/xmpp4r/vcard/helper/vcard.rb +4 -4
  38. data/lib/xmpp4r/version.rb +4 -0
  39. data/lib/xmpp4r/xmpp4r.rb +1 -1
  40. data/setup.rb +800 -575
  41. data/test/bytestreams/tc_socks5bytestreams.rb +46 -0
  42. data/test/tc_iq.rb +1 -1
  43. data/test/tc_rexml.rb +60 -0
  44. data/test/ts_xmpp4r.rb +2 -1
  45. data/test/vcard/tc_helper.rb +49 -0
  46. metadata +96 -94
  47. data/test/roster/.tc_helper.rb.swp +0 -0
@@ -1,3 +1,7 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
1
5
  require 'xmpp4r/roster/iq/roster.rb'
2
6
  require 'xmpp4r/roster/helper/roster.rb'
3
7
  require 'xmpp4r/roster/x/roster.rb'
@@ -12,6 +12,10 @@ module Jabber
12
12
  # The Roster helper intercepts <tt><iq/></tt> stanzas with Jabber::IqQueryRoster
13
13
  # and <tt><presence/></tt> stanzas, but provides cbs which allow the programmer
14
14
  # to keep track of updates.
15
+ #
16
+ # A thread for any received stanza is spawned, so the user can invoke
17
+ # accept_subscription et al in the callback blocks, without stopping
18
+ # the current (= parser) thread when waiting for a reply.
15
19
  class Helper
16
20
  ##
17
21
  # All items in your roster
@@ -25,6 +29,10 @@ module Jabber
25
29
  #
26
30
  # Request a roster
27
31
  # (Remember to send initial presence afterwards!)
32
+ #
33
+ # The initialization will not wait for the roster being received,
34
+ # use add_query_callback to get notifyed when Roster::Helper#items
35
+ # has been filled.
28
36
  def initialize(stream)
29
37
  @stream = stream
30
38
  @items = {}
@@ -37,12 +45,16 @@ module Jabber
37
45
 
38
46
  # Register cbs
39
47
  stream.add_iq_callback(120, self) { |iq|
40
- handle_iq(iq)
48
+ Thread.new do
49
+ handle_iq(iq)
50
+ end
41
51
  }
42
52
  stream.add_presence_callback(120, self) { |pres|
43
- handle_presence(pres)
53
+ Thread.new do
54
+ handle_presence(pres)
55
+ end
44
56
  }
45
-
57
+
46
58
  # Request the roster
47
59
  rosterget = Iq.new_rosterget
48
60
  stream.send(rosterget)
@@ -171,12 +183,15 @@ module Jabber
171
183
  # used internally
172
184
  def handle_presence(pres)
173
185
  item = self[pres.from]
186
+
174
187
  if [:subscribed, :unsubscribe, :unsubscribed].include?(pres.type)
175
188
  @subscription_cbs.process(item, pres)
176
189
  true
190
+
177
191
  elsif pres.type == :subscribe
178
192
  @subscription_request_cbs.process(item, pres)
179
193
  true
194
+
180
195
  else
181
196
  unless item.nil?
182
197
  update_presence(item, pres)
@@ -1,3 +1,7 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
1
5
  require 'base64'
2
6
  require 'digest/md5'
3
7
 
@@ -41,7 +45,7 @@ module Jabber
41
45
  end
42
46
 
43
47
  def generate_nonce
44
- Digest::MD5.new(Time.new.to_f.to_s).hexdigest
48
+ Digest::MD5.hexdigest(Time.new.to_f.to_s)
45
49
  end
46
50
  end
47
51
 
@@ -78,13 +82,7 @@ module Jabber
78
82
  error = nil
79
83
  @stream.send(generate_auth('DIGEST-MD5')) { |reply|
80
84
  if reply.name == 'challenge' and reply.namespace == NS_SASL
81
- challenge_text = Base64::decode64(reply.text)
82
- challenge_text.split(/,/).each { |s|
83
- key, value = s.split(/=/, 2)
84
- value.sub!(/^"/, '')
85
- value.sub!(/"$/, '')
86
- challenge[key] = value
87
- }
85
+ challenge = decode_challenge(reply.text)
88
86
  else
89
87
  error = reply.first_element(nil).name
90
88
  end
@@ -96,6 +94,49 @@ module Jabber
96
94
  @realm = challenge['realm']
97
95
  end
98
96
 
97
+ def decode_challenge(challenge)
98
+ text = Base64::decode64(challenge)
99
+ res = {}
100
+
101
+ state = :key
102
+ key = ''
103
+ value = ''
104
+
105
+ text.scan(/./) do |ch|
106
+ if state == :key
107
+ if ch == '='
108
+ state = :value
109
+ else
110
+ key += ch
111
+ end
112
+
113
+ elsif state == :value
114
+ if ch == ','
115
+ res[key] = value
116
+ key = ''
117
+ value = ''
118
+ state = :key
119
+ elsif ch == '"' and value == ''
120
+ state = :quote
121
+ else
122
+ value += ch
123
+ end
124
+
125
+ elsif state == :quote
126
+ if ch == '"'
127
+ state = :value
128
+ else
129
+ value += ch
130
+ end
131
+ end
132
+ end
133
+ res[key] = value unless key == ''
134
+
135
+ Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text.inspect}\n#{res.inspect}")
136
+
137
+ res
138
+ end
139
+
99
140
  ##
100
141
  # * Send a response
101
142
  # * Wait for the server's challenge (which aren't checked)
@@ -117,17 +158,25 @@ module Jabber
117
158
  end
118
159
  }
119
160
 
161
+ response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
162
+ Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}")
163
+
120
164
  r = REXML::Element.new('response')
121
165
  r.add_namespace NS_SASL
122
- r.text = Base64::encode64(response.collect { |k,v| "#{k}=#{v}" }.join(',')).gsub(/\s/, '')
166
+ r.text = Base64::encode64(response_text).gsub(/\s/, '')
167
+
168
+ success_already = false
123
169
  error = nil
124
170
  @stream.send(r) { |reply|
125
- if reply.name != 'challenge'
171
+ if reply.name == 'success'
172
+ success_already = true
173
+ elsif reply.name != 'challenge'
126
174
  error = reply.first_element(nil).name
127
175
  end
128
176
  true
129
177
  }
130
178
 
179
+ return if success_already
131
180
  raise error if error
132
181
 
133
182
  # TODO: check the challenge from the server
@@ -18,6 +18,14 @@ module Jabber
18
18
  ##
19
19
  # The stream class manages a connection stream (a file descriptor using which
20
20
  # XML messages are read and sent)
21
+ #
22
+ # You may register callbacks for the three Jabber stanzas
23
+ # (message, presence and iq) and use the send and send_with_id
24
+ # methods.
25
+ #
26
+ # To ensure the order of received stanzas, callback blocks are
27
+ # launched in the parser thread. If further blocking operations
28
+ # are intended in those callbacks, run your own thread there.
21
29
  class Stream
22
30
  DISCONNECTED = 1
23
31
  CONNECTED = 2
@@ -352,6 +360,10 @@ module Jabber
352
360
  # Sends XML data to the socket and (optionally) waits
353
361
  # to process received data.
354
362
  #
363
+ # Do not invoke this in a callback but in a seperate thread
364
+ # because we may not suspend the parser-thread (in whose
365
+ # context callbacks are executed).
366
+ #
355
367
  # xml:: [String] The xml data to send
356
368
  # &block:: [Block] The optional block
357
369
  def send(xml, &block)
@@ -384,12 +396,16 @@ module Jabber
384
396
  # generated by Jabber::IdGenerator if not already set.
385
397
  #
386
398
  # The block will be called once: when receiving a stanza with the
387
- # same Jabber::XMLStanza#id. It *must* return true to complete this!
399
+ # same Jabber::XMPPStanza#id. There is no need to return true to
400
+ # complete this! Instead the return value of the block will be
401
+ # returned.
388
402
  #
389
403
  # Be aware that if a stanza with <tt>type='error'</tt> is received
390
404
  # the function does not yield but raises an ErrorException with
391
405
  # the corresponding error element.
392
406
  #
407
+ # Please see Stream#send for some implementational details.
408
+ #
393
409
  # Please read the note about nesting at Stream#send
394
410
  # xml:: [XMLStanza]
395
411
  def send_with_id(xml, &block)
@@ -397,6 +413,7 @@ module Jabber
397
413
  xml.id = Jabber::IdGenerator.instance.generate_id
398
414
  end
399
415
 
416
+ res = nil
400
417
  error = nil
401
418
  send(xml) do |received|
402
419
  if received.kind_of? XMLStanza and received.id == xml.id
@@ -404,7 +421,8 @@ module Jabber
404
421
  error = (received.error ? received.error : Error.new)
405
422
  true
406
423
  else
407
- yield(received)
424
+ res = yield(received)
425
+ true
408
426
  end
409
427
  else
410
428
  false
@@ -414,6 +432,8 @@ module Jabber
414
432
  unless error.nil?
415
433
  raise ErrorException.new(error)
416
434
  end
435
+
436
+ res
417
437
  end
418
438
 
419
439
  ##
@@ -1,3 +1,7 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
1
5
  require 'xmpp4r/vcard/helper/vcard.rb'
2
6
  require 'xmpp4r/vcard/iq/vcard.rb'
3
7
 
@@ -70,15 +70,15 @@ module Jabber
70
70
  ##
71
71
  # Quickly initialize a Vcard helper and get
72
72
  # a vCard. See Vcard#get
73
- def Vcard.get(stream, jid=nil)
74
- Vcard.new(stream).get(jid)
73
+ def self.get(stream, jid=nil)
74
+ new(stream).get(jid)
75
75
  end
76
76
 
77
77
  ##
78
78
  # Quickly initialize a Vcard helper and set
79
79
  # your vCard. See Vcard#set
80
- def Vcard.set(stream, iqvcard)
81
- Vcard.new(stream).set(iqvcard)
80
+ def self.set(stream, iqvcard)
81
+ new(stream).set(iqvcard)
82
82
  end
83
83
  end
84
84
  end
@@ -1,3 +1,7 @@
1
+ # =XMPP4R - XMPP Library for Ruby
2
+ # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
+ # Website::http://home.gna.org/xmpp4r/
4
+
1
5
  require 'xmpp4r/version/helper/responder.rb'
2
6
  require 'xmpp4r/version/helper/simpleresponder.rb'
3
7
  require 'xmpp4r/version/iq/version.rb'
@@ -8,7 +8,7 @@
8
8
  # a simple debug logging support.
9
9
  module Jabber
10
10
  # XMPP4R Version number
11
- XMPP4R_VERSION = '0.3'
11
+ XMPP4R_VERSION = '0.3.1'
12
12
  end
13
13
 
14
14
  require 'xmpp4r/client'
data/setup.rb CHANGED
@@ -1,7 +1,7 @@
1
- #!/usr/bin/env ruby
1
+ #
2
2
  # setup.rb
3
3
  #
4
- # Copyright (c) 2000-2004 Minero Aoki
4
+ # Copyright (c) 2000-2005 Minero Aoki
5
5
  #
6
6
  # This program is free software.
7
7
  # You can distribute/modify this program under the terms of
@@ -22,176 +22,68 @@ unless File.respond_to?(:read) # Ruby 1.6
22
22
  end
23
23
  end
24
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
+
25
33
  def File.binread(fname)
26
34
  open(fname, 'rb') {|f|
27
35
  return f.read
28
36
  }
29
37
  end
30
38
 
31
- # for corrupted windows stat(2)
39
+ # for corrupted Windows' stat(2)
32
40
  def File.dir?(path)
33
41
  File.directory?((path[-1,1] == '/') ? path : path + '/')
34
42
  end
35
43
 
36
44
 
37
- class SetupError < StandardError; end
38
-
39
- def setup_rb_error(msg)
40
- raise SetupError, msg
41
- end
42
-
43
- #
44
- # Config
45
- #
46
-
47
- if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
48
- ARGV.delete(arg)
49
- require arg.split(/=/, 2)[1]
50
- $".push 'rbconfig.rb'
51
- else
52
- require 'rbconfig'
53
- end
54
-
55
- def multipackage_install?
56
- FileTest.directory?(File.dirname($0) + '/packages')
57
- end
58
-
59
-
60
- class ConfigItem
61
- def initialize(name, template, default, desc)
62
- @name = name.freeze
63
- @template = template
64
- @value = default
65
- @default = default.dup.freeze
66
- @description = desc
67
- end
68
-
69
- attr_reader :name
70
- attr_reader :description
71
-
72
- attr_accessor :default
73
- alias help_default default
74
-
75
- def help_opt
76
- "--#{@name}=#{@template}"
77
- end
78
-
79
- def value
80
- @value
81
- end
82
-
83
- def eval(table)
84
- @value.gsub(%r<\$([^/]+)>) { table[$1] }
85
- end
86
-
87
- def set(val)
88
- @value = check(val)
89
- end
90
-
91
- private
92
-
93
- def check(val)
94
- setup_rb_error "config: --#{name} requires argument" unless val
95
- val
96
- end
97
- end
98
-
99
- class BoolItem < ConfigItem
100
- def config_type
101
- 'bool'
102
- end
103
-
104
- def help_opt
105
- "--#{@name}"
106
- end
45
+ class ConfigTable
107
46
 
108
- private
109
-
110
- def check(val)
111
- return 'yes' unless val
112
- unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ val
113
- setup_rb_error "config: --#{@name} accepts only yes/no for argument"
114
- end
115
- (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
116
- end
117
- end
47
+ include Enumerable
118
48
 
119
- class PathItem < ConfigItem
120
- def config_type
121
- 'path'
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
122
58
  end
123
59
 
124
- private
60
+ attr_accessor :install_prefix
61
+ attr_accessor :config_opt
125
62
 
126
- def check(path)
127
- setup_rb_error "config: --#{@name} requires argument" unless path
128
- path[0,1] == '$' ? path : File.expand_path(path)
129
- end
130
- end
131
-
132
- class ProgramItem < ConfigItem
133
- def config_type
134
- 'program'
135
- end
136
- end
63
+ attr_writer :verbose
137
64
 
138
- class SelectItem < ConfigItem
139
- def initialize(name, template, default, desc)
140
- super
141
- @ok = template.split('/')
142
- end
143
-
144
- def config_type
145
- 'select'
65
+ def verbose?
66
+ @verbose
146
67
  end
147
68
 
148
- private
149
-
150
- def check(val)
151
- unless @ok.include?(val.strip)
152
- setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
153
- end
154
- val.strip
155
- end
156
- end
69
+ attr_writer :no_harm
157
70
 
158
- class PackageSelectionItem < ConfigItem
159
- def initialize(name, template, default, help_default, desc)
160
- super name, template, default, desc
161
- @help_default = help_default
71
+ def no_harm?
72
+ @no_harm
162
73
  end
163
74
 
164
- attr_reader :help_default
165
-
166
- def config_type
167
- 'package'
75
+ def [](key)
76
+ lookup(key).resolve(self)
168
77
  end
169
78
 
170
- private
171
-
172
- def check(val)
173
- unless File.dir?("packages/#{val}")
174
- setup_rb_error "config: no such package: #{val}"
175
- end
176
- val
79
+ def []=(key, val)
80
+ lookup(key).set val
177
81
  end
178
- end
179
-
180
- class ConfigTable_class
181
82
 
182
- def initialize(items)
183
- @items = items
184
- @table = {}
185
- items.each do |i|
186
- @table[i.name] = i
187
- end
188
- ALIASES.each do |ali, name|
189
- @table[ali] = @table[name]
190
- end
83
+ def names
84
+ @items.map {|i| i.name }
191
85
  end
192
86
 
193
- include Enumerable
194
-
195
87
  def each(&block)
196
88
  @items.each(&block)
197
89
  end
@@ -201,7 +93,7 @@ class ConfigTable_class
201
93
  end
202
94
 
203
95
  def lookup(name)
204
- @table[name] or raise ArgumentError, "no such config item: #{name}"
96
+ @table[name] or setup_rb_error "no such config item: #{name}"
205
97
  end
206
98
 
207
99
  def add(item)
@@ -216,24 +108,24 @@ class ConfigTable_class
216
108
  item
217
109
  end
218
110
 
219
- def new
220
- dup()
111
+ def load_script(path, inst = nil)
112
+ if File.file?(path)
113
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
114
+ end
221
115
  end
222
116
 
223
117
  def savefile
224
118
  '.config'
225
119
  end
226
120
 
227
- def load
121
+ def load_savefile
228
122
  begin
229
- t = dup()
230
123
  File.foreach(savefile()) do |line|
231
124
  k, v = *line.split(/=/, 2)
232
- t[k] = v.strip
125
+ self[k] = v.strip
233
126
  end
234
- t
235
127
  rescue Errno::ENOENT
236
- setup_rb_error $!.message + "#{File.basename($0)} config first"
128
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
237
129
  end
238
130
  end
239
131
 
@@ -241,117 +133,151 @@ class ConfigTable_class
241
133
  @items.each {|i| i.value }
242
134
  File.open(savefile(), 'w') {|f|
243
135
  @items.each do |i|
244
- f.printf "%s=%s\n", i.name, i.value if i.value
136
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
245
137
  end
246
138
  }
247
139
  end
248
140
 
249
- def [](key)
250
- lookup(key).eval(self)
141
+ def load_standard_entries
142
+ standard_entries(@rbconfig).each do |ent|
143
+ add ent
144
+ end
251
145
  end
252
146
 
253
- def []=(key, val)
254
- lookup(key).set val
255
- end
147
+ def standard_entries(rbconfig)
148
+ c = rbconfig
149
+
150
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
151
+
152
+ major = c['MAJOR'].to_i
153
+ minor = c['MINOR'].to_i
154
+ teeny = c['TEENY'].to_i
155
+ version = "#{major}.#{minor}"
156
+
157
+ # ruby ver. >= 1.4.4?
158
+ newpath_p = ((major >= 2) or
159
+ ((major == 1) and
160
+ ((minor >= 5) or
161
+ ((minor == 4) and (teeny >= 4)))))
162
+
163
+ if c['rubylibdir']
164
+ # V > 1.6.3
165
+ libruby = "#{c['prefix']}/lib/ruby"
166
+ librubyver = c['rubylibdir']
167
+ librubyverarch = c['archdir']
168
+ siteruby = c['sitedir']
169
+ siterubyver = c['sitelibdir']
170
+ siterubyverarch = c['sitearchdir']
171
+ elsif newpath_p
172
+ # 1.4.4 <= V <= 1.6.3
173
+ libruby = "#{c['prefix']}/lib/ruby"
174
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
175
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
176
+ siteruby = c['sitedir']
177
+ siterubyver = "$siteruby/#{version}"
178
+ siterubyverarch = "$siterubyver/#{c['arch']}"
179
+ else
180
+ # V < 1.4.4
181
+ libruby = "#{c['prefix']}/lib/ruby"
182
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
183
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
184
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
185
+ siterubyver = siteruby
186
+ siterubyverarch = "$siterubyver/#{c['arch']}"
187
+ end
188
+ parameterize = lambda {|path|
189
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
190
+ }
256
191
 
257
- end
192
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
193
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
194
+ else
195
+ makeprog = 'make'
196
+ end
258
197
 
259
- c = ::Config::CONFIG
260
-
261
- rubypath = c['bindir'] + '/' + c['ruby_install_name']
262
-
263
- major = c['MAJOR'].to_i
264
- minor = c['MINOR'].to_i
265
- teeny = c['TEENY'].to_i
266
- version = "#{major}.#{minor}"
267
-
268
- # ruby ver. >= 1.4.4?
269
- newpath_p = ((major >= 2) or
270
- ((major == 1) and
271
- ((minor >= 5) or
272
- ((minor == 4) and (teeny >= 4)))))
273
-
274
- if c['rubylibdir']
275
- # V < 1.6.3
276
- _stdruby = c['rubylibdir']
277
- _siteruby = c['sitedir']
278
- _siterubyver = c['sitelibdir']
279
- _siterubyverarch = c['sitearchdir']
280
- elsif newpath_p
281
- # 1.4.4 <= V <= 1.6.3
282
- _stdruby = "$prefix/lib/ruby/#{version}"
283
- _siteruby = c['sitedir']
284
- _siterubyver = "$siteruby/#{version}"
285
- _siterubyverarch = "$siterubyver/#{c['arch']}"
286
- else
287
- # V < 1.4.4
288
- _stdruby = "$prefix/lib/ruby/#{version}"
289
- _siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
290
- _siterubyver = _siteruby
291
- _siterubyverarch = "$siterubyver/#{c['arch']}"
292
- end
293
- libdir = '-* dummy libdir *-'
294
- stdruby = '-* dummy rubylibdir *-'
295
- siteruby = '-* dummy site_ruby *-'
296
- siterubyver = '-* dummy site_ruby version *-'
297
- parameterize = lambda {|path|
298
- path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')\
299
- .sub(/\A#{Regexp.quote(libdir)}/, '$libdir')\
300
- .sub(/\A#{Regexp.quote(stdruby)}/, '$stdruby')\
301
- .sub(/\A#{Regexp.quote(siteruby)}/, '$siteruby')\
302
- .sub(/\A#{Regexp.quote(siterubyver)}/, '$siterubyver')
303
- }
304
- libdir = parameterize.call(c['libdir'])
305
- stdruby = parameterize.call(_stdruby)
306
- siteruby = parameterize.call(_siteruby)
307
- siterubyver = parameterize.call(_siterubyver)
308
- siterubyverarch = parameterize.call(_siterubyverarch)
309
-
310
- if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
311
- makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
312
- else
313
- makeprog = 'make'
314
- end
198
+ [
199
+ ExecItem.new('installdirs', 'std/site/home',
200
+ 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
201
+ {|val, table|
202
+ case val
203
+ when 'std'
204
+ table['rbdir'] = '$librubyver'
205
+ table['sodir'] = '$librubyverarch'
206
+ when 'site'
207
+ table['rbdir'] = '$siterubyver'
208
+ table['sodir'] = '$siterubyverarch'
209
+ when 'home'
210
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
211
+ table['prefix'] = ENV['HOME']
212
+ table['rbdir'] = '$libdir/ruby'
213
+ table['sodir'] = '$libdir/ruby'
214
+ end
215
+ },
216
+ PathItem.new('prefix', 'path', c['prefix'],
217
+ 'path prefix of target environment'),
218
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
219
+ 'the directory for commands'),
220
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
221
+ 'the directory for libraries'),
222
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
223
+ 'the directory for shared data'),
224
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
225
+ 'the directory for man pages'),
226
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
227
+ 'the directory for system configuration files'),
228
+ PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
229
+ 'the directory for local state data'),
230
+ PathItem.new('libruby', 'path', libruby,
231
+ 'the directory for ruby libraries'),
232
+ PathItem.new('librubyver', 'path', librubyver,
233
+ 'the directory for standard ruby libraries'),
234
+ PathItem.new('librubyverarch', 'path', librubyverarch,
235
+ 'the directory for standard ruby extensions'),
236
+ PathItem.new('siteruby', 'path', siteruby,
237
+ 'the directory for version-independent aux ruby libraries'),
238
+ PathItem.new('siterubyver', 'path', siterubyver,
239
+ 'the directory for aux ruby libraries'),
240
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
241
+ 'the directory for aux ruby binaries'),
242
+ PathItem.new('rbdir', 'path', '$siterubyver',
243
+ 'the directory for ruby scripts'),
244
+ PathItem.new('sodir', 'path', '$siterubyverarch',
245
+ 'the directory for ruby extentions'),
246
+ PathItem.new('rubypath', 'path', rubypath,
247
+ 'the path to set to #! line'),
248
+ ProgramItem.new('rubyprog', 'name', rubypath,
249
+ 'the ruby program using for installation'),
250
+ ProgramItem.new('makeprog', 'name', makeprog,
251
+ 'the make program to compile ruby extentions'),
252
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
253
+ 'shebang line (#!) editing mode'),
254
+ BoolItem.new('without-ext', 'yes/no', 'no',
255
+ 'does not compile/install ruby extentions')
256
+ ]
257
+ end
258
+ private :standard_entries
259
+
260
+ def load_multipackage_entries
261
+ multipackage_entries().each do |ent|
262
+ add ent
263
+ end
264
+ end
265
+
266
+ def multipackage_entries
267
+ [
268
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
269
+ 'package names that you want to install'),
270
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
271
+ 'package names that you do not want to install')
272
+ ]
273
+ end
274
+ private :multipackage_entries
315
275
 
316
- common_conf = [
317
- PathItem.new('prefix', 'path', c['prefix'],
318
- 'path prefix of target environment'),
319
- PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
320
- 'the directory for commands'),
321
- PathItem.new('libdir', 'path', libdir,
322
- 'the directory for libraries'),
323
- PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
324
- 'the directory for shared data'),
325
- PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
326
- 'the directory for man pages'),
327
- PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
328
- 'the directory for man pages'),
329
- PathItem.new('stdruby', 'path', stdruby,
330
- 'the directory for standard ruby libraries'),
331
- PathItem.new('siteruby', 'path', siteruby,
332
- 'the directory for version-independent aux ruby libraries'),
333
- PathItem.new('siterubyver', 'path', siterubyver,
334
- 'the directory for aux ruby libraries'),
335
- PathItem.new('siterubyverarch', 'path', siterubyverarch,
336
- 'the directory for aux ruby binaries'),
337
- PathItem.new('rbdir', 'path', '$siterubyver',
338
- 'the directory for ruby scripts'),
339
- PathItem.new('sodir', 'path', '$siterubyverarch',
340
- 'the directory for ruby extentions'),
341
- PathItem.new('rubypath', 'path', rubypath,
342
- 'the path to set to #! line'),
343
- ProgramItem.new('rubyprog', 'name', rubypath,
344
- 'the ruby program using for installation'),
345
- ProgramItem.new('makeprog', 'name', makeprog,
346
- 'the make program to compile ruby extentions'),
347
- SelectItem.new('shebang', 'all/ruby/never', 'ruby',
348
- 'shebang line (#!) editing mode'),
349
- BoolItem.new('without-ext', 'yes/no', 'no',
350
- 'does not compile/install ruby extentions')
351
- ]
352
- class ConfigTable_class # open again
353
276
  ALIASES = {
354
- 'std-ruby' => 'stdruby',
277
+ 'std-ruby' => 'librubyver',
278
+ 'stdruby' => 'librubyver',
279
+ 'rubylibdir' => 'librubyver',
280
+ 'archdir' => 'librubyverarch',
355
281
  'site-ruby-common' => 'siteruby', # For backward compatibility
356
282
  'site-ruby' => 'siterubyver', # For backward compatibility
357
283
  'bin-dir' => 'bindir',
@@ -365,78 +291,248 @@ class ConfigTable_class # open again
365
291
  'make-prog' => 'makeprog',
366
292
  'make' => 'makeprog'
367
293
  }
368
- end
369
- multipackage_conf = [
370
- PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
371
- 'package names that you want to install'),
372
- PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
373
- 'package names that you do not want to install')
374
- ]
375
- if multipackage_install?
376
- ConfigTable = ConfigTable_class.new(common_conf + multipackage_conf)
377
- else
378
- ConfigTable = ConfigTable_class.new(common_conf)
379
- end
380
294
 
381
-
382
- module MetaConfigAPI
383
-
384
- def eval_file_ifexist(fname)
385
- instance_eval File.read(fname), fname, 1 if File.file?(fname)
295
+ def fixup
296
+ ALIASES.each do |ali, name|
297
+ @table[ali] = @table[name]
298
+ end
299
+ @items.freeze
300
+ @table.freeze
301
+ @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
386
302
  end
387
303
 
388
- def config_names
389
- ConfigTable.map {|i| i.name }
304
+ def parse_opt(opt)
305
+ m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
306
+ m.to_a[1,2]
390
307
  end
391
308
 
392
- def config?(name)
393
- ConfigTable.key?(name)
309
+ def dllext
310
+ @rbconfig['DLEXT']
394
311
  end
395
312
 
396
- def bool_config?(name)
397
- ConfigTable.lookup(name).config_type == 'bool'
313
+ def value_config?(name)
314
+ lookup(name).value?
398
315
  end
399
316
 
400
- def path_config?(name)
401
- ConfigTable.lookup(name).config_type == 'path'
402
- end
317
+ class Item
318
+ def initialize(name, template, default, desc)
319
+ @name = name.freeze
320
+ @template = template
321
+ @value = default
322
+ @default = default
323
+ @description = desc
324
+ end
403
325
 
404
- def value_config?(name)
405
- case ConfigTable.lookup(name).config_type
406
- when 'bool', 'path'
326
+ attr_reader :name
327
+ attr_reader :description
328
+
329
+ attr_accessor :default
330
+ alias help_default default
331
+
332
+ def help_opt
333
+ "--#{@name}=#{@template}"
334
+ end
335
+
336
+ def value?
407
337
  true
408
- else
409
- false
338
+ end
339
+
340
+ def value
341
+ @value
342
+ end
343
+
344
+ def resolve(table)
345
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
346
+ end
347
+
348
+ def set(val)
349
+ @value = check(val)
350
+ end
351
+
352
+ private
353
+
354
+ def check(val)
355
+ setup_rb_error "config: --#{name} requires argument" unless val
356
+ val
410
357
  end
411
358
  end
412
359
 
413
- def add_config(item)
414
- ConfigTable.add item
360
+ class BoolItem < Item
361
+ def config_type
362
+ 'bool'
363
+ end
364
+
365
+ def help_opt
366
+ "--#{@name}"
367
+ end
368
+
369
+ private
370
+
371
+ def check(val)
372
+ return 'yes' unless val
373
+ case val
374
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
375
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
376
+ else
377
+ setup_rb_error "config: --#{@name} accepts only yes/no for argument"
378
+ end
379
+ end
415
380
  end
416
381
 
417
- def add_bool_config(name, default, desc)
418
- ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
382
+ class PathItem < Item
383
+ def config_type
384
+ 'path'
385
+ end
386
+
387
+ private
388
+
389
+ def check(path)
390
+ setup_rb_error "config: --#{@name} requires argument" unless path
391
+ path[0,1] == '$' ? path : File.expand_path(path)
392
+ end
419
393
  end
420
394
 
421
- def add_path_config(name, default, desc)
422
- ConfigTable.add PathItem.new(name, 'path', default, desc)
395
+ class ProgramItem < Item
396
+ def config_type
397
+ 'program'
398
+ end
423
399
  end
424
400
 
425
- def set_config_default(name, default)
426
- ConfigTable.lookup(name).default = default
401
+ class SelectItem < Item
402
+ def initialize(name, selection, default, desc)
403
+ super
404
+ @ok = selection.split('/')
405
+ end
406
+
407
+ def config_type
408
+ 'select'
409
+ end
410
+
411
+ private
412
+
413
+ def check(val)
414
+ unless @ok.include?(val.strip)
415
+ setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
416
+ end
417
+ val.strip
418
+ end
427
419
  end
428
420
 
429
- def remove_config(name)
430
- ConfigTable.remove(name)
421
+ class ExecItem < Item
422
+ def initialize(name, selection, desc, &block)
423
+ super name, selection, nil, desc
424
+ @ok = selection.split('/')
425
+ @action = block
426
+ end
427
+
428
+ def config_type
429
+ 'exec'
430
+ end
431
+
432
+ def value?
433
+ false
434
+ end
435
+
436
+ def resolve(table)
437
+ setup_rb_error "$#{name()} wrongly used as option value"
438
+ end
439
+
440
+ undef set
441
+
442
+ def evaluate(val, table)
443
+ v = val.strip.downcase
444
+ unless @ok.include?(v)
445
+ setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
446
+ end
447
+ @action.call v, table
448
+ end
431
449
  end
432
450
 
433
- end
451
+ class PackageSelectionItem < Item
452
+ def initialize(name, template, default, help_default, desc)
453
+ super name, template, default, desc
454
+ @help_default = help_default
455
+ end
434
456
 
457
+ attr_reader :help_default
435
458
 
436
- #
437
- # File Operations
438
- #
459
+ def config_type
460
+ 'package'
461
+ end
462
+
463
+ private
439
464
 
465
+ def check(val)
466
+ unless File.dir?("packages/#{val}")
467
+ setup_rb_error "config: no such package: #{val}"
468
+ end
469
+ val
470
+ end
471
+ end
472
+
473
+ class MetaConfigEnvironment
474
+ def initialize(config, installer)
475
+ @config = config
476
+ @installer = installer
477
+ end
478
+
479
+ def config_names
480
+ @config.names
481
+ end
482
+
483
+ def config?(name)
484
+ @config.key?(name)
485
+ end
486
+
487
+ def bool_config?(name)
488
+ @config.lookup(name).config_type == 'bool'
489
+ end
490
+
491
+ def path_config?(name)
492
+ @config.lookup(name).config_type == 'path'
493
+ end
494
+
495
+ def value_config?(name)
496
+ @config.lookup(name).config_type != 'exec'
497
+ end
498
+
499
+ def add_config(item)
500
+ @config.add item
501
+ end
502
+
503
+ def add_bool_config(name, default, desc)
504
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
505
+ end
506
+
507
+ def add_path_config(name, default, desc)
508
+ @config.add PathItem.new(name, 'path', default, desc)
509
+ end
510
+
511
+ def set_config_default(name, default)
512
+ @config.lookup(name).default = default
513
+ end
514
+
515
+ def remove_config(name)
516
+ @config.remove(name)
517
+ end
518
+
519
+ # For only multipackage
520
+ def packages
521
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
522
+ @installer.packages
523
+ end
524
+
525
+ # For only multipackage
526
+ def declare_packages(list)
527
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
528
+ @installer.packages = list
529
+ end
530
+ end
531
+
532
+ end # class ConfigTable
533
+
534
+
535
+ # This module requires: #verbose?, #no_harm?
440
536
  module FileOperations
441
537
 
442
538
  def mkdir_p(dirname, prefix = nil)
@@ -444,7 +540,7 @@ module FileOperations
444
540
  $stderr.puts "mkdir -p #{dirname}" if verbose?
445
541
  return if no_harm?
446
542
 
447
- # does not check '/'... it's too abnormal case
543
+ # Does not check '/', it's too abnormal.
448
544
  dirs = File.expand_path(dirname).split(%r<(?=/)>)
449
545
  if /\A[a-z]:\z/i =~ dirs[0]
450
546
  disk = dirs.shift
@@ -456,49 +552,73 @@ module FileOperations
456
552
  end
457
553
  end
458
554
 
459
- def rm_f(fname)
460
- $stderr.puts "rm -f #{fname}" if verbose?
555
+ def rm_f(path)
556
+ $stderr.puts "rm -f #{path}" if verbose?
461
557
  return if no_harm?
462
-
463
- if File.exist?(fname) or File.symlink?(fname)
464
- File.chmod 0777, fname
465
- File.unlink fname
466
- end
558
+ force_remove_file path
467
559
  end
468
560
 
469
- def rm_rf(dn)
470
- $stderr.puts "rm -rf #{dn}" if verbose?
561
+ def rm_rf(path)
562
+ $stderr.puts "rm -rf #{path}" if verbose?
471
563
  return if no_harm?
564
+ remove_tree path
565
+ end
566
+
567
+ def remove_tree(path)
568
+ if File.symlink?(path)
569
+ remove_file path
570
+ elsif File.dir?(path)
571
+ remove_tree0 path
572
+ else
573
+ force_remove_file path
574
+ end
575
+ end
472
576
 
473
- Dir.chdir dn
474
- Dir.foreach('.') do |fn|
475
- next if fn == '.'
476
- next if fn == '..'
477
- if File.dir?(fn)
478
- verbose_off {
479
- rm_rf fn
480
- }
577
+ def remove_tree0(path)
578
+ Dir.foreach(path) do |ent|
579
+ next if ent == '.'
580
+ next if ent == '..'
581
+ entpath = "#{path}/#{ent}"
582
+ if File.symlink?(entpath)
583
+ remove_file entpath
584
+ elsif File.dir?(entpath)
585
+ remove_tree0 entpath
481
586
  else
482
- verbose_off {
483
- rm_f fn
484
- }
587
+ force_remove_file entpath
485
588
  end
486
589
  end
487
- Dir.chdir '..'
488
- Dir.rmdir dn
590
+ begin
591
+ Dir.rmdir path
592
+ rescue Errno::ENOTEMPTY
593
+ # directory may not be empty
594
+ end
489
595
  end
490
596
 
491
597
  def move_file(src, dest)
492
- File.unlink dest if File.exist?(dest)
598
+ force_remove_file dest
493
599
  begin
494
600
  File.rename src, dest
495
601
  rescue
496
- File.open(dest, 'wb') {|f| f.write File.binread(src) }
602
+ File.open(dest, 'wb') {|f|
603
+ f.write File.binread(src)
604
+ }
497
605
  File.chmod File.stat(src).mode, dest
498
606
  File.unlink src
499
607
  end
500
608
  end
501
609
 
610
+ def force_remove_file(path)
611
+ begin
612
+ remove_file path
613
+ rescue
614
+ end
615
+ end
616
+
617
+ def remove_file(path)
618
+ File.chmod 0777, path
619
+ File.unlink path
620
+ end
621
+
502
622
  def install(from, dest, mode, prefix = nil)
503
623
  $stderr.puts "install #{from} #{dest}" if verbose?
504
624
  return if no_harm?
@@ -530,66 +650,42 @@ module FileOperations
530
650
  new_content != File.binread(path)
531
651
  end
532
652
 
533
- def command(str)
534
- $stderr.puts str if verbose?
535
- system str or raise RuntimeError, "'system #{str}' failed"
653
+ def command(*args)
654
+ $stderr.puts args.join(' ') if verbose?
655
+ system(*args) or raise RuntimeError,
656
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
536
657
  end
537
658
 
538
- def ruby(str)
539
- command config('rubyprog') + ' ' + str
659
+ def ruby(*args)
660
+ command config('rubyprog'), *args
540
661
  end
541
662
 
542
- def make(task = '')
543
- command config('makeprog') + ' ' + task
663
+ def make(task = nil)
664
+ command(*[config('makeprog'), task].compact)
544
665
  end
545
666
 
546
667
  def extdir?(dir)
547
- File.exist?(dir + '/MANIFEST')
668
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
548
669
  end
549
670
 
550
- def all_files_in(dirname)
551
- Dir.open(dirname) {|d|
552
- return d.select {|ent| File.file?("#{dirname}/#{ent}") }
671
+ def files_of(dir)
672
+ Dir.open(dir) {|d|
673
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
553
674
  }
554
675
  end
555
676
 
556
- REJECT_DIRS = %w(
557
- CVS SCCS RCS CVS.adm .svn
558
- )
677
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
559
678
 
560
- def all_dirs_in(dirname)
561
- Dir.open(dirname) {|d|
562
- return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
679
+ def directories_of(dir)
680
+ Dir.open(dir) {|d|
681
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
563
682
  }
564
683
  end
565
684
 
566
685
  end
567
686
 
568
687
 
569
- #
570
- # Main Installer
571
- #
572
-
573
- module HookUtils
574
-
575
- def run_hook(name)
576
- try_run_hook "#{curr_srcdir()}/#{name}" or
577
- try_run_hook "#{curr_srcdir()}/#{name}.rb"
578
- end
579
-
580
- def try_run_hook(fname)
581
- return false unless File.file?(fname)
582
- begin
583
- instance_eval File.read(fname), fname, 1
584
- rescue
585
- setup_rb_error "hook #{fname} failed:\n" + $!.message
586
- end
587
- true
588
- end
589
-
590
- end
591
-
592
-
688
+ # This module requires: #srcdir_root, #objdir_root, #relpath
593
689
  module HookScriptAPI
594
690
 
595
691
  def get_config(key)
@@ -598,6 +694,7 @@ module HookScriptAPI
598
694
 
599
695
  alias config get_config
600
696
 
697
+ # obsolete: use metaconfig to change configuration
601
698
  def set_config(key, val)
602
699
  @config[key] = val
603
700
  end
@@ -606,10 +703,6 @@ module HookScriptAPI
606
703
  # srcdir/objdir (works only in the package directory)
607
704
  #
608
705
 
609
- #abstract srcdir_root
610
- #abstract objdir_root
611
- #abstract relpath
612
-
613
706
  def curr_srcdir
614
707
  "#{srcdir_root()}/#{relpath()}"
615
708
  end
@@ -631,7 +724,7 @@ module HookScriptAPI
631
724
  end
632
725
 
633
726
  def srcfile?(path)
634
- File.file? srcfile(path)
727
+ File.file?(srcfile(path))
635
728
  end
636
729
 
637
730
  def srcentries(path = '.')
@@ -657,8 +750,8 @@ end
657
750
 
658
751
  class ToplevelInstaller
659
752
 
660
- Version = '3.3.1'
661
- Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
753
+ Version = '3.4.1'
754
+ Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
662
755
 
663
756
  TASKS = [
664
757
  [ 'all', 'do config, setup, then install' ],
@@ -666,27 +759,44 @@ class ToplevelInstaller
666
759
  [ 'show', 'shows current configuration' ],
667
760
  [ 'setup', 'compiles ruby extentions and others' ],
668
761
  [ 'install', 'installs files' ],
762
+ [ 'test', 'run all tests in test/' ],
669
763
  [ 'clean', "does `make clean' for each extention" ],
670
764
  [ 'distclean',"does `make distclean' for each extention" ]
671
765
  ]
672
766
 
673
767
  def ToplevelInstaller.invoke
674
- instance().invoke
768
+ config = ConfigTable.new(load_rbconfig())
769
+ config.load_standard_entries
770
+ config.load_multipackage_entries if multipackage?
771
+ config.fixup
772
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
773
+ klass.new(File.dirname($0), config).invoke
675
774
  end
676
775
 
677
- @singleton = nil
678
-
679
- def ToplevelInstaller.instance
680
- @singleton ||= new(File.dirname($0))
681
- @singleton
776
+ def ToplevelInstaller.multipackage?
777
+ File.dir?(File.dirname($0) + '/packages')
682
778
  end
683
779
 
684
- include MetaConfigAPI
780
+ def ToplevelInstaller.load_rbconfig
781
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
782
+ ARGV.delete(arg)
783
+ load File.expand_path(arg.split(/=/, 2)[1])
784
+ $".push 'rbconfig.rb'
785
+ else
786
+ require 'rbconfig'
787
+ end
788
+ ::Config::CONFIG
789
+ end
685
790
 
686
- def initialize(ardir_root)
687
- @config = nil
688
- @options = { 'verbose' => true }
791
+ def initialize(ardir_root, config)
689
792
  @ardir = File.expand_path(ardir_root)
793
+ @config = config
794
+ # cache
795
+ @valid_task_re = nil
796
+ end
797
+
798
+ def config(key)
799
+ @config[key]
690
800
  end
691
801
 
692
802
  def inspect
@@ -697,14 +807,20 @@ class ToplevelInstaller
697
807
  run_metaconfigs
698
808
  case task = parsearg_global()
699
809
  when nil, 'all'
700
- @config = load_config('config')
701
810
  parsearg_config
702
811
  init_installers
703
812
  exec_config
704
813
  exec_setup
705
814
  exec_install
706
815
  else
707
- @config = load_config(task)
816
+ case task
817
+ when 'config', 'test'
818
+ ;
819
+ when 'clean', 'distclean'
820
+ @config.load_savefile if File.exist?(@config.savefile)
821
+ else
822
+ @config.load_savefile
823
+ end
708
824
  __send__ "parsearg_#{task}"
709
825
  init_installers
710
826
  __send__ "exec_#{task}"
@@ -712,25 +828,11 @@ class ToplevelInstaller
712
828
  end
713
829
 
714
830
  def run_metaconfigs
715
- eval_file_ifexist "#{@ardir}/metaconfig"
716
- end
717
-
718
- def load_config(task)
719
- case task
720
- when 'config'
721
- ConfigTable.new
722
- when 'clean', 'distclean'
723
- if File.exist?(ConfigTable.savefile)
724
- then ConfigTable.load
725
- else ConfigTable.new
726
- end
727
- else
728
- ConfigTable.load
729
- end
831
+ @config.load_script "#{@ardir}/metaconfig"
730
832
  end
731
833
 
732
834
  def init_installers
733
- @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
835
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
734
836
  end
735
837
 
736
838
  #
@@ -754,78 +856,89 @@ class ToplevelInstaller
754
856
  #
755
857
 
756
858
  def parsearg_global
757
- valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
758
-
759
859
  while arg = ARGV.shift
760
860
  case arg
761
861
  when /\A\w+\z/
762
- setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
862
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
763
863
  return arg
764
-
765
864
  when '-q', '--quiet'
766
- @options['verbose'] = false
767
-
768
- when '--verbose'
769
- @options['verbose'] = true
770
-
771
- when '-h', '--help'
865
+ @config.verbose = false
866
+ when '--verbose'
867
+ @config.verbose = true
868
+ when '--help'
772
869
  print_usage $stdout
773
870
  exit 0
774
-
775
- when '-v', '--version'
871
+ when '--version'
776
872
  puts "#{File.basename($0)} version #{Version}"
777
873
  exit 0
778
-
779
874
  when '--copyright'
780
875
  puts Copyright
781
876
  exit 0
782
-
783
877
  else
784
878
  setup_rb_error "unknown global option '#{arg}'"
785
879
  end
786
880
  end
787
-
788
881
  nil
789
882
  end
790
883
 
884
+ def valid_task?(t)
885
+ valid_task_re() =~ t
886
+ end
887
+
888
+ def valid_task_re
889
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
890
+ end
791
891
 
792
892
  def parsearg_no_options
793
893
  unless ARGV.empty?
794
- setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
894
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
895
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
795
896
  end
796
897
  end
797
898
 
798
899
  alias parsearg_show parsearg_no_options
799
900
  alias parsearg_setup parsearg_no_options
901
+ alias parsearg_test parsearg_no_options
800
902
  alias parsearg_clean parsearg_no_options
801
903
  alias parsearg_distclean parsearg_no_options
802
904
 
803
905
  def parsearg_config
804
- re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
805
- @options['config-opt'] = []
806
-
906
+ evalopt = []
907
+ set = []
908
+ @config.config_opt = []
807
909
  while i = ARGV.shift
808
910
  if /\A--?\z/ =~ i
809
- @options['config-opt'] = ARGV.dup
911
+ @config.config_opt = ARGV.dup
810
912
  break
811
913
  end
812
- m = re.match(i) or setup_rb_error "config: unknown option #{i}"
813
- name, value = *m.to_a[1,2]
814
- @config[name] = value
914
+ name, value = *@config.parse_opt(i)
915
+ if @config.value_config?(name)
916
+ @config[name] = value
917
+ else
918
+ evalopt.push [name, value]
919
+ end
920
+ set.push name
921
+ end
922
+ evalopt.each do |name, value|
923
+ @config.lookup(name).evaluate value, @config
924
+ end
925
+ # Check if configuration is valid
926
+ set.each do |n|
927
+ @config[n] if @config.value_config?(n)
815
928
  end
816
929
  end
817
930
 
818
931
  def parsearg_install
819
- @options['no-harm'] = false
820
- @options['install-prefix'] = ''
932
+ @config.no_harm = false
933
+ @config.install_prefix = ''
821
934
  while a = ARGV.shift
822
935
  case a
823
- when /\A--no-harm\z/
824
- @options['no-harm'] = true
825
- when /\A--prefix=(.*)\z/
826
- path = $1
936
+ when '--no-harm'
937
+ @config.no_harm = true
938
+ when /\A--prefix=/
939
+ path = a.split(/=/, 2)[1]
827
940
  path = File.expand_path(path) unless path[0,1] == '/'
828
- @options['install-prefix'] = path
941
+ @config.install_prefix = path
829
942
  else
830
943
  setup_rb_error "install: unknown option #{a}"
831
944
  end
@@ -847,8 +960,8 @@ class ToplevelInstaller
847
960
  out.puts 'Global options:'
848
961
  out.printf fmt, '-q,--quiet', 'suppress message outputs'
849
962
  out.printf fmt, ' --verbose', 'output messages verbosely'
850
- out.printf fmt, '-h,--help', 'print this message'
851
- out.printf fmt, '-v,--version', 'print version and quit'
963
+ out.printf fmt, ' --help', 'print this message'
964
+ out.printf fmt, ' --version', 'print version and quit'
852
965
  out.printf fmt, ' --copyright', 'print copyright and quit'
853
966
  out.puts
854
967
  out.puts 'Tasks:'
@@ -859,14 +972,14 @@ class ToplevelInstaller
859
972
  fmt = " %-24s %s [%s]\n"
860
973
  out.puts
861
974
  out.puts 'Options for CONFIG or ALL:'
862
- ConfigTable.each do |item|
975
+ @config.each do |item|
863
976
  out.printf fmt, item.help_opt, item.description, item.help_default
864
977
  end
865
978
  out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
866
979
  out.puts
867
980
  out.puts 'Options for INSTALL:'
868
981
  out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
869
- out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
982
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
870
983
  out.puts
871
984
  end
872
985
 
@@ -887,9 +1000,13 @@ class ToplevelInstaller
887
1000
  @installer.exec_install
888
1001
  end
889
1002
 
1003
+ def exec_test
1004
+ @installer.exec_test
1005
+ end
1006
+
890
1007
  def exec_show
891
- ConfigTable.each do |i|
892
- printf "%-20s %s\n", i.name, i.value
1008
+ @config.each do |i|
1009
+ printf "%-20s %s\n", i.name, i.value if i.value?
893
1010
  end
894
1011
  end
895
1012
 
@@ -901,36 +1018,45 @@ class ToplevelInstaller
901
1018
  @installer.exec_distclean
902
1019
  end
903
1020
 
904
- end
1021
+ end # class ToplevelInstaller
905
1022
 
906
1023
 
907
1024
  class ToplevelInstallerMulti < ToplevelInstaller
908
1025
 
909
- include HookUtils
910
- include HookScriptAPI
911
1026
  include FileOperations
912
1027
 
913
- def initialize(ardir)
1028
+ def initialize(ardir_root, config)
914
1029
  super
915
- @packages = all_dirs_in("#{@ardir}/packages")
1030
+ @packages = directories_of("#{@ardir}/packages")
916
1031
  raise 'no package exists' if @packages.empty?
1032
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
917
1033
  end
918
1034
 
919
1035
  def run_metaconfigs
920
- eval_file_ifexist "#{@ardir}/metaconfig"
1036
+ @config.load_script "#{@ardir}/metaconfig", self
921
1037
  @packages.each do |name|
922
- eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
1038
+ @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
923
1039
  end
924
1040
  end
925
1041
 
1042
+ attr_reader :packages
1043
+
1044
+ def packages=(list)
1045
+ raise 'package list is empty' if list.empty?
1046
+ list.each do |name|
1047
+ raise "directory packages/#{name} does not exist"\
1048
+ unless File.dir?("#{@ardir}/packages/#{name}")
1049
+ end
1050
+ @packages = list
1051
+ end
1052
+
926
1053
  def init_installers
927
1054
  @installers = {}
928
1055
  @packages.each do |pack|
929
- @installers[pack] = Installer.new(@config, @options,
1056
+ @installers[pack] = Installer.new(@config,
930
1057
  "#{@ardir}/packages/#{pack}",
931
1058
  "packages/#{pack}")
932
1059
  end
933
-
934
1060
  with = extract_selection(config('with'))
935
1061
  without = extract_selection(config('without'))
936
1062
  @selected = @installers.keys.select {|name|
@@ -954,21 +1080,6 @@ class ToplevelInstallerMulti < ToplevelInstaller
954
1080
  f.puts
955
1081
  end
956
1082
 
957
- #
958
- # multi-package metaconfig API
959
- #
960
-
961
- attr_reader :packages
962
-
963
- def declare_packages(list)
964
- raise 'package list is empty' if list.empty?
965
- list.each do |name|
966
- raise "directory packages/#{name} does not exist"\
967
- unless File.dir?("#{@ardir}/packages/#{name}")
968
- end
969
- @packages = list
970
- end
971
-
972
1083
  #
973
1084
  # Task Handlers
974
1085
  #
@@ -992,15 +1103,21 @@ class ToplevelInstallerMulti < ToplevelInstaller
992
1103
  run_hook 'post-install'
993
1104
  end
994
1105
 
1106
+ def exec_test
1107
+ run_hook 'pre-test'
1108
+ each_selected_installers {|inst| inst.exec_test }
1109
+ run_hook 'post-test'
1110
+ end
1111
+
995
1112
  def exec_clean
996
- rm_f ConfigTable.savefile
1113
+ rm_f @config.savefile
997
1114
  run_hook 'pre-clean'
998
1115
  each_selected_installers {|inst| inst.exec_clean }
999
1116
  run_hook 'post-clean'
1000
1117
  end
1001
1118
 
1002
1119
  def exec_distclean
1003
- rm_f ConfigTable.savefile
1120
+ rm_f @config.savefile
1004
1121
  run_hook 'pre-distclean'
1005
1122
  each_selected_installers {|inst| inst.exec_distclean }
1006
1123
  run_hook 'post-distclean'
@@ -1013,7 +1130,7 @@ class ToplevelInstallerMulti < ToplevelInstaller
1013
1130
  def each_selected_installers
1014
1131
  Dir.mkdir 'packages' unless File.dir?('packages')
1015
1132
  @selected.each do |pack|
1016
- $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1133
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1017
1134
  Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1018
1135
  Dir.chdir "packages/#{pack}"
1019
1136
  yield @installers[pack]
@@ -1021,28 +1138,32 @@ class ToplevelInstallerMulti < ToplevelInstaller
1021
1138
  end
1022
1139
  end
1023
1140
 
1141
+ def run_hook(id)
1142
+ @root_installer.run_hook id
1143
+ end
1144
+
1145
+ # module FileOperations requires this
1024
1146
  def verbose?
1025
- @options['verbose']
1147
+ @config.verbose?
1026
1148
  end
1027
1149
 
1150
+ # module FileOperations requires this
1028
1151
  def no_harm?
1029
- @options['no-harm']
1152
+ @config.no_harm?
1030
1153
  end
1031
1154
 
1032
- end
1155
+ end # class ToplevelInstallerMulti
1033
1156
 
1034
1157
 
1035
1158
  class Installer
1036
1159
 
1037
- FILETYPES = %w( bin lib ext data )
1160
+ FILETYPES = %w( bin lib ext data conf man )
1038
1161
 
1039
- include HookScriptAPI
1040
- include HookUtils
1041
1162
  include FileOperations
1163
+ include HookScriptAPI
1042
1164
 
1043
- def initialize(config, opt, srcroot, objroot)
1165
+ def initialize(config, srcroot, objroot)
1044
1166
  @config = config
1045
- @options = opt
1046
1167
  @srcdir = File.expand_path(srcroot)
1047
1168
  @objdir = File.expand_path(objroot)
1048
1169
  @currdir = '.'
@@ -1052,6 +1173,9 @@ class Installer
1052
1173
  "#<#{self.class} #{File.basename(@srcdir)}>"
1053
1174
  end
1054
1175
 
1176
+ def noop(rel)
1177
+ end
1178
+
1055
1179
  #
1056
1180
  # Hook Script API base methods
1057
1181
  #
@@ -1069,23 +1193,25 @@ class Installer
1069
1193
  end
1070
1194
 
1071
1195
  #
1072
- # configs/options
1196
+ # Config Access
1073
1197
  #
1074
1198
 
1075
- def no_harm?
1076
- @options['no-harm']
1199
+ # module FileOperations requires this
1200
+ def verbose?
1201
+ @config.verbose?
1077
1202
  end
1078
1203
 
1079
- def verbose?
1080
- @options['verbose']
1204
+ # module FileOperations requires this
1205
+ def no_harm?
1206
+ @config.no_harm?
1081
1207
  end
1082
1208
 
1083
1209
  def verbose_off
1084
1210
  begin
1085
- save, @options['verbose'] = @options['verbose'], false
1211
+ save, @config.verbose = @config.verbose?, false
1086
1212
  yield
1087
1213
  ensure
1088
- @options['verbose'] = save
1214
+ @config.verbose = save
1089
1215
  end
1090
1216
  end
1091
1217
 
@@ -1097,22 +1223,19 @@ class Installer
1097
1223
  exec_task_traverse 'config'
1098
1224
  end
1099
1225
 
1100
- def config_dir_bin(rel)
1101
- end
1102
-
1103
- def config_dir_lib(rel)
1104
- end
1226
+ alias config_dir_bin noop
1227
+ alias config_dir_lib noop
1105
1228
 
1106
1229
  def config_dir_ext(rel)
1107
1230
  extconf if extdir?(curr_srcdir())
1108
1231
  end
1109
1232
 
1110
- def extconf
1111
- opt = @options['config-opt'].join(' ')
1112
- command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
1113
- end
1233
+ alias config_dir_data noop
1234
+ alias config_dir_conf noop
1235
+ alias config_dir_man noop
1114
1236
 
1115
- def config_dir_data(rel)
1237
+ def extconf
1238
+ ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1116
1239
  end
1117
1240
 
1118
1241
  #
@@ -1124,39 +1247,90 @@ class Installer
1124
1247
  end
1125
1248
 
1126
1249
  def setup_dir_bin(rel)
1127
- all_files_in(curr_srcdir()).each do |fname|
1128
- adjust_shebang "#{curr_srcdir()}/#{fname}"
1250
+ files_of(curr_srcdir()).each do |fname|
1251
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
1129
1252
  end
1130
1253
  end
1131
1254
 
1132
- def adjust_shebang(path)
1255
+ alias setup_dir_lib noop
1256
+
1257
+ def setup_dir_ext(rel)
1258
+ make if extdir?(curr_srcdir())
1259
+ end
1260
+
1261
+ alias setup_dir_data noop
1262
+ alias setup_dir_conf noop
1263
+ alias setup_dir_man noop
1264
+
1265
+ def update_shebang_line(path)
1133
1266
  return if no_harm?
1267
+ return if config('shebang') == 'never'
1268
+ old = Shebang.load(path)
1269
+ if old
1270
+ $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
1271
+ new = new_shebang(old)
1272
+ return if new.to_s == old.to_s
1273
+ else
1274
+ return unless config('shebang') == 'all'
1275
+ new = Shebang.new(config('rubypath'))
1276
+ end
1277
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
1278
+ open_atomic_writer(path) {|output|
1279
+ File.open(path, 'rb') {|f|
1280
+ f.gets if old # discard
1281
+ output.puts new.to_s
1282
+ output.print f.read
1283
+ }
1284
+ }
1285
+ end
1286
+
1287
+ def new_shebang(old)
1288
+ if /\Aruby/ =~ File.basename(old.cmd)
1289
+ Shebang.new(config('rubypath'), old.args)
1290
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
1291
+ Shebang.new(config('rubypath'), old.args[1..-1])
1292
+ else
1293
+ return old unless config('shebang') == 'all'
1294
+ Shebang.new(config('rubypath'))
1295
+ end
1296
+ end
1297
+
1298
+ def open_atomic_writer(path, &block)
1134
1299
  tmpfile = File.basename(path) + '.tmp'
1135
1300
  begin
1136
- File.open(path, 'rb') {|r|
1137
- first = r.gets
1138
- return unless File.basename(config('rubypath')) == 'ruby'
1139
- return unless File.basename(first.sub(/\A\#!/, '').split[0]) == 'ruby'
1140
- $stderr.puts "adjusting shebang: #{File.basename(path)}" if verbose?
1141
- File.open(tmpfile, 'wb') {|w|
1142
- w.print first.sub(/\A\#!\s*\S+/, '#! ' + config('rubypath'))
1143
- w.write r.read
1144
- }
1145
- move_file tmpfile, File.basename(path)
1146
- }
1301
+ File.open(tmpfile, 'wb', &block)
1302
+ File.rename tmpfile, File.basename(path)
1147
1303
  ensure
1148
1304
  File.unlink tmpfile if File.exist?(tmpfile)
1149
1305
  end
1150
1306
  end
1151
1307
 
1152
- def setup_dir_lib(rel)
1153
- end
1308
+ class Shebang
1309
+ def Shebang.load(path)
1310
+ line = nil
1311
+ File.open(path) {|f|
1312
+ line = f.gets
1313
+ }
1314
+ return nil unless /\A#!/ =~ line
1315
+ parse(line)
1316
+ end
1154
1317
 
1155
- def setup_dir_ext(rel)
1156
- make if extdir?(curr_srcdir())
1157
- end
1318
+ def Shebang.parse(line)
1319
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
1320
+ new(cmd, args)
1321
+ end
1322
+
1323
+ def initialize(cmd, args = [])
1324
+ @cmd = cmd
1325
+ @args = args
1326
+ end
1158
1327
 
1159
- def setup_dir_data(rel)
1328
+ attr_reader :cmd
1329
+ attr_reader :args
1330
+
1331
+ def to_s
1332
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1333
+ end
1160
1334
  end
1161
1335
 
1162
1336
  #
@@ -1169,63 +1343,77 @@ class Installer
1169
1343
  end
1170
1344
 
1171
1345
  def install_dir_bin(rel)
1172
- install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
1346
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1173
1347
  end
1174
1348
 
1175
1349
  def install_dir_lib(rel)
1176
- install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
1350
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1177
1351
  end
1178
1352
 
1179
1353
  def install_dir_ext(rel)
1180
1354
  return unless extdir?(curr_srcdir())
1181
- install_files ruby_extentions('.'),
1355
+ install_files rubyextentions('.'),
1182
1356
  "#{config('sodir')}/#{File.dirname(rel)}",
1183
1357
  0555
1184
1358
  end
1185
1359
 
1186
1360
  def install_dir_data(rel)
1187
- install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
1361
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
1362
+ end
1363
+
1364
+ def install_dir_conf(rel)
1365
+ # FIXME: should not remove current config files
1366
+ # (rename previous file to .old/.org)
1367
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
1368
+ end
1369
+
1370
+ def install_dir_man(rel)
1371
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
1188
1372
  end
1189
1373
 
1190
1374
  def install_files(list, dest, mode)
1191
- mkdir_p dest, @options['install-prefix']
1375
+ mkdir_p dest, @config.install_prefix
1192
1376
  list.each do |fname|
1193
- install fname, dest, mode, @options['install-prefix']
1377
+ install fname, dest, mode, @config.install_prefix
1194
1378
  end
1195
1379
  end
1196
1380
 
1197
- def ruby_scripts
1198
- collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1381
+ def libfiles
1382
+ glob_reject(%w(*.y *.output), targetfiles())
1199
1383
  end
1200
-
1384
+
1385
+ def rubyextentions(dir)
1386
+ ents = glob_select("*.#{@config.dllext}", targetfiles())
1387
+ if ents.empty?
1388
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1389
+ end
1390
+ ents
1391
+ end
1392
+
1393
+ def targetfiles
1394
+ mapdir(existfiles() - hookfiles())
1395
+ end
1396
+
1397
+ def mapdir(ents)
1398
+ ents.map {|ent|
1399
+ if File.exist?(ent)
1400
+ then ent # objdir
1401
+ else "#{curr_srcdir()}/#{ent}" # srcdir
1402
+ end
1403
+ }
1404
+ end
1405
+
1201
1406
  # picked up many entries from cvs-1.11.1/src/ignore.c
1202
- reject_patterns = %w(
1407
+ JUNK_FILES = %w(
1203
1408
  core RCSLOG tags TAGS .make.state
1204
1409
  .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1205
1410
  *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1206
1411
 
1207
1412
  *.org *.in .*
1208
1413
  )
1209
- mapping = {
1210
- '.' => '\.',
1211
- '$' => '\$',
1212
- '#' => '\#',
1213
- '*' => '.*'
1214
- }
1215
- REJECT_PATTERNS = Regexp.new('\A(?:' +
1216
- reject_patterns.map {|pat|
1217
- pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
1218
- }.join('|') +
1219
- ')\z')
1220
-
1221
- def collect_filenames_auto
1222
- mapdir((existfiles() - hookfiles()).reject {|fname|
1223
- REJECT_PATTERNS =~ fname
1224
- })
1225
- end
1226
1414
 
1227
1415
  def existfiles
1228
- all_files_in(curr_srcdir()) | all_files_in('.')
1416
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1229
1417
  end
1230
1418
 
1231
1419
  def hookfiles
@@ -1234,24 +1422,49 @@ class Installer
1234
1422
  }.flatten
1235
1423
  end
1236
1424
 
1237
- def mapdir(filelist)
1238
- filelist.map {|fname|
1239
- if File.exist?(fname) # objdir
1240
- fname
1241
- else # srcdir
1242
- File.join(curr_srcdir(), fname)
1243
- end
1244
- }
1425
+ def glob_select(pat, ents)
1426
+ re = globs2re([pat])
1427
+ ents.select {|ent| re =~ ent }
1245
1428
  end
1246
1429
 
1247
- def ruby_extentions(dir)
1248
- Dir.open(dir) {|d|
1249
- ents = d.select {|fname| /\.#{::Config::CONFIG['DLEXT']}\z/ =~ fname }
1250
- if ents.empty?
1251
- setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
1252
- end
1253
- return ents
1254
- }
1430
+ def glob_reject(pats, ents)
1431
+ re = globs2re(pats)
1432
+ ents.reject {|ent| re =~ ent }
1433
+ end
1434
+
1435
+ GLOB2REGEX = {
1436
+ '.' => '\.',
1437
+ '$' => '\$',
1438
+ '#' => '\#',
1439
+ '*' => '.*'
1440
+ }
1441
+
1442
+ def globs2re(pats)
1443
+ /\A(?:#{
1444
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
1445
+ })\z/
1446
+ end
1447
+
1448
+ #
1449
+ # TASK test
1450
+ #
1451
+
1452
+ TESTDIR = 'test'
1453
+
1454
+ def exec_test
1455
+ unless File.directory?('test')
1456
+ $stderr.puts 'no test in this package' if verbose?
1457
+ return
1458
+ end
1459
+ $stderr.puts 'Running tests...' if verbose?
1460
+ begin
1461
+ require 'test/unit'
1462
+ rescue LoadError
1463
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
1464
+ end
1465
+ runner = Test::Unit::AutoRunner.new(true)
1466
+ runner.to_run << TESTDIR
1467
+ runner.run
1255
1468
  end
1256
1469
 
1257
1470
  #
@@ -1260,53 +1473,51 @@ class Installer
1260
1473
 
1261
1474
  def exec_clean
1262
1475
  exec_task_traverse 'clean'
1263
- rm_f ConfigTable.savefile
1476
+ rm_f @config.savefile
1264
1477
  rm_f 'InstalledFiles'
1265
1478
  end
1266
1479
 
1267
- def clean_dir_bin(rel)
1268
- end
1269
-
1270
- def clean_dir_lib(rel)
1271
- end
1480
+ alias clean_dir_bin noop
1481
+ alias clean_dir_lib noop
1482
+ alias clean_dir_data noop
1483
+ alias clean_dir_conf noop
1484
+ alias clean_dir_man noop
1272
1485
 
1273
1486
  def clean_dir_ext(rel)
1274
1487
  return unless extdir?(curr_srcdir())
1275
1488
  make 'clean' if File.file?('Makefile')
1276
1489
  end
1277
1490
 
1278
- def clean_dir_data(rel)
1279
- end
1280
-
1281
1491
  #
1282
1492
  # TASK distclean
1283
1493
  #
1284
1494
 
1285
1495
  def exec_distclean
1286
1496
  exec_task_traverse 'distclean'
1287
- rm_f ConfigTable.savefile
1497
+ rm_f @config.savefile
1288
1498
  rm_f 'InstalledFiles'
1289
1499
  end
1290
1500
 
1291
- def distclean_dir_bin(rel)
1292
- end
1293
-
1294
- def distclean_dir_lib(rel)
1295
- end
1501
+ alias distclean_dir_bin noop
1502
+ alias distclean_dir_lib noop
1296
1503
 
1297
1504
  def distclean_dir_ext(rel)
1298
1505
  return unless extdir?(curr_srcdir())
1299
1506
  make 'distclean' if File.file?('Makefile')
1300
1507
  end
1301
1508
 
1509
+ alias distclean_dir_data noop
1510
+ alias distclean_dir_conf noop
1511
+ alias distclean_dir_man noop
1512
+
1302
1513
  #
1303
- # lib
1514
+ # Traversing
1304
1515
  #
1305
1516
 
1306
1517
  def exec_task_traverse(task)
1307
1518
  run_hook "pre-#{task}"
1308
1519
  FILETYPES.each do |type|
1309
- if config('without-ext') == 'yes' and type == 'ext'
1520
+ if type == 'ext' and config('without-ext') == 'yes'
1310
1521
  $stderr.puts 'skipping ext/* by user option' if verbose?
1311
1522
  next
1312
1523
  end
@@ -1319,7 +1530,7 @@ class Installer
1319
1530
  dive_into(rel) {
1320
1531
  run_hook "pre-#{task}"
1321
1532
  __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1322
- all_dirs_in(curr_srcdir()).each do |d|
1533
+ directories_of(curr_srcdir()).each do |d|
1323
1534
  traverse task, "#{rel}/#{d}", mid
1324
1535
  end
1325
1536
  run_hook "post-#{task}"
@@ -1341,16 +1552,30 @@ class Installer
1341
1552
  @currdir = File.dirname(rel)
1342
1553
  end
1343
1554
 
1344
- end
1555
+ def run_hook(id)
1556
+ path = [ "#{curr_srcdir()}/#{id}",
1557
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1558
+ return unless path
1559
+ begin
1560
+ instance_eval File.read(path), path, 1
1561
+ rescue
1562
+ raise if $DEBUG
1563
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1564
+ end
1565
+ end
1566
+
1567
+ end # class Installer
1568
+
1569
+
1570
+ class SetupError < StandardError; end
1345
1571
 
1572
+ def setup_rb_error(msg)
1573
+ raise SetupError, msg
1574
+ end
1346
1575
 
1347
1576
  if $0 == __FILE__
1348
1577
  begin
1349
- if multipackage_install?
1350
- ToplevelInstallerMulti.invoke
1351
- else
1352
- ToplevelInstaller.invoke
1353
- end
1578
+ ToplevelInstaller.invoke
1354
1579
  rescue SetupError
1355
1580
  raise if $DEBUG
1356
1581
  $stderr.puts $!.message