float-formats 0.2.0 → 0.2.1

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.
data/License.txt DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2007 Javier Goizueta
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt DELETED
@@ -1,20 +0,0 @@
1
- History.txt
2
- License.txt
3
- Manifest.txt
4
- README.txt
5
- Rakefile
6
- lib/float-formats.rb
7
- lib/float-formats/version.rb
8
- lib/float-formats/bytes.rb
9
- lib/float-formats/classes.rb
10
- lib/float-formats/formats.rb
11
- lib/float-formats/native.rb
12
- setup.rb
13
- test/test_float_formats.rb
14
- test/test_helper.rb
15
- test/test_data.yaml
16
- test/gen_test_data.rb
17
- test/test_native-float.rb
18
- test/test_arithmetic.rb
19
- test/test_bytes.rb
20
- test/gen_test_data.rb
data/README.txt DELETED
@@ -1,330 +0,0 @@
1
- =Introduction
2
-
3
- Float-Formats is a Ruby package with methods to handle diverse floating-point formats.
4
- These are some of the things that can be done with it:
5
-
6
- * Enconding and decoding numerical values in specific floating point representations.
7
- * Conversion of floating-point data between different formats.
8
- * Obtaining properties of floating-point formats (ranges, precision, etc.)
9
- * Exploring and learning about floating point representations.
10
- * Definition and testing of new floating-point formats.
11
-
12
- =Installation
13
-
14
- The easiest way to install Nio is using gems:
15
-
16
- <tt> gem install --remote float-formats -y</tt>
17
-
18
- ==Requirements
19
-
20
- Nio[http://nio.rubyforge.org/] 0.2.4 or later
21
- and Flt[http://flt.rubyforge.org/] 1.0.0 or later are needed. These
22
- can be installed as gems and should be automatically
23
- installed by the command shown above to install float-formats.
24
-
25
- ==Downloads
26
-
27
- The latest version of Float-Formats and its source code can be downloaded from
28
- * http://rubyforge.org/project/showfiles.php?group_id=4684
29
-
30
- You can find the code also in GitHub:
31
- * http://github.com/jgoizueta/float-formats/
32
-
33
- =Predefined formats
34
-
35
- A number of common formats are defined as constants in the Flt module:
36
-
37
- ==IEEE 754-2008
38
- <b>binary</b> floating point representations in little endian order:
39
- IEEE_binary16 (half precision),
40
- IEEE_binary32 (single precision),
41
- IEEE_binary64 (double precision),
42
- IEEE_binary80 (extended), IEEE_binary128 (quadruple precision) and
43
- as little endian: IEEE_binary16_BE, etc.
44
-
45
- <b>decimal</b> formats (using DPD):
46
- IEEE_decimal32, IEEE_decimal64 and IEEE_decimal128.
47
-
48
- <b>interchange binary & decimal</b> formats:
49
- IEEE_binary256, IEEE_binary512, IEEE_binary1024, IEEE_decimal192, IEEE_decimal256.
50
- Others can be defined with IEEE.interchange_binary and IEEE.interchange_decimal
51
- (see the IEEE module).
52
-
53
- ==Legacy
54
- Formats of historical interest, some of which are found
55
- in file formats still in use.
56
-
57
- <b>Mainframe/supercomputer</b> formats:
58
- Univac 1100 (UNIVAC_SINGLE, UNIVAC_DOUBLE),
59
- IBM 360 etc. (IBM32, IBM64 and IBM128),
60
- CDC 6600/7600: (CDC_SINGLE, CDC_DOUBLE),
61
- Cray-1: (CRAY).
62
-
63
- <b>Minis</b>: PDP11 and Vaxes: (PDP11_F, PDP11_D, VAX_F, VAX_D, VAX_G and VAX_H),
64
- HP3000: (XS256, XS256_DOUBLE),
65
- Wang 2200: (WANG2200).
66
-
67
- <b>Microcomputers</b> (software implementations):
68
- Apple II: (APPLE),
69
- Microsoft Basic, Spectrum, etc.: (XS128),
70
- Microsoft Quickbasic: (MBF_SINGLE, MBF_DOUBLE),
71
- Borland Pascal: (BORLAND48).
72
-
73
- <b>Embedded systems</b>:
74
- Formats used in the Intel 8051 by the C51 compiler:
75
- (C51_BCD_FLOAT, C51_BCD_DOUBLE and C51_BCD_LONG_DOUBLE).
76
-
77
-
78
- ==Calculators
79
- Formats used in HP RPL calculators: (RPL, RPL_X),
80
- HP-71B formats (HP71B, HP71B_X)
81
- and classic HP 10 digit calculators: (HP_CLASSIC).
82
-
83
-
84
- =Using the pre-defined formats
85
-
86
- require 'rubygems'
87
- require 'float-formats'
88
- include Flt
89
-
90
- The properties of the floating point formats can be queried (which can be
91
- used for tables or reports comparing different formats):
92
-
93
- Size in bits of the representations:
94
- puts IEEE_binary32.total_bits # -> 32
95
-
96
- Numeric radix:
97
- puts IEEE_binary32.radix # -> 2
98
-
99
- Digits of precision (radix-based)
100
- puts IEEE_binary32.significand_digits # -> 24
101
-
102
- Minimum and maximum values of the radix-based exponent:
103
- puts IEEE_binary32.radix_min_exp # -> -126
104
- puts IEEE_binary32.radix_max_exp # -> 127
105
-
106
- Decimal precision
107
- puts IEEE_binary32.decimal_digits_stored # -> 6
108
- puts IEEE_binary32.decimal_digits_necessary # -> 9
109
-
110
- Minimum and maximum decimal exponents:
111
- puts IEEE_binary32.decimal_min_exp # -> -37
112
- puts IEEE_binary32.decimal_max_exp # -> 38
113
-
114
- ==Encode and decode numbers
115
-
116
- For each floating-point format class there is a constructor method with the same
117
- name which can build a floating-point value from a variety of parameters:
118
- * Using three integers:
119
- the sign (+1 for +, -1 for -), the significand (coefficient or mantissa)
120
- and the exponent.
121
- * From a text numeral (with an optional Nio format specifier)
122
- * From a number : converts a numerical value
123
- to a floating point representation.
124
-
125
- File.open('binary_file.dat','wb'){|f| f.write IEEE_binary80('0.1').to_bytes}
126
-
127
- puts IEEE_binary80('0.1').to_hex(true) # -> CD CC CC CC CC CC CC CC FB 3F
128
- puts IEEE_binary80(0.1).to_hex(true) # -> CD CC CC CC CC CC CC CC FB 3F
129
- puts IEEE_binary80(+1,123,-2).to_hex(true) # -> 00 00 00 00 00 00 00 F6 03 40
130
- puts IEEE_decimal32('1.234').to_hex(true) # -> 22 20 05 34
131
-
132
- A floating-point encoded value can be converted to useful formats with the to_ and similar methods:
133
- * <tt>split</tt> (split as integral sign, significand, exponent)
134
- * <tt>to_text</tt>
135
- * <tt>to(num_class)</tt>
136
-
137
- v = IEEE_binary80.from_bytes(File.read('binary_file.dat'))
138
- puts v.to(Rational) # -> 1/10
139
- puts v.split.inspect # -> [1, 14757395258967641293, -67]
140
- puts v.to_text # -> 0.1
141
- puts v.to(Float) # -> 0.1
142
- puts v.to_hex # -> CDCCCCCCCCCCCCCCFB3F
143
- puts v.to_bits # -> 00111111111110111100110011001100110011001100110011001100110011001100110011001101
144
- puts v.to_bits_text(16) # -> 3ffbcccccccccccccccd
145
-
146
- ==Special values:
147
-
148
- Let's show the decimal expression of some interesting values using
149
- 3 significative digits:
150
-
151
- fmt = Nio::Fmt.mode(:gen,3)
152
-
153
- puts IEEE_SINGLE.min_value.to_text(fmt) # -> 2E-45
154
- puts IEEE_SINGLE.min_normalized_value.to_text(fmt) # -> 1.18E-38
155
- puts IEEE_SINGLE.max_value.to_text(fmt) # -> 3.4E38
156
- puts IEEE_SINGLE.epsilon.to_text(fmt) # -> 1.19E-7
157
-
158
- ==Convert between formats
159
-
160
- v = IEEE_EXTENDED.from_text('1.1')
161
- v = v.convert_to(IEEE_SINGLE)
162
- v = v.convert_to(IEEE_DEC64)
163
-
164
-
165
- =Tools for the native floating point format
166
- This is an optional module to perform conversions and manipulate the native Float format.
167
-
168
- require 'float-formats/native'
169
- include Flt
170
-
171
- puts float_shortest_dec(0.1) # -> 0.1
172
- puts float_significant_dec(0.1) # -> 0.10000000000000001
173
- puts float_dec(0.1) # -> 0.1000000000000000055511151231257827021181583404541015625
174
- puts float_bin(0.1) # -> 1.100110011001100110011001100110011001100110011001101E-4
175
- puts hex_from_float(0.1) # -> 0x1999999999999ap-56
176
-
177
- puts float_significant_dec(Float::MIN_D) # -> 5E-324
178
- puts float_significant_dec(Float::MAX_D) # -> 2.2250738585072009E-308
179
- puts float_significant_dec(Float::MIN_N) # -> 2.2250738585072014E-308
180
-
181
-
182
- Together with flt/sugar (from Flt) can be use to explore or work with Floats:
183
-
184
- require 'flt/sugar'
185
-
186
- puts 1.0.next_plus-1 == Float::EPSILON # -> true
187
- puts float_shortest_dec(1.0.next_plus) # -> 1.0000000000000002
188
- puts float_dec(1.0.next_minus) # -> 0.99999999999999988897769753748434595763683319091796875
189
- puts float_dec(1.0.next_plus) # -> 1.0000000000000002220446049250313080847263336181640625
190
- puts float_bin(1.0.next_plus) # -> 1.0000000000000000000000000000000000000000000000000001E0
191
- puts float_bin(1.0.next_minus) # -> 1.1111111111111111111111111111111111111111111111111111E-1
192
-
193
- puts float_significant_dec(Float::MIN_D.next_plus) # -> 1.0E-323
194
- puts float_significant_dec(Float::MAX_D.next_minus) # -> 2.2250738585072004E-308
195
-
196
- =Defining new formats
197
-
198
- New formats are defined using one of the classes defined in float-formats/classes.rb
199
- and passing the necessary parameters in a hash to the constructor.
200
-
201
- For example, here we define a binary floating point 32-bits format with
202
- 22 bits for the significand, 9 for the exponent and 1 for the sign
203
- (these fields are allocated from least to most significant bits).
204
- We'll use excess notation with bias 127 for the exponent, interpreting
205
- the significand bits as a fractional number with the radix point after
206
- the first bit, which will be hidden:
207
- Flt.define(:MY_FP, BinaryFormat,
208
- :fields=>[:significand,22,:exponent,9,:sign,1],
209
- :bias=>127, :bias_mode=>:scientific_significand,
210
- :hidden_bit=>true)
211
- Now we can encode values in this format, decode values, convet to other
212
- formats, query it's range, etc:
213
-
214
- puts MY_FP('0.1').to_bits_text(16) # -> 1ee66666
215
- puts MY_FP.max_value.to_text(Nio::Fmt.prec(3)) # -> 7.88E115
216
-
217
- You can look at float-formats/formats.rb to see how the built-in formats
218
- are defined.
219
-
220
- =License
221
-
222
- This code is free to use under the terms of the GNU GENERAL PUBLIC LICENSE.
223
-
224
- =References
225
-
226
-
227
- [<i>Floating Point Representations.</i> C.B. Silio.]
228
- http://www.ece.umd.edu/class/enpm607.S2000/fltngpt.pdf
229
- Description of formats used in UNIVAC 1100, CDC 6600/7600, PDP-11, IEEE754, IBM360/370
230
-
231
- [<i>Floating-Point Formats.</i> John Savard.]
232
- http://www.quadibloc.com/comp/cp0201.htm
233
- Description of formats used in VAX and PDF-11
234
-
235
-
236
- ===IEEE754 binary formats
237
- [<i>IEEE-754 References.</i> Christopher Vickery.]
238
- http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html
239
-
240
- [<i>What Every Computer Scientist Should Know About Floating-Point Arithmetic.</i> David Goldberg.]
241
- http://docs.sun.com/source/806-3568/ncg_goldberg.html
242
-
243
-
244
- ===DPD/IEEE754r decimal formats
245
- [<i>Decimal Arithmetic Encoding. Strawman 4d.</i> Mike Cowlishaw.]
246
- http://www2.hursley.ibm.com/decimal/decbits.pdf
247
-
248
- [<i>A Summary of Densely Packed Decimal encoding.</i> Mike Cowlishaw.]
249
- http://www2.hursley.ibm.com/decimal/DPDecimal.html
250
-
251
- [<i>Packed Decimal Encoding IEEE-754-r.</i> J.H.M. Bonten.]
252
- http://home.hetnet.nl/mr_1/81/jhm.bonten/computers/bitsandbytes/wordsizes/ibmpde.htm
253
-
254
- [<i>DRAFT Standard for Floating-Point Arithmetic P754.</i> IEEE.]
255
- http://www.validlab.com/754R/drafts/archive/2007-10-05.pdf
256
-
257
-
258
-
259
- ===HP 10 digits calculators
260
-
261
- [<i>HP CPU and Programming</i>. David G.Hicks.]
262
- http://www.hpmuseum.org/techcpu.htm Description of calculator CPUs from the Museum of HP Calculators.
263
- [<i>HP 35 ROM step by step.</i> Jacques Laporte]
264
- http://www.jacques-laporte.org/HP35%20ROM.htm
265
- Description of HP35 registers.
266
- [<i>Scientific Pocket Calculator Extends Range of Built-In Functions.</i> Eric A. Evett, Paul J. McClellan, Joseph P. Tanzini.]
267
- Hewlett Packard Journal 1983-05 pgs 27-28. Describes format used in HP-15C.
268
-
269
-
270
- ===HP 12 digits calculators
271
- [<i>Software Internal Design Specification Volume I For the HP-71</i>. Hewlett Packard.]
272
- Available from http://www.hpmuseum.org/cd/cddesc.htm
273
- [<i>RPL PROGRAMMING GUIDE</i>]
274
- Excerpted from <i>RPL: A Mathematical Control Language</i>. by W. C. Wickes.
275
- Available at http://www.hpcalc.org/details.php?id=1743
276
-
277
- ===HP-3000
278
- [<i>A Pocket Calculator for Computer Science Professionals.</i> Eric A. Evett.]
279
- Hewlett Packard Journal 1983-05 pg 37. Describes format used in HP-3000
280
-
281
- ===IBM
282
- [<i>IBM Floating Point Architecture.</i> Wikipedia.]
283
- http://en.wikipedia.org/wiki/IBM_Floating_Point_Architecture
284
- [<i>The IBM eServer z990 floating-point unit</i>. G. Gerwig, H. Wetter, E. M. Schwarz, J. Haess, C. A. Krygowski, B. M. Fleischer and M. Kroener.]
285
- http://www.research.ibm.com/journal/rd/483/gerwig.html
286
-
287
- ===MBF
288
- [<i>Microsoft Knowledbase Article 35826</i>]
289
- http://support.microsoft.com/?scid=kb%3Ben-us%3B35826&x=17&y=12
290
- [<i>Microsoft MBF2IEEE library</i>]
291
- http://download.microsoft.com/download/vb30/install/1/win98/en-us/mbf2ieee.exe
292
-
293
- ===Borland
294
- [<i>An Overview of Floating Point Numbers.</i> Borland Developer Support Staff]
295
-
296
- [<i>Pascal Floating-Point Page.<i> J R Stockton.]
297
- http://www.merlyn.demon.co.uk/pas-real.htm
298
-
299
- ===8-bit micros
300
- This is the MS Basic format (BASIC09 for TRS-80 Color Computer, Dragon),
301
- also used in the Sinclair Spectrum.
302
-
303
- [<i>Numbers are followed by information not in listings</i>]
304
- Sinclair User October 1983 http://www.sincuser.f9.co.uk/019/helplne.htm
305
-
306
- [<i>Sinclair ZX Spectrum / Basic Programming.</i>. Steven Vickers.]
307
- Chapter 24. http://www.worldofspectrum.org/ZXBasicManual/zxmanchap24.html
308
-
309
-
310
-
311
- ===Apple II
312
- [<i>Floating Point Routines for the 6502</i> Roy Rankin and Steve Wozniak.]
313
- Dr. Dobb's Journal, August 1976, pages 17-19.
314
-
315
- ===C51
316
- [<i>Advanced Development System</i> Franklin Software, Inc.]
317
- http://www.fsinc.com/reference/html/com9anm.htm
318
-
319
- ===CDC6600
320
- [<i>CONTROL DATA 6400/6500/6600 COMPUTER SYSTEMS Reference Manual</i>]
321
- Manuals available at http://bitsavers.org/
322
-
323
-
324
- ===Cray
325
- [<i>CRAY-1 COMPUTER SYSTEM Hardware Reference Manual</i>]
326
- See pg 3-20 from 2240004 or pg 4-30 from HR-0808 or pg 4-21 from HP-0032.
327
- Manuals available at http://bitsavers.org/
328
-
329
- ===Wang 2200
330
- [<i>Internal Floating Point Representation</i>] http://www.wang2200.org/fp_format.html
data/setup.rb DELETED
@@ -1,1585 +0,0 @@
1
- #
2
- # setup.rb
3
- #
4
- # Copyright (c) 2000-2005 Minero Aoki
5
- #
6
- # This program is free software.
7
- # You can distribute/modify this program under the terms of
8
- # the GNU LGPL, Lesser General Public License version 2.1.
9
- #
10
-
11
- unless Enumerable.method_defined?(:map) # Ruby 1.4.6
12
- module Enumerable
13
- alias map collect
14
- end
15
- end
16
-
17
- unless File.respond_to?(:read) # Ruby 1.6
18
- def File.read(fname)
19
- open(fname) {|f|
20
- return f.read
21
- }
22
- end
23
- end
24
-
25
- unless Errno.const_defined?(:ENOTEMPTY) # Windows?
26
- module Errno
27
- class ENOTEMPTY
28
- # We do not raise this exception, implementation is not needed.
29
- end
30
- end
31
- end
32
-
33
- def File.binread(fname)
34
- open(fname, 'rb') {|f|
35
- return f.read
36
- }
37
- end
38
-
39
- # for corrupted Windows' stat(2)
40
- def File.dir?(path)
41
- File.directory?((path[-1,1] == '/') ? path : path + '/')
42
- end
43
-
44
-
45
- class ConfigTable
46
-
47
- include Enumerable
48
-
49
- def initialize(rbconfig)
50
- @rbconfig = rbconfig
51
- @items = []
52
- @table = {}
53
- # options
54
- @install_prefix = nil
55
- @config_opt = nil
56
- @verbose = true
57
- @no_harm = false
58
- end
59
-
60
- attr_accessor :install_prefix
61
- attr_accessor :config_opt
62
-
63
- attr_writer :verbose
64
-
65
- def verbose?
66
- @verbose
67
- end
68
-
69
- attr_writer :no_harm
70
-
71
- def no_harm?
72
- @no_harm
73
- end
74
-
75
- def [](key)
76
- lookup(key).resolve(self)
77
- end
78
-
79
- def []=(key, val)
80
- lookup(key).set val
81
- end
82
-
83
- def names
84
- @items.map {|i| i.name }
85
- end
86
-
87
- def each(&block)
88
- @items.each(&block)
89
- end
90
-
91
- def key?(name)
92
- @table.key?(name)
93
- end
94
-
95
- def lookup(name)
96
- @table[name] or setup_rb_error "no such config item: #{name}"
97
- end
98
-
99
- def add(item)
100
- @items.push item
101
- @table[item.name] = item
102
- end
103
-
104
- def remove(name)
105
- item = lookup(name)
106
- @items.delete_if {|i| i.name == name }
107
- @table.delete_if {|name, i| i.name == name }
108
- item
109
- end
110
-
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
115
- end
116
-
117
- def savefile
118
- '.config'
119
- end
120
-
121
- def load_savefile
122
- begin
123
- File.foreach(savefile()) do |line|
124
- k, v = *line.split(/=/, 2)
125
- self[k] = v.strip
126
- end
127
- rescue Errno::ENOENT
128
- setup_rb_error $!.message + "\n#{File.basename($0)} config first"
129
- end
130
- end
131
-
132
- def save
133
- @items.each {|i| i.value }
134
- File.open(savefile(), 'w') {|f|
135
- @items.each do |i|
136
- f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
137
- end
138
- }
139
- end
140
-
141
- def load_standard_entries
142
- standard_entries(@rbconfig).each do |ent|
143
- add ent
144
- end
145
- end
146
-
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
- }
191
-
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
197
-
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
275
-
276
- ALIASES = {
277
- 'std-ruby' => 'librubyver',
278
- 'stdruby' => 'librubyver',
279
- 'rubylibdir' => 'librubyver',
280
- 'archdir' => 'librubyverarch',
281
- 'site-ruby-common' => 'siteruby', # For backward compatibility
282
- 'site-ruby' => 'siterubyver', # For backward compatibility
283
- 'bin-dir' => 'bindir',
284
- 'bin-dir' => 'bindir',
285
- 'rb-dir' => 'rbdir',
286
- 'so-dir' => 'sodir',
287
- 'data-dir' => 'datadir',
288
- 'ruby-path' => 'rubypath',
289
- 'ruby-prog' => 'rubyprog',
290
- 'ruby' => 'rubyprog',
291
- 'make-prog' => 'makeprog',
292
- 'make' => 'makeprog'
293
- }
294
-
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/
302
- end
303
-
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]
307
- end
308
-
309
- def dllext
310
- @rbconfig['DLEXT']
311
- end
312
-
313
- def value_config?(name)
314
- lookup(name).value?
315
- end
316
-
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
325
-
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?
337
- true
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
357
- end
358
- end
359
-
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
380
- end
381
-
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
393
- end
394
-
395
- class ProgramItem < Item
396
- def config_type
397
- 'program'
398
- end
399
- end
400
-
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
419
- end
420
-
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
449
- end
450
-
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
456
-
457
- attr_reader :help_default
458
-
459
- def config_type
460
- 'package'
461
- end
462
-
463
- private
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?
536
- module FileOperations
537
-
538
- def mkdir_p(dirname, prefix = nil)
539
- dirname = prefix + File.expand_path(dirname) if prefix
540
- $stderr.puts "mkdir -p #{dirname}" if verbose?
541
- return if no_harm?
542
-
543
- # Does not check '/', it's too abnormal.
544
- dirs = File.expand_path(dirname).split(%r<(?=/)>)
545
- if /\A[a-z]:\z/i =~ dirs[0]
546
- disk = dirs.shift
547
- dirs[0] = disk + dirs[0]
548
- end
549
- dirs.each_index do |idx|
550
- path = dirs[0..idx].join('')
551
- Dir.mkdir path unless File.dir?(path)
552
- end
553
- end
554
-
555
- def rm_f(path)
556
- $stderr.puts "rm -f #{path}" if verbose?
557
- return if no_harm?
558
- force_remove_file path
559
- end
560
-
561
- def rm_rf(path)
562
- $stderr.puts "rm -rf #{path}" if verbose?
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
576
-
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
586
- else
587
- force_remove_file entpath
588
- end
589
- end
590
- begin
591
- Dir.rmdir path
592
- rescue Errno::ENOTEMPTY
593
- # directory may not be empty
594
- end
595
- end
596
-
597
- def move_file(src, dest)
598
- force_remove_file dest
599
- begin
600
- File.rename src, dest
601
- rescue
602
- File.open(dest, 'wb') {|f|
603
- f.write File.binread(src)
604
- }
605
- File.chmod File.stat(src).mode, dest
606
- File.unlink src
607
- end
608
- end
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
-
622
- def install(from, dest, mode, prefix = nil)
623
- $stderr.puts "install #{from} #{dest}" if verbose?
624
- return if no_harm?
625
-
626
- realdest = prefix ? prefix + File.expand_path(dest) : dest
627
- realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
628
- str = File.binread(from)
629
- if diff?(str, realdest)
630
- verbose_off {
631
- rm_f realdest if File.exist?(realdest)
632
- }
633
- File.open(realdest, 'wb') {|f|
634
- f.write str
635
- }
636
- File.chmod mode, realdest
637
-
638
- File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
639
- if prefix
640
- f.puts realdest.sub(prefix, '')
641
- else
642
- f.puts realdest
643
- end
644
- }
645
- end
646
- end
647
-
648
- def diff?(new_content, path)
649
- return true unless File.exist?(path)
650
- new_content != File.binread(path)
651
- end
652
-
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"
657
- end
658
-
659
- def ruby(*args)
660
- command config('rubyprog'), *args
661
- end
662
-
663
- def make(task = nil)
664
- command(*[config('makeprog'), task].compact)
665
- end
666
-
667
- def extdir?(dir)
668
- File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
669
- end
670
-
671
- def files_of(dir)
672
- Dir.open(dir) {|d|
673
- return d.select {|ent| File.file?("#{dir}/#{ent}") }
674
- }
675
- end
676
-
677
- DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
678
-
679
- def directories_of(dir)
680
- Dir.open(dir) {|d|
681
- return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
682
- }
683
- end
684
-
685
- end
686
-
687
-
688
- # This module requires: #srcdir_root, #objdir_root, #relpath
689
- module HookScriptAPI
690
-
691
- def get_config(key)
692
- @config[key]
693
- end
694
-
695
- alias config get_config
696
-
697
- # obsolete: use metaconfig to change configuration
698
- def set_config(key, val)
699
- @config[key] = val
700
- end
701
-
702
- #
703
- # srcdir/objdir (works only in the package directory)
704
- #
705
-
706
- def curr_srcdir
707
- "#{srcdir_root()}/#{relpath()}"
708
- end
709
-
710
- def curr_objdir
711
- "#{objdir_root()}/#{relpath()}"
712
- end
713
-
714
- def srcfile(path)
715
- "#{curr_srcdir()}/#{path}"
716
- end
717
-
718
- def srcexist?(path)
719
- File.exist?(srcfile(path))
720
- end
721
-
722
- def srcdirectory?(path)
723
- File.dir?(srcfile(path))
724
- end
725
-
726
- def srcfile?(path)
727
- File.file?(srcfile(path))
728
- end
729
-
730
- def srcentries(path = '.')
731
- Dir.open("#{curr_srcdir()}/#{path}") {|d|
732
- return d.to_a - %w(. ..)
733
- }
734
- end
735
-
736
- def srcfiles(path = '.')
737
- srcentries(path).select {|fname|
738
- File.file?(File.join(curr_srcdir(), path, fname))
739
- }
740
- end
741
-
742
- def srcdirectories(path = '.')
743
- srcentries(path).select {|fname|
744
- File.dir?(File.join(curr_srcdir(), path, fname))
745
- }
746
- end
747
-
748
- end
749
-
750
-
751
- class ToplevelInstaller
752
-
753
- Version = '3.4.1'
754
- Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
755
-
756
- TASKS = [
757
- [ 'all', 'do config, setup, then install' ],
758
- [ 'config', 'saves your configurations' ],
759
- [ 'show', 'shows current configuration' ],
760
- [ 'setup', 'compiles ruby extentions and others' ],
761
- [ 'install', 'installs files' ],
762
- [ 'test', 'run all tests in test/' ],
763
- [ 'clean', "does `make clean' for each extention" ],
764
- [ 'distclean',"does `make distclean' for each extention" ]
765
- ]
766
-
767
- def ToplevelInstaller.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
774
- end
775
-
776
- def ToplevelInstaller.multipackage?
777
- File.dir?(File.dirname($0) + '/packages')
778
- end
779
-
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
790
-
791
- def initialize(ardir_root, config)
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]
800
- end
801
-
802
- def inspect
803
- "#<#{self.class} #{__id__()}>"
804
- end
805
-
806
- def invoke
807
- run_metaconfigs
808
- case task = parsearg_global()
809
- when nil, 'all'
810
- parsearg_config
811
- init_installers
812
- exec_config
813
- exec_setup
814
- exec_install
815
- else
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
824
- __send__ "parsearg_#{task}"
825
- init_installers
826
- __send__ "exec_#{task}"
827
- end
828
- end
829
-
830
- def run_metaconfigs
831
- @config.load_script "#{@ardir}/metaconfig"
832
- end
833
-
834
- def init_installers
835
- @installer = Installer.new(@config, @ardir, File.expand_path('.'))
836
- end
837
-
838
- #
839
- # Hook Script API bases
840
- #
841
-
842
- def srcdir_root
843
- @ardir
844
- end
845
-
846
- def objdir_root
847
- '.'
848
- end
849
-
850
- def relpath
851
- '.'
852
- end
853
-
854
- #
855
- # Option Parsing
856
- #
857
-
858
- def parsearg_global
859
- while arg = ARGV.shift
860
- case arg
861
- when /\A\w+\z/
862
- setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
863
- return arg
864
- when '-q', '--quiet'
865
- @config.verbose = false
866
- when '--verbose'
867
- @config.verbose = true
868
- when '--help'
869
- print_usage $stdout
870
- exit 0
871
- when '--version'
872
- puts "#{File.basename($0)} version #{Version}"
873
- exit 0
874
- when '--copyright'
875
- puts Copyright
876
- exit 0
877
- else
878
- setup_rb_error "unknown global option '#{arg}'"
879
- end
880
- end
881
- nil
882
- end
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
891
-
892
- def parsearg_no_options
893
- unless ARGV.empty?
894
- task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
895
- setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
896
- end
897
- end
898
-
899
- alias parsearg_show parsearg_no_options
900
- alias parsearg_setup parsearg_no_options
901
- alias parsearg_test parsearg_no_options
902
- alias parsearg_clean parsearg_no_options
903
- alias parsearg_distclean parsearg_no_options
904
-
905
- def parsearg_config
906
- evalopt = []
907
- set = []
908
- @config.config_opt = []
909
- while i = ARGV.shift
910
- if /\A--?\z/ =~ i
911
- @config.config_opt = ARGV.dup
912
- break
913
- end
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)
928
- end
929
- end
930
-
931
- def parsearg_install
932
- @config.no_harm = false
933
- @config.install_prefix = ''
934
- while a = ARGV.shift
935
- case a
936
- when '--no-harm'
937
- @config.no_harm = true
938
- when /\A--prefix=/
939
- path = a.split(/=/, 2)[1]
940
- path = File.expand_path(path) unless path[0,1] == '/'
941
- @config.install_prefix = path
942
- else
943
- setup_rb_error "install: unknown option #{a}"
944
- end
945
- end
946
- end
947
-
948
- def print_usage(out)
949
- out.puts 'Typical Installation Procedure:'
950
- out.puts " $ ruby #{File.basename $0} config"
951
- out.puts " $ ruby #{File.basename $0} setup"
952
- out.puts " # ruby #{File.basename $0} install (may require root privilege)"
953
- out.puts
954
- out.puts 'Detailed Usage:'
955
- out.puts " ruby #{File.basename $0} <global option>"
956
- out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
957
-
958
- fmt = " %-24s %s\n"
959
- out.puts
960
- out.puts 'Global options:'
961
- out.printf fmt, '-q,--quiet', 'suppress message outputs'
962
- out.printf fmt, ' --verbose', 'output messages verbosely'
963
- out.printf fmt, ' --help', 'print this message'
964
- out.printf fmt, ' --version', 'print version and quit'
965
- out.printf fmt, ' --copyright', 'print copyright and quit'
966
- out.puts
967
- out.puts 'Tasks:'
968
- TASKS.each do |name, desc|
969
- out.printf fmt, name, desc
970
- end
971
-
972
- fmt = " %-24s %s [%s]\n"
973
- out.puts
974
- out.puts 'Options for CONFIG or ALL:'
975
- @config.each do |item|
976
- out.printf fmt, item.help_opt, item.description, item.help_default
977
- end
978
- out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
979
- out.puts
980
- out.puts 'Options for INSTALL:'
981
- out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
982
- out.printf fmt, '--prefix=path', 'install path prefix', ''
983
- out.puts
984
- end
985
-
986
- #
987
- # Task Handlers
988
- #
989
-
990
- def exec_config
991
- @installer.exec_config
992
- @config.save # must be final
993
- end
994
-
995
- def exec_setup
996
- @installer.exec_setup
997
- end
998
-
999
- def exec_install
1000
- @installer.exec_install
1001
- end
1002
-
1003
- def exec_test
1004
- @installer.exec_test
1005
- end
1006
-
1007
- def exec_show
1008
- @config.each do |i|
1009
- printf "%-20s %s\n", i.name, i.value if i.value?
1010
- end
1011
- end
1012
-
1013
- def exec_clean
1014
- @installer.exec_clean
1015
- end
1016
-
1017
- def exec_distclean
1018
- @installer.exec_distclean
1019
- end
1020
-
1021
- end # class ToplevelInstaller
1022
-
1023
-
1024
- class ToplevelInstallerMulti < ToplevelInstaller
1025
-
1026
- include FileOperations
1027
-
1028
- def initialize(ardir_root, config)
1029
- super
1030
- @packages = directories_of("#{@ardir}/packages")
1031
- raise 'no package exists' if @packages.empty?
1032
- @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
1033
- end
1034
-
1035
- def run_metaconfigs
1036
- @config.load_script "#{@ardir}/metaconfig", self
1037
- @packages.each do |name|
1038
- @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
1039
- end
1040
- end
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
-
1053
- def init_installers
1054
- @installers = {}
1055
- @packages.each do |pack|
1056
- @installers[pack] = Installer.new(@config,
1057
- "#{@ardir}/packages/#{pack}",
1058
- "packages/#{pack}")
1059
- end
1060
- with = extract_selection(config('with'))
1061
- without = extract_selection(config('without'))
1062
- @selected = @installers.keys.select {|name|
1063
- (with.empty? or with.include?(name)) \
1064
- and not without.include?(name)
1065
- }
1066
- end
1067
-
1068
- def extract_selection(list)
1069
- a = list.split(/,/)
1070
- a.each do |name|
1071
- setup_rb_error "no such package: #{name}" unless @installers.key?(name)
1072
- end
1073
- a
1074
- end
1075
-
1076
- def print_usage(f)
1077
- super
1078
- f.puts 'Inluded packages:'
1079
- f.puts ' ' + @packages.sort.join(' ')
1080
- f.puts
1081
- end
1082
-
1083
- #
1084
- # Task Handlers
1085
- #
1086
-
1087
- def exec_config
1088
- run_hook 'pre-config'
1089
- each_selected_installers {|inst| inst.exec_config }
1090
- run_hook 'post-config'
1091
- @config.save # must be final
1092
- end
1093
-
1094
- def exec_setup
1095
- run_hook 'pre-setup'
1096
- each_selected_installers {|inst| inst.exec_setup }
1097
- run_hook 'post-setup'
1098
- end
1099
-
1100
- def exec_install
1101
- run_hook 'pre-install'
1102
- each_selected_installers {|inst| inst.exec_install }
1103
- run_hook 'post-install'
1104
- end
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
-
1112
- def exec_clean
1113
- rm_f @config.savefile
1114
- run_hook 'pre-clean'
1115
- each_selected_installers {|inst| inst.exec_clean }
1116
- run_hook 'post-clean'
1117
- end
1118
-
1119
- def exec_distclean
1120
- rm_f @config.savefile
1121
- run_hook 'pre-distclean'
1122
- each_selected_installers {|inst| inst.exec_distclean }
1123
- run_hook 'post-distclean'
1124
- end
1125
-
1126
- #
1127
- # lib
1128
- #
1129
-
1130
- def each_selected_installers
1131
- Dir.mkdir 'packages' unless File.dir?('packages')
1132
- @selected.each do |pack|
1133
- $stderr.puts "Processing the package `#{pack}' ..." if verbose?
1134
- Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
1135
- Dir.chdir "packages/#{pack}"
1136
- yield @installers[pack]
1137
- Dir.chdir '../..'
1138
- end
1139
- end
1140
-
1141
- def run_hook(id)
1142
- @root_installer.run_hook id
1143
- end
1144
-
1145
- # module FileOperations requires this
1146
- def verbose?
1147
- @config.verbose?
1148
- end
1149
-
1150
- # module FileOperations requires this
1151
- def no_harm?
1152
- @config.no_harm?
1153
- end
1154
-
1155
- end # class ToplevelInstallerMulti
1156
-
1157
-
1158
- class Installer
1159
-
1160
- FILETYPES = %w( bin lib ext data conf man )
1161
-
1162
- include FileOperations
1163
- include HookScriptAPI
1164
-
1165
- def initialize(config, srcroot, objroot)
1166
- @config = config
1167
- @srcdir = File.expand_path(srcroot)
1168
- @objdir = File.expand_path(objroot)
1169
- @currdir = '.'
1170
- end
1171
-
1172
- def inspect
1173
- "#<#{self.class} #{File.basename(@srcdir)}>"
1174
- end
1175
-
1176
- def noop(rel)
1177
- end
1178
-
1179
- #
1180
- # Hook Script API base methods
1181
- #
1182
-
1183
- def srcdir_root
1184
- @srcdir
1185
- end
1186
-
1187
- def objdir_root
1188
- @objdir
1189
- end
1190
-
1191
- def relpath
1192
- @currdir
1193
- end
1194
-
1195
- #
1196
- # Config Access
1197
- #
1198
-
1199
- # module FileOperations requires this
1200
- def verbose?
1201
- @config.verbose?
1202
- end
1203
-
1204
- # module FileOperations requires this
1205
- def no_harm?
1206
- @config.no_harm?
1207
- end
1208
-
1209
- def verbose_off
1210
- begin
1211
- save, @config.verbose = @config.verbose?, false
1212
- yield
1213
- ensure
1214
- @config.verbose = save
1215
- end
1216
- end
1217
-
1218
- #
1219
- # TASK config
1220
- #
1221
-
1222
- def exec_config
1223
- exec_task_traverse 'config'
1224
- end
1225
-
1226
- alias config_dir_bin noop
1227
- alias config_dir_lib noop
1228
-
1229
- def config_dir_ext(rel)
1230
- extconf if extdir?(curr_srcdir())
1231
- end
1232
-
1233
- alias config_dir_data noop
1234
- alias config_dir_conf noop
1235
- alias config_dir_man noop
1236
-
1237
- def extconf
1238
- ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
1239
- end
1240
-
1241
- #
1242
- # TASK setup
1243
- #
1244
-
1245
- def exec_setup
1246
- exec_task_traverse 'setup'
1247
- end
1248
-
1249
- def setup_dir_bin(rel)
1250
- files_of(curr_srcdir()).each do |fname|
1251
- update_shebang_line "#{curr_srcdir()}/#{fname}"
1252
- end
1253
- end
1254
-
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)
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)
1299
- tmpfile = File.basename(path) + '.tmp'
1300
- begin
1301
- File.open(tmpfile, 'wb', &block)
1302
- File.rename tmpfile, File.basename(path)
1303
- ensure
1304
- File.unlink tmpfile if File.exist?(tmpfile)
1305
- end
1306
- end
1307
-
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
1317
-
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
1327
-
1328
- attr_reader :cmd
1329
- attr_reader :args
1330
-
1331
- def to_s
1332
- "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
1333
- end
1334
- end
1335
-
1336
- #
1337
- # TASK install
1338
- #
1339
-
1340
- def exec_install
1341
- rm_f 'InstalledFiles'
1342
- exec_task_traverse 'install'
1343
- end
1344
-
1345
- def install_dir_bin(rel)
1346
- install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
1347
- end
1348
-
1349
- def install_dir_lib(rel)
1350
- install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
1351
- end
1352
-
1353
- def install_dir_ext(rel)
1354
- return unless extdir?(curr_srcdir())
1355
- install_files rubyextentions('.'),
1356
- "#{config('sodir')}/#{File.dirname(rel)}",
1357
- 0555
1358
- end
1359
-
1360
- def install_dir_data(rel)
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
1372
- end
1373
-
1374
- def install_files(list, dest, mode)
1375
- mkdir_p dest, @config.install_prefix
1376
- list.each do |fname|
1377
- install fname, dest, mode, @config.install_prefix
1378
- end
1379
- end
1380
-
1381
- def libfiles
1382
- glob_reject(%w(*.y *.output), targetfiles())
1383
- end
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
-
1406
- # picked up many entries from cvs-1.11.1/src/ignore.c
1407
- JUNK_FILES = %w(
1408
- core RCSLOG tags TAGS .make.state
1409
- .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
1410
- *~ *.old *.bak *.BAK *.orig *.rej _$* *$
1411
-
1412
- *.org *.in .*
1413
- )
1414
-
1415
- def existfiles
1416
- glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
1417
- end
1418
-
1419
- def hookfiles
1420
- %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
1421
- %w( config setup install clean ).map {|t| sprintf(fmt, t) }
1422
- }.flatten
1423
- end
1424
-
1425
- def glob_select(pat, ents)
1426
- re = globs2re([pat])
1427
- ents.select {|ent| re =~ ent }
1428
- end
1429
-
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
1468
- end
1469
-
1470
- #
1471
- # TASK clean
1472
- #
1473
-
1474
- def exec_clean
1475
- exec_task_traverse 'clean'
1476
- rm_f @config.savefile
1477
- rm_f 'InstalledFiles'
1478
- end
1479
-
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
1485
-
1486
- def clean_dir_ext(rel)
1487
- return unless extdir?(curr_srcdir())
1488
- make 'clean' if File.file?('Makefile')
1489
- end
1490
-
1491
- #
1492
- # TASK distclean
1493
- #
1494
-
1495
- def exec_distclean
1496
- exec_task_traverse 'distclean'
1497
- rm_f @config.savefile
1498
- rm_f 'InstalledFiles'
1499
- end
1500
-
1501
- alias distclean_dir_bin noop
1502
- alias distclean_dir_lib noop
1503
-
1504
- def distclean_dir_ext(rel)
1505
- return unless extdir?(curr_srcdir())
1506
- make 'distclean' if File.file?('Makefile')
1507
- end
1508
-
1509
- alias distclean_dir_data noop
1510
- alias distclean_dir_conf noop
1511
- alias distclean_dir_man noop
1512
-
1513
- #
1514
- # Traversing
1515
- #
1516
-
1517
- def exec_task_traverse(task)
1518
- run_hook "pre-#{task}"
1519
- FILETYPES.each do |type|
1520
- if type == 'ext' and config('without-ext') == 'yes'
1521
- $stderr.puts 'skipping ext/* by user option' if verbose?
1522
- next
1523
- end
1524
- traverse task, type, "#{task}_dir_#{type}"
1525
- end
1526
- run_hook "post-#{task}"
1527
- end
1528
-
1529
- def traverse(task, rel, mid)
1530
- dive_into(rel) {
1531
- run_hook "pre-#{task}"
1532
- __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1533
- directories_of(curr_srcdir()).each do |d|
1534
- traverse task, "#{rel}/#{d}", mid
1535
- end
1536
- run_hook "post-#{task}"
1537
- }
1538
- end
1539
-
1540
- def dive_into(rel)
1541
- return unless File.dir?("#{@srcdir}/#{rel}")
1542
-
1543
- dir = File.basename(rel)
1544
- Dir.mkdir dir unless File.dir?(dir)
1545
- prevdir = Dir.pwd
1546
- Dir.chdir dir
1547
- $stderr.puts '---> ' + rel if verbose?
1548
- @currdir = rel
1549
- yield
1550
- Dir.chdir prevdir
1551
- $stderr.puts '<--- ' + rel if verbose?
1552
- @currdir = File.dirname(rel)
1553
- end
1554
-
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
1571
-
1572
- def setup_rb_error(msg)
1573
- raise SetupError, msg
1574
- end
1575
-
1576
- if $0 == __FILE__
1577
- begin
1578
- ToplevelInstaller.invoke
1579
- rescue SetupError
1580
- raise if $DEBUG
1581
- $stderr.puts $!.message
1582
- $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1583
- exit 1
1584
- end
1585
- end