x10-cm17a 1.0.1-i386-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Modifications for dual windows/unix compatibility:
3
+ * Copyright 2004 by Jim Weirich (jim@weirichhouse.org)
4
+ * All rights reserved.
5
+ *
6
+ * See the MIT-LICENSE file included with the distribution for
7
+ * details on redistribution rights.
8
+ */
9
+
10
+ /*-
11
+ * Copyright (C) 1999, 2002 Matt Armstrong
12
+ * All rights reserved.
13
+ *
14
+ * Redistribution and use in source and binary forms, with or without
15
+ * modification, are permitted provided that the following conditions
16
+ * are met:
17
+ * 1. Redistributions of source code must retain the above copyright
18
+ * notice, this list of conditions and the following disclaimer.
19
+ * 2. Redistributions in binary form must reproduce the above copyright
20
+ * notice, this list of conditions and the following disclaimer in the
21
+ * documentation and/or other materials provided with the distribution.
22
+ *
23
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
+ * SUCH DAMAGE.
34
+ */
35
+
36
+ #ifndef CM17A_H
37
+ #define CM17A_H
38
+
39
+ #ifdef CM17A_WIN32
40
+ # include <windows.h>
41
+ # define X10_DEVICE HANDLE
42
+ # define INVALID_X10_DEVICE INVALID_HANDLE_VALUE
43
+ #else
44
+ # define X10_DEVICE int
45
+ # define INVALID_X10_DEVICE -1
46
+ #endif
47
+
48
+ enum CM17A_COMMAND {
49
+ CM17A_ON, CM17A_OFF, CM17A_BRIGHTEN, CM17A_DIM
50
+ };
51
+
52
+ X10_DEVICE cm17a_open_device(const char * device_name);
53
+
54
+ void cm17a_close_device(X10_DEVICE fd);
55
+
56
+ void cm17a_command(
57
+ X10_DEVICE fd,
58
+ int house,
59
+ int device,
60
+ enum CM17A_COMMAND,
61
+ int steps);
62
+
63
+ #endif
@@ -0,0 +1,130 @@
1
+ #include "ruby.h"
2
+ #include "cm17a.h"
3
+
4
+ #include <stdio.h>
5
+ #include <stddef.h>
6
+ #include <ctype.h>
7
+ #include <stdlib.h>
8
+ #include <assert.h>
9
+
10
+ #if HAVE_FCNTL_H
11
+ #include <fcntl.h>
12
+ #endif
13
+ #if HAVE_SYS_IOCTL_H
14
+ #include <sys/ioctl.h>
15
+ #endif
16
+ #if HAVE_SYS_TIME_H
17
+ #include <sys/time.h>
18
+ #endif
19
+ #if HAVE_SYS_TYPES_H
20
+ #include <sys/types.h>
21
+ #endif
22
+ #if HAVE_UNISTD_H
23
+ #include <unistd.h>
24
+ #endif
25
+ #if HAVE_TERMIO_H
26
+ #include <termio.h>
27
+ #endif
28
+
29
+ static X10_DEVICE fd;
30
+
31
+ static VALUE mX10;
32
+ static VALUE mCm17a;
33
+ static VALUE cCm17aController;
34
+ static VALUE eX10Error;
35
+ static VALUE symON;
36
+ static VALUE symOFF;
37
+ static VALUE symDIM;
38
+ static VALUE symBRIGHTEN;
39
+
40
+ /*
41
+ * call-seq:
42
+ * X10::Cm17a::Controller.new()
43
+ * X10::Cm17a::Controller.new(device_name)
44
+ *
45
+ * Create a new CM17A controller on the serial device named
46
+ * +device_name+. If no device name is given, then a default device
47
+ * will be selected (<tt>/dev/ttyS0</tt> for linux and <tt>COM1</tt>
48
+ * for windows).
49
+ */
50
+ static VALUE cm17a_init(int argc, VALUE *argv, VALUE self)
51
+ {
52
+ const char * device_name = DEFAULT_SERIAL_DEVICE;
53
+ if (argc > 0)
54
+ device_name = STR2CSTR(argv[0]);
55
+ fd = cm17a_open_device(device_name);
56
+ if (fd == INVALID_X10_DEVICE)
57
+ rb_raise(eX10Error, "Unable to open cm17a device '%s'", device_name);
58
+ return self;
59
+ }
60
+
61
+ /*
62
+ * call-seq:
63
+ * controller.command(house, unit, command_code, steps)
64
+ *
65
+ * Send a command to the CM17A controller. The X10 unit to get the
66
+ * address is given by the +house+ code (0-15) and the +unit+ code
67
+ * (0-15). The command must be one of the following constants:
68
+ *
69
+ * * <tt>:on</tt> -- Turn the device on.
70
+ * * <tt>:off</tt> -- Turn the device off.
71
+ * * <tt>:dim</tt> -- Dim the device by +steps+ steps.
72
+ * * <tt>:brighten</tt> -- Brighten the device by +steps+ steps.
73
+ *
74
+ * Note that the unit code is ignored for the <tt>:brighten</tt> and
75
+ * <tt>:dim</tt> commands. The bright/dim commands will effect the
76
+ * last addressed unit.
77
+ */
78
+ static VALUE cm17a_ruby_command(
79
+ VALUE self,
80
+ VALUE rhouse,
81
+ VALUE rdevice,
82
+ VALUE rcommand,
83
+ VALUE rsteps)
84
+ {
85
+ int house = NUM2INT(rhouse);
86
+ int device = NUM2INT(rdevice);
87
+ int steps = NUM2INT(rsteps);
88
+ int command;
89
+
90
+ if (rcommand == symON)
91
+ command = CM17A_ON;
92
+ else if (rcommand == symOFF)
93
+ command = CM17A_OFF;
94
+ else if (rcommand == symDIM)
95
+ command = CM17A_DIM;
96
+ else if (rcommand == symBRIGHTEN)
97
+ command = CM17A_BRIGHTEN;
98
+ else
99
+ command = NUM2INT(rcommand);
100
+
101
+ cm17a_command(fd, house, device, command, steps);
102
+ return Qnil;
103
+ }
104
+
105
+ /*
106
+ * Close the controller device.
107
+ */
108
+ static VALUE cm17a_close(VALUE self)
109
+ {
110
+ cm17a_close_device(fd);
111
+ return Qnil;
112
+ }
113
+
114
+ void Init_cm17a_api()
115
+ {
116
+ mX10 = rb_define_module("X10");
117
+ mCm17a = rb_define_module_under(mX10, "Cm17a");
118
+ cCm17aController = rb_define_class_under(mCm17a, "Controller", rb_cObject);
119
+
120
+ eX10Error = rb_eval_string("X10::X10Error");
121
+
122
+ symON = rb_eval_string(":on");
123
+ symOFF = rb_eval_string(":off");
124
+ symDIM = rb_eval_string(":dim");
125
+ symBRIGHTEN = rb_eval_string(":brighten");
126
+
127
+ rb_define_method(cCm17aController, "initialize", cm17a_init, -1);
128
+ rb_define_method(cCm17aController, "close", cm17a_close, 0);
129
+ rb_define_method(cCm17aController, "command", cm17a_ruby_command, 4);
130
+ }
@@ -0,0 +1,17 @@
1
+ require 'mkmf'
2
+ require 'rbconfig'
3
+
4
+ have_header("fcntl.h")
5
+ have_header("sys/ioctl.h")
6
+ have_header("sys/time.h")
7
+ have_header("sys/types.h")
8
+ have_header("unistd.h")
9
+ have_header("termio.h")
10
+
11
+ if Config::CONFIG['arch'] =~ /win32/
12
+ $defs.push "-DCM17A_WIN32"
13
+ $defs.push '-DDEFAULT_SERIAL_DEVICE=\\"COM1\\"'
14
+ else
15
+ $defs.push '-DDEFAULT_SERIAL_DEVICE=\\"/dev/ttyS0\\"'
16
+ end
17
+ create_makefile("cm17a_api")
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # = X10 Interface
4
+ #
5
+ # This is a really simple X10 framework for controlling X10 devices.
6
+ #
7
+ # == Example:
8
+ #
9
+ # require 'x10/cm17a'
10
+ # lamp = X10.device("a1")
11
+ # lamp.on
12
+ # sleep(1)
13
+ # lamp.off
14
+ #
15
+ # == Controllers and Devices
16
+ #
17
+ # An X10 controller is responsible for interfacing to the X10
18
+ # interface device. An example is the CM17A firecracker module.
19
+ #
20
+ # == Default Controllers
21
+ #
22
+ # blah blah blah.
23
+ #
24
+ class Class # :nodoc:
25
+ def x10_controller?
26
+ false
27
+ end
28
+ end
29
+
30
+ # The X10 Module. This module provides a root namespace for all X10
31
+ # devices and software. It also provides a few utility methods for
32
+ # use by the X10 controllers and devices.
33
+ module X10
34
+
35
+ # Error thrown for X10 specific failures.
36
+ class X10Error < RuntimeError; end
37
+
38
+ class << self
39
+ # Create an X10 device at the given address. THe address should
40
+ # be a string specifying the device address is standard X10
41
+ # nomenclature (e.g. a1 ... a16, b1 ... b16, ... p1 ... p16).
42
+ def device(address)
43
+ house, unit = parse_address(address)
44
+ controller.device(house, unit)
45
+ end
46
+
47
+ # Return the controller currently in use.
48
+ def controller
49
+ @controller ||= discover_single_controller
50
+ end
51
+
52
+ # Set the controller to be used to create X10 devices. If there
53
+ # is only one X10 controller loaded, then that controller will be
54
+ # used by default. Otherwise, the controller must be explicitly
55
+ # set using this method.
56
+ def controller=(controller)
57
+ @controller = controller
58
+ end
59
+
60
+ # Make a canonical X10 device address from the house number and
61
+ # unit. House and unit numbers are zero based.
62
+ def make_address(house, unit)
63
+ (house + ?a).chr + (unit+1).to_s
64
+ end
65
+
66
+ # Parse a canonical X10 device adderss into house number and unit
67
+ # number. House and unit numbers are zero based.
68
+ def parse_address(address)
69
+ address = address.downcase
70
+ if address !~ /^([a-p])(\d+)$/
71
+ fail X10::X10Error, "Bad X10 device address [#{address}]"
72
+ end
73
+ house_letter = $1
74
+ unit = $2.to_i - 1
75
+
76
+ if unit < 0 || unit > 15
77
+ fail X10::X10Error, "Bad X10 device address [#{address}]"
78
+ end
79
+
80
+ house = address[0] - ?a
81
+ [house, unit]
82
+ end
83
+
84
+ # If there is only one X10 controller class defined in the object
85
+ # space, then use it by default.
86
+ def discover_single_controller
87
+ controllers = []
88
+ ObjectSpace.each_object(Class) do |c|
89
+ controllers << c if c.x10_controller?
90
+ end
91
+ case controllers.size
92
+ when 0
93
+ fail X10::X10Error, "No X10 Controllers Found"
94
+ when 1
95
+ controllers.first.new
96
+ else
97
+ fail X10::X10Error, "Multiple X10 Controllers Found"
98
+ end
99
+ end
100
+ end
101
+
102
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'x10'
4
+ require 'cm17a_api'
5
+ require 'x10/cm17a_device'
6
+
7
+ module X10
8
+
9
+ module Cm17a
10
+
11
+ # The Controller object is the low level interface to the CM17A
12
+ # Firecracker controller. Client software generally uses the
13
+ # device level interface rather than the controller directly.
14
+ class Controller
15
+
16
+ # Create an X10::Cm17a::Device on this controller at the given
17
+ # X10 house and unit address.
18
+ def device(house, unit)
19
+ X10::Cm17a::Device.new(house, unit, self)
20
+ end
21
+
22
+ # Yes, this class represents a X10 controller.
23
+ def self.x10_controller?
24
+ true
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module X10
4
+
5
+ module Cm17a
6
+
7
+ # A CM17A device that responds to on/off and adjust (brightness)
8
+ # commands.
9
+ #
10
+ # Typical use:
11
+ #
12
+ # lamp = X10.device("a2") # Create the device
13
+ # lamp.on # Turn the lamp on
14
+ # lamp.adjust(-0.5) # Dim the lamp
15
+ # lamp.off # Turn the lamp off
16
+ #
17
+ class Device
18
+ attr_reader :address, :controller
19
+
20
+ # Create a new X10 device using a CM17A protocol controller.
21
+ # Such a device can handle on/off commands and simple dim
22
+ # controls.
23
+ #
24
+ # Normally device objects are created through the X10 framework
25
+ # and not directly by the client software.
26
+ def initialize(house, unit, controller)
27
+ @house = house
28
+ @unit = unit
29
+ @controller = controller
30
+ @address = X10.make_address(house, unit)
31
+ end
32
+
33
+ # Turn the device on.
34
+ def on
35
+ @controller.command(@house, @unit, :on, 0)
36
+ end
37
+
38
+ # Turn the device off.
39
+ def off
40
+ @controller.command(@house, @unit, :off, 0)
41
+ end
42
+
43
+ # Adjust the brightness level by +steps+. If +steps+ is
44
+ # positive, the brightness level is increased that many steps.
45
+ # If +steps+ is negative, the brightness level is decreased by
46
+ # <tt>|steps|</tt>.
47
+ #
48
+ # The controller limits the step size to 6, so larger steps must
49
+ # be broken up.
50
+ def step(steps)
51
+ direction = (steps > 0) ? :brighten : :dim
52
+ nsteps = steps.abs
53
+ while nsteps > 0
54
+ n = (nsteps > 6) ? 6 : nsteps
55
+ @controller.command(@house, @unit, direction, n)
56
+ nsteps -= n
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'drb'
4
+ require 'x10'
5
+ require 'x10/cm17a_device'
6
+
7
+ module X10
8
+ module Cm17aRemote
9
+ class Controller
10
+ def initialize(uri="druby:localhost:7777")
11
+ @uri = uri
12
+ @remote = DRbObject.new(nil, @uri)
13
+ end
14
+ def command(house, unit, command, steps)
15
+ @remote.command(house, unit, command, steps)
16
+ end
17
+ def device(house, unit)
18
+ X10::Cm17a::Device.new(house, unit, self)
19
+ end
20
+ def self.x10_controller?
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,1360 @@
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
+ unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12
+ module Enumerable
13
+ alias map collect
14
+ end
15
+ end
16
+
17
+ unless File.respond_to?(:read) # Ruby 1.6
18
+ def File.read(fname)
19
+ open(fname) {|f|
20
+ return f.read
21
+ }
22
+ end
23
+ end
24
+
25
+ def File.binread(fname)
26
+ open(fname, 'rb') {|f|
27
+ return f.read
28
+ }
29
+ end
30
+
31
+ # for corrupted windows stat(2)
32
+ def File.dir?(path)
33
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
34
+ end
35
+
36
+
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
107
+
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
118
+
119
+ class PathItem < ConfigItem
120
+ def config_type
121
+ 'path'
122
+ end
123
+
124
+ private
125
+
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
137
+
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'
146
+ end
147
+
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
157
+
158
+ class PackageSelectionItem < ConfigItem
159
+ def initialize(name, template, default, help_default, desc)
160
+ super name, template, default, desc
161
+ @help_default = help_default
162
+ end
163
+
164
+ attr_reader :help_default
165
+
166
+ def config_type
167
+ 'package'
168
+ end
169
+
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
177
+ end
178
+ end
179
+
180
+ class ConfigTable_class
181
+
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
191
+ end
192
+
193
+ include Enumerable
194
+
195
+ def each(&block)
196
+ @items.each(&block)
197
+ end
198
+
199
+ def key?(name)
200
+ @table.key?(name)
201
+ end
202
+
203
+ def lookup(name)
204
+ @table[name] or raise ArgumentError, "no such config item: #{name}"
205
+ end
206
+
207
+ def add(item)
208
+ @items.push item
209
+ @table[item.name] = item
210
+ end
211
+
212
+ def remove(name)
213
+ item = lookup(name)
214
+ @items.delete_if {|i| i.name == name }
215
+ @table.delete_if {|name, i| i.name == name }
216
+ item
217
+ end
218
+
219
+ def new
220
+ dup()
221
+ end
222
+
223
+ def savefile
224
+ '.config'
225
+ end
226
+
227
+ def load
228
+ begin
229
+ t = dup()
230
+ File.foreach(savefile()) do |line|
231
+ k, v = *line.split(/=/, 2)
232
+ t[k] = v.strip
233
+ end
234
+ t
235
+ rescue Errno::ENOENT
236
+ setup_rb_error $!.message + "#{File.basename($0)} config first"
237
+ end
238
+ end
239
+
240
+ def save
241
+ @items.each {|i| i.value }
242
+ File.open(savefile(), 'w') {|f|
243
+ @items.each do |i|
244
+ f.printf "%s=%s\n", i.name, i.value if i.value
245
+ end
246
+ }
247
+ end
248
+
249
+ def [](key)
250
+ lookup(key).eval(self)
251
+ end
252
+
253
+ def []=(key, val)
254
+ lookup(key).set val
255
+ end
256
+
257
+ end
258
+
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
315
+
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
+ ALIASES = {
354
+ 'std-ruby' => 'stdruby',
355
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
356
+ 'site-ruby' => 'siterubyver', # For backward compatibility
357
+ 'bin-dir' => 'bindir',
358
+ 'bin-dir' => 'bindir',
359
+ 'rb-dir' => 'rbdir',
360
+ 'so-dir' => 'sodir',
361
+ 'data-dir' => 'datadir',
362
+ 'ruby-path' => 'rubypath',
363
+ 'ruby-prog' => 'rubyprog',
364
+ 'ruby' => 'rubyprog',
365
+ 'make-prog' => 'makeprog',
366
+ 'make' => 'makeprog'
367
+ }
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
+
381
+
382
+ module MetaConfigAPI
383
+
384
+ def eval_file_ifexist(fname)
385
+ instance_eval File.read(fname), fname, 1 if File.file?(fname)
386
+ end
387
+
388
+ def config_names
389
+ ConfigTable.map {|i| i.name }
390
+ end
391
+
392
+ def config?(name)
393
+ ConfigTable.key?(name)
394
+ end
395
+
396
+ def bool_config?(name)
397
+ ConfigTable.lookup(name).config_type == 'bool'
398
+ end
399
+
400
+ def path_config?(name)
401
+ ConfigTable.lookup(name).config_type == 'path'
402
+ end
403
+
404
+ def value_config?(name)
405
+ case ConfigTable.lookup(name).config_type
406
+ when 'bool', 'path'
407
+ true
408
+ else
409
+ false
410
+ end
411
+ end
412
+
413
+ def add_config(item)
414
+ ConfigTable.add item
415
+ end
416
+
417
+ def add_bool_config(name, default, desc)
418
+ ConfigTable.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
419
+ end
420
+
421
+ def add_path_config(name, default, desc)
422
+ ConfigTable.add PathItem.new(name, 'path', default, desc)
423
+ end
424
+
425
+ def set_config_default(name, default)
426
+ ConfigTable.lookup(name).default = default
427
+ end
428
+
429
+ def remove_config(name)
430
+ ConfigTable.remove(name)
431
+ end
432
+
433
+ end
434
+
435
+
436
+ #
437
+ # File Operations
438
+ #
439
+
440
+ module FileOperations
441
+
442
+ def mkdir_p(dirname, prefix = nil)
443
+ dirname = prefix + File.expand_path(dirname) if prefix
444
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
445
+ return if no_harm?
446
+
447
+ # does not check '/'... it's too abnormal case
448
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
449
+ if /\A[a-z]:\z/i =~ dirs[0]
450
+ disk = dirs.shift
451
+ dirs[0] = disk + dirs[0]
452
+ end
453
+ dirs.each_index do |idx|
454
+ path = dirs[0..idx].join('')
455
+ Dir.mkdir path unless File.dir?(path)
456
+ end
457
+ end
458
+
459
+ def rm_f(fname)
460
+ $stderr.puts "rm -f #{fname}" if verbose?
461
+ 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
467
+ end
468
+
469
+ def rm_rf(dn)
470
+ $stderr.puts "rm -rf #{dn}" if verbose?
471
+ return if no_harm?
472
+
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
+ }
481
+ else
482
+ verbose_off {
483
+ rm_f fn
484
+ }
485
+ end
486
+ end
487
+ Dir.chdir '..'
488
+ Dir.rmdir dn
489
+ end
490
+
491
+ def move_file(src, dest)
492
+ File.unlink dest if File.exist?(dest)
493
+ begin
494
+ File.rename src, dest
495
+ rescue
496
+ File.open(dest, 'wb') {|f| f.write File.binread(src) }
497
+ File.chmod File.stat(src).mode, dest
498
+ File.unlink src
499
+ end
500
+ end
501
+
502
+ def install(from, dest, mode, prefix = nil)
503
+ $stderr.puts "install #{from} #{dest}" if verbose?
504
+ return if no_harm?
505
+
506
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
507
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
508
+ str = File.binread(from)
509
+ if diff?(str, realdest)
510
+ verbose_off {
511
+ rm_f realdest if File.exist?(realdest)
512
+ }
513
+ File.open(realdest, 'wb') {|f|
514
+ f.write str
515
+ }
516
+ File.chmod mode, realdest
517
+
518
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
519
+ if prefix
520
+ f.puts realdest.sub(prefix, '')
521
+ else
522
+ f.puts realdest
523
+ end
524
+ }
525
+ end
526
+ end
527
+
528
+ def diff?(new_content, path)
529
+ return true unless File.exist?(path)
530
+ new_content != File.binread(path)
531
+ end
532
+
533
+ def command(str)
534
+ $stderr.puts str if verbose?
535
+ system str or raise RuntimeError, "'system #{str}' failed"
536
+ end
537
+
538
+ def ruby(str)
539
+ command config('rubyprog') + ' ' + str
540
+ end
541
+
542
+ def make(task = '')
543
+ command config('makeprog') + ' ' + task
544
+ end
545
+
546
+ def extdir?(dir)
547
+ File.exist?(dir + '/MANIFEST')
548
+ end
549
+
550
+ def all_files_in(dirname)
551
+ Dir.open(dirname) {|d|
552
+ return d.select {|ent| File.file?("#{dirname}/#{ent}") }
553
+ }
554
+ end
555
+
556
+ REJECT_DIRS = %w(
557
+ CVS SCCS RCS CVS.adm .svn
558
+ )
559
+
560
+ def all_dirs_in(dirname)
561
+ Dir.open(dirname) {|d|
562
+ return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
563
+ }
564
+ end
565
+
566
+ end
567
+
568
+
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
+
593
+ module HookScriptAPI
594
+
595
+ def get_config(key)
596
+ @config[key]
597
+ end
598
+
599
+ alias config get_config
600
+
601
+ def set_config(key, val)
602
+ @config[key] = val
603
+ end
604
+
605
+ #
606
+ # srcdir/objdir (works only in the package directory)
607
+ #
608
+
609
+ #abstract srcdir_root
610
+ #abstract objdir_root
611
+ #abstract relpath
612
+
613
+ def curr_srcdir
614
+ "#{srcdir_root()}/#{relpath()}"
615
+ end
616
+
617
+ def curr_objdir
618
+ "#{objdir_root()}/#{relpath()}"
619
+ end
620
+
621
+ def srcfile(path)
622
+ "#{curr_srcdir()}/#{path}"
623
+ end
624
+
625
+ def srcexist?(path)
626
+ File.exist?(srcfile(path))
627
+ end
628
+
629
+ def srcdirectory?(path)
630
+ File.dir?(srcfile(path))
631
+ end
632
+
633
+ def srcfile?(path)
634
+ File.file? srcfile(path)
635
+ end
636
+
637
+ def srcentries(path = '.')
638
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
639
+ return d.to_a - %w(. ..)
640
+ }
641
+ end
642
+
643
+ def srcfiles(path = '.')
644
+ srcentries(path).select {|fname|
645
+ File.file?(File.join(curr_srcdir(), path, fname))
646
+ }
647
+ end
648
+
649
+ def srcdirectories(path = '.')
650
+ srcentries(path).select {|fname|
651
+ File.dir?(File.join(curr_srcdir(), path, fname))
652
+ }
653
+ end
654
+
655
+ end
656
+
657
+
658
+ class ToplevelInstaller
659
+
660
+ Version = '3.3.1'
661
+ Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
662
+
663
+ TASKS = [
664
+ [ 'all', 'do config, setup, then install' ],
665
+ [ 'config', 'saves your configurations' ],
666
+ [ 'show', 'shows current configuration' ],
667
+ [ 'setup', 'compiles ruby extentions and others' ],
668
+ [ 'install', 'installs files' ],
669
+ [ 'clean', "does `make clean' for each extention" ],
670
+ [ 'distclean',"does `make distclean' for each extention" ]
671
+ ]
672
+
673
+ def ToplevelInstaller.invoke
674
+ instance().invoke
675
+ end
676
+
677
+ @singleton = nil
678
+
679
+ def ToplevelInstaller.instance
680
+ @singleton ||= new(File.dirname($0))
681
+ @singleton
682
+ end
683
+
684
+ include MetaConfigAPI
685
+
686
+ def initialize(ardir_root)
687
+ @config = nil
688
+ @options = { 'verbose' => true }
689
+ @ardir = File.expand_path(ardir_root)
690
+ end
691
+
692
+ def inspect
693
+ "#<#{self.class} #{__id__()}>"
694
+ end
695
+
696
+ def invoke
697
+ run_metaconfigs
698
+ case task = parsearg_global()
699
+ when nil, 'all'
700
+ @config = load_config('config')
701
+ parsearg_config
702
+ init_installers
703
+ exec_config
704
+ exec_setup
705
+ exec_install
706
+ else
707
+ @config = load_config(task)
708
+ __send__ "parsearg_#{task}"
709
+ init_installers
710
+ __send__ "exec_#{task}"
711
+ end
712
+ end
713
+
714
+ 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
730
+ end
731
+
732
+ def init_installers
733
+ @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
734
+ end
735
+
736
+ #
737
+ # Hook Script API bases
738
+ #
739
+
740
+ def srcdir_root
741
+ @ardir
742
+ end
743
+
744
+ def objdir_root
745
+ '.'
746
+ end
747
+
748
+ def relpath
749
+ '.'
750
+ end
751
+
752
+ #
753
+ # Option Parsing
754
+ #
755
+
756
+ def parsearg_global
757
+ valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
758
+
759
+ while arg = ARGV.shift
760
+ case arg
761
+ when /\A\w+\z/
762
+ setup_rb_error "invalid task: #{arg}" unless valid_task =~ arg
763
+ return arg
764
+
765
+ when '-q', '--quiet'
766
+ @options['verbose'] = false
767
+
768
+ when '--verbose'
769
+ @options['verbose'] = true
770
+
771
+ when '-h', '--help'
772
+ print_usage $stdout
773
+ exit 0
774
+
775
+ when '-v', '--version'
776
+ puts "#{File.basename($0)} version #{Version}"
777
+ exit 0
778
+
779
+ when '--copyright'
780
+ puts Copyright
781
+ exit 0
782
+
783
+ else
784
+ setup_rb_error "unknown global option '#{arg}'"
785
+ end
786
+ end
787
+
788
+ nil
789
+ end
790
+
791
+
792
+ def parsearg_no_options
793
+ unless ARGV.empty?
794
+ setup_rb_error "#{task}: unknown options: #{ARGV.join ' '}"
795
+ end
796
+ end
797
+
798
+ alias parsearg_show parsearg_no_options
799
+ alias parsearg_setup parsearg_no_options
800
+ alias parsearg_clean parsearg_no_options
801
+ alias parsearg_distclean parsearg_no_options
802
+
803
+ def parsearg_config
804
+ re = /\A--(#{ConfigTable.map {|i| i.name }.join('|')})(?:=(.*))?\z/
805
+ @options['config-opt'] = []
806
+
807
+ while i = ARGV.shift
808
+ if /\A--?\z/ =~ i
809
+ @options['config-opt'] = ARGV.dup
810
+ break
811
+ 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
815
+ end
816
+ end
817
+
818
+ def parsearg_install
819
+ @options['no-harm'] = false
820
+ @options['install-prefix'] = ''
821
+ while a = ARGV.shift
822
+ case a
823
+ when /\A--no-harm\z/
824
+ @options['no-harm'] = true
825
+ when /\A--prefix=(.*)\z/
826
+ path = $1
827
+ path = File.expand_path(path) unless path[0,1] == '/'
828
+ @options['install-prefix'] = path
829
+ else
830
+ setup_rb_error "install: unknown option #{a}"
831
+ end
832
+ end
833
+ end
834
+
835
+ def print_usage(out)
836
+ out.puts 'Typical Installation Procedure:'
837
+ out.puts " $ ruby #{File.basename $0} config"
838
+ out.puts " $ ruby #{File.basename $0} setup"
839
+ out.puts " # ruby #{File.basename $0} install (may require root privilege)"
840
+ out.puts
841
+ out.puts 'Detailed Usage:'
842
+ out.puts " ruby #{File.basename $0} <global option>"
843
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
844
+
845
+ fmt = " %-24s %s\n"
846
+ out.puts
847
+ out.puts 'Global options:'
848
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
849
+ 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'
852
+ out.printf fmt, ' --copyright', 'print copyright and quit'
853
+ out.puts
854
+ out.puts 'Tasks:'
855
+ TASKS.each do |name, desc|
856
+ out.printf fmt, name, desc
857
+ end
858
+
859
+ fmt = " %-24s %s [%s]\n"
860
+ out.puts
861
+ out.puts 'Options for CONFIG or ALL:'
862
+ ConfigTable.each do |item|
863
+ out.printf fmt, item.help_opt, item.description, item.help_default
864
+ end
865
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
866
+ out.puts
867
+ out.puts 'Options for INSTALL:'
868
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
869
+ out.printf fmt, '--prefix=path', 'install path prefix', '$prefix'
870
+ out.puts
871
+ end
872
+
873
+ #
874
+ # Task Handlers
875
+ #
876
+
877
+ def exec_config
878
+ @installer.exec_config
879
+ @config.save # must be final
880
+ end
881
+
882
+ def exec_setup
883
+ @installer.exec_setup
884
+ end
885
+
886
+ def exec_install
887
+ @installer.exec_install
888
+ end
889
+
890
+ def exec_show
891
+ ConfigTable.each do |i|
892
+ printf "%-20s %s\n", i.name, i.value
893
+ end
894
+ end
895
+
896
+ def exec_clean
897
+ @installer.exec_clean
898
+ end
899
+
900
+ def exec_distclean
901
+ @installer.exec_distclean
902
+ end
903
+
904
+ end
905
+
906
+
907
+ class ToplevelInstallerMulti < ToplevelInstaller
908
+
909
+ include HookUtils
910
+ include HookScriptAPI
911
+ include FileOperations
912
+
913
+ def initialize(ardir)
914
+ super
915
+ @packages = all_dirs_in("#{@ardir}/packages")
916
+ raise 'no package exists' if @packages.empty?
917
+ end
918
+
919
+ def run_metaconfigs
920
+ eval_file_ifexist "#{@ardir}/metaconfig"
921
+ @packages.each do |name|
922
+ eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
923
+ end
924
+ end
925
+
926
+ def init_installers
927
+ @installers = {}
928
+ @packages.each do |pack|
929
+ @installers[pack] = Installer.new(@config, @options,
930
+ "#{@ardir}/packages/#{pack}",
931
+ "packages/#{pack}")
932
+ end
933
+
934
+ with = extract_selection(config('with'))
935
+ without = extract_selection(config('without'))
936
+ @selected = @installers.keys.select {|name|
937
+ (with.empty? or with.include?(name)) \
938
+ and not without.include?(name)
939
+ }
940
+ end
941
+
942
+ def extract_selection(list)
943
+ a = list.split(/,/)
944
+ a.each do |name|
945
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
946
+ end
947
+ a
948
+ end
949
+
950
+ def print_usage(f)
951
+ super
952
+ f.puts 'Inluded packages:'
953
+ f.puts ' ' + @packages.sort.join(' ')
954
+ f.puts
955
+ end
956
+
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
+ #
973
+ # Task Handlers
974
+ #
975
+
976
+ def exec_config
977
+ run_hook 'pre-config'
978
+ each_selected_installers {|inst| inst.exec_config }
979
+ run_hook 'post-config'
980
+ @config.save # must be final
981
+ end
982
+
983
+ def exec_setup
984
+ run_hook 'pre-setup'
985
+ each_selected_installers {|inst| inst.exec_setup }
986
+ run_hook 'post-setup'
987
+ end
988
+
989
+ def exec_install
990
+ run_hook 'pre-install'
991
+ each_selected_installers {|inst| inst.exec_install }
992
+ run_hook 'post-install'
993
+ end
994
+
995
+ def exec_clean
996
+ rm_f ConfigTable.savefile
997
+ run_hook 'pre-clean'
998
+ each_selected_installers {|inst| inst.exec_clean }
999
+ run_hook 'post-clean'
1000
+ end
1001
+
1002
+ def exec_distclean
1003
+ rm_f ConfigTable.savefile
1004
+ run_hook 'pre-distclean'
1005
+ each_selected_installers {|inst| inst.exec_distclean }
1006
+ run_hook 'post-distclean'
1007
+ end
1008
+
1009
+ #
1010
+ # lib
1011
+ #
1012
+
1013
+ def each_selected_installers
1014
+ Dir.mkdir 'packages' unless File.dir?('packages')
1015
+ @selected.each do |pack|
1016
+ $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
1017
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1018
+ Dir.chdir "packages/#{pack}"
1019
+ yield @installers[pack]
1020
+ Dir.chdir '../..'
1021
+ end
1022
+ end
1023
+
1024
+ def verbose?
1025
+ @options['verbose']
1026
+ end
1027
+
1028
+ def no_harm?
1029
+ @options['no-harm']
1030
+ end
1031
+
1032
+ end
1033
+
1034
+
1035
+ class Installer
1036
+
1037
+ FILETYPES = %w( bin lib ext data )
1038
+
1039
+ include HookScriptAPI
1040
+ include HookUtils
1041
+ include FileOperations
1042
+
1043
+ def initialize(config, opt, srcroot, objroot)
1044
+ @config = config
1045
+ @options = opt
1046
+ @srcdir = File.expand_path(srcroot)
1047
+ @objdir = File.expand_path(objroot)
1048
+ @currdir = '.'
1049
+ end
1050
+
1051
+ def inspect
1052
+ "#<#{self.class} #{File.basename(@srcdir)}>"
1053
+ end
1054
+
1055
+ #
1056
+ # Hook Script API base methods
1057
+ #
1058
+
1059
+ def srcdir_root
1060
+ @srcdir
1061
+ end
1062
+
1063
+ def objdir_root
1064
+ @objdir
1065
+ end
1066
+
1067
+ def relpath
1068
+ @currdir
1069
+ end
1070
+
1071
+ #
1072
+ # configs/options
1073
+ #
1074
+
1075
+ def no_harm?
1076
+ @options['no-harm']
1077
+ end
1078
+
1079
+ def verbose?
1080
+ @options['verbose']
1081
+ end
1082
+
1083
+ def verbose_off
1084
+ begin
1085
+ save, @options['verbose'] = @options['verbose'], false
1086
+ yield
1087
+ ensure
1088
+ @options['verbose'] = save
1089
+ end
1090
+ end
1091
+
1092
+ #
1093
+ # TASK config
1094
+ #
1095
+
1096
+ def exec_config
1097
+ exec_task_traverse 'config'
1098
+ end
1099
+
1100
+ def config_dir_bin(rel)
1101
+ end
1102
+
1103
+ def config_dir_lib(rel)
1104
+ end
1105
+
1106
+ def config_dir_ext(rel)
1107
+ extconf if extdir?(curr_srcdir())
1108
+ end
1109
+
1110
+ def extconf
1111
+ opt = @options['config-opt'].join(' ')
1112
+ command "#{config('rubyprog')} #{curr_srcdir()}/extconf.rb #{opt}"
1113
+ end
1114
+
1115
+ def config_dir_data(rel)
1116
+ end
1117
+
1118
+ #
1119
+ # TASK setup
1120
+ #
1121
+
1122
+ def exec_setup
1123
+ exec_task_traverse 'setup'
1124
+ end
1125
+
1126
+ def setup_dir_bin(rel)
1127
+ all_files_in(curr_srcdir()).each do |fname|
1128
+ adjust_shebang "#{curr_srcdir()}/#{fname}"
1129
+ end
1130
+ end
1131
+
1132
+ def adjust_shebang(path)
1133
+ return if no_harm?
1134
+ tmpfile = File.basename(path) + '.tmp'
1135
+ 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
+ }
1147
+ ensure
1148
+ File.unlink tmpfile if File.exist?(tmpfile)
1149
+ end
1150
+ end
1151
+
1152
+ def setup_dir_lib(rel)
1153
+ end
1154
+
1155
+ def setup_dir_ext(rel)
1156
+ make if extdir?(curr_srcdir())
1157
+ end
1158
+
1159
+ def setup_dir_data(rel)
1160
+ end
1161
+
1162
+ #
1163
+ # TASK install
1164
+ #
1165
+
1166
+ def exec_install
1167
+ rm_f 'InstalledFiles'
1168
+ exec_task_traverse 'install'
1169
+ end
1170
+
1171
+ def install_dir_bin(rel)
1172
+ install_files collect_filenames_auto(), "#{config('bindir')}/#{rel}", 0755
1173
+ end
1174
+
1175
+ def install_dir_lib(rel)
1176
+ install_files ruby_scripts(), "#{config('rbdir')}/#{rel}", 0644
1177
+ end
1178
+
1179
+ def install_dir_ext(rel)
1180
+ return unless extdir?(curr_srcdir())
1181
+ install_files ruby_extentions('.'),
1182
+ "#{config('sodir')}/#{File.dirname(rel)}",
1183
+ 0555
1184
+ end
1185
+
1186
+ def install_dir_data(rel)
1187
+ install_files collect_filenames_auto(), "#{config('datadir')}/#{rel}", 0644
1188
+ end
1189
+
1190
+ def install_files(list, dest, mode)
1191
+ mkdir_p dest, @options['install-prefix']
1192
+ list.each do |fname|
1193
+ install fname, dest, mode, @options['install-prefix']
1194
+ end
1195
+ end
1196
+
1197
+ def ruby_scripts
1198
+ collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
1199
+ end
1200
+
1201
+ # picked up many entries from cvs-1.11.1/src/ignore.c
1202
+ reject_patterns = %w(
1203
+ core RCSLOG tags TAGS .make.state
1204
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1205
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1206
+
1207
+ *.org *.in .*
1208
+ )
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
+
1227
+ def existfiles
1228
+ all_files_in(curr_srcdir()) | all_files_in('.')
1229
+ end
1230
+
1231
+ def hookfiles
1232
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1233
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1234
+ }.flatten
1235
+ end
1236
+
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
+ }
1245
+ end
1246
+
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
+ }
1255
+ end
1256
+
1257
+ #
1258
+ # TASK clean
1259
+ #
1260
+
1261
+ def exec_clean
1262
+ exec_task_traverse 'clean'
1263
+ rm_f ConfigTable.savefile
1264
+ rm_f 'InstalledFiles'
1265
+ end
1266
+
1267
+ def clean_dir_bin(rel)
1268
+ end
1269
+
1270
+ def clean_dir_lib(rel)
1271
+ end
1272
+
1273
+ def clean_dir_ext(rel)
1274
+ return unless extdir?(curr_srcdir())
1275
+ make 'clean' if File.file?('Makefile')
1276
+ end
1277
+
1278
+ def clean_dir_data(rel)
1279
+ end
1280
+
1281
+ #
1282
+ # TASK distclean
1283
+ #
1284
+
1285
+ def exec_distclean
1286
+ exec_task_traverse 'distclean'
1287
+ rm_f ConfigTable.savefile
1288
+ rm_f 'InstalledFiles'
1289
+ end
1290
+
1291
+ def distclean_dir_bin(rel)
1292
+ end
1293
+
1294
+ def distclean_dir_lib(rel)
1295
+ end
1296
+
1297
+ def distclean_dir_ext(rel)
1298
+ return unless extdir?(curr_srcdir())
1299
+ make 'distclean' if File.file?('Makefile')
1300
+ end
1301
+
1302
+ #
1303
+ # lib
1304
+ #
1305
+
1306
+ def exec_task_traverse(task)
1307
+ run_hook "pre-#{task}"
1308
+ FILETYPES.each do |type|
1309
+ if config('without-ext') == 'yes' and type == 'ext'
1310
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1311
+ next
1312
+ end
1313
+ traverse task, type, "#{task}_dir_#{type}"
1314
+ end
1315
+ run_hook "post-#{task}"
1316
+ end
1317
+
1318
+ def traverse(task, rel, mid)
1319
+ dive_into(rel) {
1320
+ run_hook "pre-#{task}"
1321
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1322
+ all_dirs_in(curr_srcdir()).each do |d|
1323
+ traverse task, "#{rel}/#{d}", mid
1324
+ end
1325
+ run_hook "post-#{task}"
1326
+ }
1327
+ end
1328
+
1329
+ def dive_into(rel)
1330
+ return unless File.dir?("#{@srcdir}/#{rel}")
1331
+
1332
+ dir = File.basename(rel)
1333
+ Dir.mkdir dir unless File.dir?(dir)
1334
+ prevdir = Dir.pwd
1335
+ Dir.chdir dir
1336
+ $stderr.puts '---> ' + rel if verbose?
1337
+ @currdir = rel
1338
+ yield
1339
+ Dir.chdir prevdir
1340
+ $stderr.puts '<--- ' + rel if verbose?
1341
+ @currdir = File.dirname(rel)
1342
+ end
1343
+
1344
+ end
1345
+
1346
+
1347
+ if $0 == __FILE__
1348
+ begin
1349
+ if multipackage_install?
1350
+ ToplevelInstallerMulti.invoke
1351
+ else
1352
+ ToplevelInstaller.invoke
1353
+ end
1354
+ rescue SetupError
1355
+ raise if $DEBUG
1356
+ $stderr.puts $!.message
1357
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1358
+ exit 1
1359
+ end
1360
+ end