cgen 0.16.0
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/.gitignore +5 -0
- data/History.txt +199 -0
- data/README.txt +34 -0
- data/examples/bench.rb +14 -0
- data/examples/complex.rb +63 -0
- data/examples/complex2.rb +48 -0
- data/examples/cshadow-example.rb +55 -0
- data/examples/cshadow-point.rb +58 -0
- data/examples/ctest.rb +34 -0
- data/examples/ctest2.rb +32 -0
- data/examples/ctest3.rb +179 -0
- data/examples/ctest4.rb +18 -0
- data/examples/ctest5.rb +27 -0
- data/examples/example-ruby-talk-30April2004.rb +65 -0
- data/examples/fixed-array.rb +221 -0
- data/examples/inherit-example.rb +26 -0
- data/examples/inherit-example.txt +80 -0
- data/examples/instance-eval.rb +66 -0
- data/examples/ivset.rb +55 -0
- data/examples/marshal-test.rb +19 -0
- data/examples/matrix.rb +91 -0
- data/examples/modular-def.rb +87 -0
- data/examples/objattr.rb +46 -0
- data/examples/opaque-struct-test.rb +36 -0
- data/examples/sample.rb +184 -0
- data/examples/struct.rb +103 -0
- data/examples/test.rb +24 -0
- data/examples/yaml.rb +56 -0
- data/install.rb +1015 -0
- data/lib/cgen/attribute.rb +414 -0
- data/lib/cgen/cgen.rb +2041 -0
- data/lib/cgen/cshadow.rb +1037 -0
- data/lib/cgen/inherit.rb +46 -0
- data/rakefile +42 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test-attribute.rb +430 -0
- data/test/test-cgen.rb +127 -0
- data/test/test-cshadow.rb +289 -0
- data/test/test.rb +17 -0
- metadata +123 -0
@@ -0,0 +1,414 @@
|
|
1
|
+
module CShadow
|
2
|
+
|
3
|
+
AttributeTypes = []
|
4
|
+
|
5
|
+
# This is the base class for all plug-in attribute classes used with the
|
6
|
+
# CShadow module. Each subclass provides information which CShadow uses to
|
7
|
+
# manage some of the housekeeping for the attribute:
|
8
|
+
#
|
9
|
+
# * declaration of the attribute in the shadow struct,
|
10
|
+
#
|
11
|
+
# * accessor code (which CShadow wraps in methods),
|
12
|
+
#
|
13
|
+
# * type conversion for the write accessor,
|
14
|
+
#
|
15
|
+
# * type checking for the write accessor,
|
16
|
+
#
|
17
|
+
# * the 'mark' function, if the attribute refers to Ruby objects,
|
18
|
+
#
|
19
|
+
# * the 'free' function, if the attribute refers to C data,
|
20
|
+
#
|
21
|
+
# * initialization (usually, this is left to the class's #initialize method),
|
22
|
+
#
|
23
|
+
# * serialization methods for the attribute.(*)
|
24
|
+
#
|
25
|
+
# (*) For Ruby versions before 1.7, requires a patch using the marshal.patch
|
26
|
+
# file (the patch is explained in marshal.txt).
|
27
|
+
#
|
28
|
+
# The subclass hierarchy has two branches: ObjectAttribute and
|
29
|
+
# CNativeAttribute. The former is a reference to a Ruby object (in other
|
30
|
+
# words, a struct member of type +VALUE+. The latter has subclasses for
|
31
|
+
# various C data types, such as +double+ and <tt>char *</tt>.
|
32
|
+
#
|
33
|
+
# ==Adding new attribute classes
|
34
|
+
#
|
35
|
+
# Each attribute class must define a class method called 'match' which returns
|
36
|
+
# true if the right hand side of the ':name => ...' expression is recognized
|
37
|
+
# as defining an attribute of the class. The class should have an initialize
|
38
|
+
# method to supply custom code for readers, writers, type checking, memory
|
39
|
+
# management, and serialization. (If serialization methods are omitted, the
|
40
|
+
# attribute will be ignored during +dump+/+load+.) The easiest way is to
|
41
|
+
# follow the examples. For many purposes, most of the work can be done by
|
42
|
+
# subclassing existing classes.
|
43
|
+
#
|
44
|
+
# The +dump+ and +load+ methods require a bit of explanation. Each attribute
|
45
|
+
# provides code to dump and load itself in a very generic way. The dump code
|
46
|
+
# must operate on a ruby array called result. It must push _one_ piece of ruby
|
47
|
+
# data (which may itself be an array, hash, etc.) onto this array. Similarly,
|
48
|
+
# the load code operates on a ruby array called from_array. It must shift
|
49
|
+
# _one_ piece of ruby data from this array. (Probably it would have been more
|
50
|
+
# efficient to use LIFO rather than FIFO. Oh, well.)
|
51
|
+
#
|
52
|
+
# ==To do:
|
53
|
+
#
|
54
|
+
# * Type checking and conversion should to to_str, to_ary, etc. first if
|
55
|
+
# appropriate.
|
56
|
+
#
|
57
|
+
# * Consider changing '[Foo]' to 'shadow(Foo)' and using [Foo, Bar, Baz],
|
58
|
+
# [Foo]*20, and [Foo .. Foo] to signify structs, fixed-length arrays, and
|
59
|
+
# var-length arrays of (VALUE *) ?
|
60
|
+
#
|
61
|
+
# * More attribute classes: floats, unsigned, fixed length arrays, bitfields,
|
62
|
+
# etc.
|
63
|
+
#
|
64
|
+
# * substructs?
|
65
|
+
#
|
66
|
+
# * Make classes more generic, so that there aren't so many classes. (Factory
|
67
|
+
# classes, like ArrayAttribute(PointerAttribute(:char)) ?)
|
68
|
+
#
|
69
|
+
# * Support for #freeze, #taint, etc.
|
70
|
+
#
|
71
|
+
# ==Limitations:
|
72
|
+
#
|
73
|
+
# * IntAttribute: No attempt to handle Bignums.
|
74
|
+
#
|
75
|
+
class Attribute
|
76
|
+
|
77
|
+
def Attribute.inherited subclass
|
78
|
+
AttributeTypes << subclass
|
79
|
+
end
|
80
|
+
|
81
|
+
def Attribute.match decl
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_reader :var, :cvar, :cdecl,
|
86
|
+
:init, :reader, :check, :writer,
|
87
|
+
:mark, :free, :dump, :load,
|
88
|
+
:persists, :owner_class
|
89
|
+
|
90
|
+
def initialize owner_class, var, match, persists = true
|
91
|
+
@owner_class = owner_class
|
92
|
+
@var, @match = var, match
|
93
|
+
@persists = persists
|
94
|
+
end
|
95
|
+
|
96
|
+
def inspect
|
97
|
+
%{<#{self.class} #{@cvar} => #{@cdecl.inspect}>}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# There are two kinds of object attributes. Both refer to Ruby objects and can
|
102
|
+
# be typed or untyped. This one is less optimized but cat refer to arbitrary
|
103
|
+
# ruby objects.
|
104
|
+
#
|
105
|
+
# The syntax for adding an object attribute to a class is simple. The
|
106
|
+
# following code adds three object attributes, one untyped and two typed:
|
107
|
+
#
|
108
|
+
# class A
|
109
|
+
# include CShadow
|
110
|
+
# shadow_attr_accessor :obj => Object, :sym => Symbol, :ary => Array
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# (See CShadow for variations on #shadow_attr_accessor.)
|
114
|
+
#
|
115
|
+
# Assignment to +obj+ performs no type checking. Assignment to +sym+ raises a
|
116
|
+
# TypeError unless the object assigned is a +Symbol+ or +nil+. Similarly +ary+
|
117
|
+
# must always be an +Array+ or +nil+. Type checking always allows +nil+ in
|
118
|
+
# addition to the specified type. In each case, the attribute is initialized
|
119
|
+
# to +nil+.
|
120
|
+
#
|
121
|
+
# The referenced Ruby object is marked to protect it from the garbage
|
122
|
+
# collector.
|
123
|
+
#
|
124
|
+
class ObjectAttribute < Attribute
|
125
|
+
|
126
|
+
def ObjectAttribute.match decl
|
127
|
+
decl if decl.is_a? Class
|
128
|
+
end
|
129
|
+
|
130
|
+
def target_class; @class; end
|
131
|
+
|
132
|
+
def initialize(*args)
|
133
|
+
super
|
134
|
+
@class = @match
|
135
|
+
|
136
|
+
@cvar = @var
|
137
|
+
@cdecl = "VALUE #{@cvar}; // #{@class}"
|
138
|
+
|
139
|
+
@init = "shadow->#{@cvar} = Qnil" # otherwise, it's Qfalse == 0
|
140
|
+
@reader = "result = shadow->#{@cvar}"
|
141
|
+
@writer = "shadow->#{@cvar} = arg"
|
142
|
+
@check = @class unless @class == Object
|
143
|
+
@mark = "rb_gc_mark(shadow->#{@cvar})"
|
144
|
+
|
145
|
+
@dump = "rb_ary_push(result, shadow->#{@cvar})"
|
146
|
+
@load = "shadow->#{@cvar} = rb_ary_shift(from_array)"
|
147
|
+
end
|
148
|
+
|
149
|
+
def inspect
|
150
|
+
%{<#{self.class} #{@cvar} => #{@class.inspect}>}
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# There are two kinds of object attributes. Both refer to Ruby objects and can
|
156
|
+
# be typed or untyped. This one is a slightly optimized variation that is
|
157
|
+
# restricted to references to other shadow objects.
|
158
|
+
#
|
159
|
+
# ShadowObjectAttribute is a restricted variant of ObjectAttribute in which
|
160
|
+
# the object referred to must belong to a class that includes CShadow. The
|
161
|
+
# actual pointer is to the shadow struct itself, rather than a +VALUE+. This
|
162
|
+
# difference is transparent to Ruby code. The syntax for this variant differs
|
163
|
+
# only in the use of brackets around the type. For example, using the class
|
164
|
+
# +A+ defined above:
|
165
|
+
#
|
166
|
+
# class B
|
167
|
+
# include CShadow
|
168
|
+
# shadow_attr_accessor :a => [A]
|
169
|
+
#
|
170
|
+
# def initialize
|
171
|
+
# self.a = A.new
|
172
|
+
# a.sym = :something
|
173
|
+
# end
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# Note that a shadow struct always has a +self+ pointer, so a
|
177
|
+
# ShadowObjectAttribute contains essentially the same information as an
|
178
|
+
# ObjectAttribute. It is included for situation in which the efficiency of a
|
179
|
+
# direct reference to the shadow struct is desirable. Note that only
|
180
|
+
# ObjectAttributes can refer to general Ruby objects which may or may not
|
181
|
+
# include the CShadow module.
|
182
|
+
#
|
183
|
+
# The accessors work just as with ObjectAttribute, with type checking
|
184
|
+
# performed by the writer. From Ruby, these two kinds of attributes are
|
185
|
+
# indistinguishable in all respects except their declaration syntax.
|
186
|
+
#
|
187
|
+
# The referenced Ruby object is marked to protect it from the garbage
|
188
|
+
# collector.
|
189
|
+
#
|
190
|
+
class ShadowObjectAttribute < Attribute
|
191
|
+
|
192
|
+
def ShadowObjectAttribute.match decl
|
193
|
+
decl[0] if decl.is_a? Array and decl.size == 1 and decl[0].is_a? Class
|
194
|
+
end
|
195
|
+
|
196
|
+
def target_class; @class; end
|
197
|
+
|
198
|
+
def initialize(*args)
|
199
|
+
super
|
200
|
+
@class = @match
|
201
|
+
|
202
|
+
ssn = @class.shadow_struct.name
|
203
|
+
|
204
|
+
@cvar = @var
|
205
|
+
if @class < CShadow
|
206
|
+
@cdecl = "struct #{ssn} *#{@cvar}"
|
207
|
+
else
|
208
|
+
raise ScriptError, "Class #{@class} doesn't include CShadow."
|
209
|
+
end
|
210
|
+
|
211
|
+
@reader = "result = shadow->#{@cvar} ? shadow->#{@cvar}->self : Qnil"
|
212
|
+
@writer = %{
|
213
|
+
if (arg != Qnil) {
|
214
|
+
Data_Get_Struct(arg, #{ssn}, shadow->#{@cvar});
|
215
|
+
} else
|
216
|
+
shadow->#{@cvar} = 0;
|
217
|
+
}
|
218
|
+
@check = @class unless @class == Object
|
219
|
+
@mark = %{\
|
220
|
+
if (shadow->#{@cvar})
|
221
|
+
rb_gc_mark(shadow->#{@cvar}->self);\
|
222
|
+
}
|
223
|
+
|
224
|
+
@dump =
|
225
|
+
"rb_ary_push(result, shadow->#{@cvar} ? shadow->#{@cvar}->self : 0)"
|
226
|
+
@load = %{
|
227
|
+
tmp = rb_ary_shift(from_array);
|
228
|
+
if (tmp) {
|
229
|
+
Data_Get_Struct(tmp, #{ssn}, shadow->#{@cvar});
|
230
|
+
} else
|
231
|
+
shadow->#{@cvar} = 0;
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
def inspect
|
236
|
+
%{<#{self.class} #{@cvar} => [#{@class.inspect}]>}
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# CNativeAttribute and its subclasses handle all but the two special cases
|
241
|
+
# described above. The general form for declarations of such attributes is:
|
242
|
+
#
|
243
|
+
# shadow_attr_accessor ruby_var => c_declaration
|
244
|
+
#
|
245
|
+
# where +ruby_var+ is the name (symbol or string) which will access the data
|
246
|
+
# from Ruby, and +c_declaration+ is the string used to declare the data. For
|
247
|
+
# example:
|
248
|
+
#
|
249
|
+
# shadow_attr_accessor :x => "double x", :y => "int yyy"
|
250
|
+
#
|
251
|
+
# Note that the symbol and C identifier need not be the same.
|
252
|
+
#
|
253
|
+
# Native attributes fall into two categories: those that embed data within the
|
254
|
+
# struct, and those that point to a separately allocated block. Embedded
|
255
|
+
# attributes are limited in that they are of fixed size. Pointer attributes do
|
256
|
+
# not have this limitation. But programmers should be wary of treating them as
|
257
|
+
# separate objects: the lifespan of the referenced data block is the same as
|
258
|
+
# the lifespan of the Ruby object. If the Ruby object and its shadow are
|
259
|
+
# garbage collected while the data is in use, the data will be freed and no
|
260
|
+
# longer valid.
|
261
|
+
#
|
262
|
+
# When using a separately allocated data block, it is a good practice is to
|
263
|
+
# use "copy" semantics, so that there can be no other references to the data.
|
264
|
+
# See CharPointerAttribute, for example. Reading or writing to such an
|
265
|
+
# attribute has copy semantics, in the following sense. On assignment, the
|
266
|
+
# Ruby string argument is copied into an allocated block; later references to
|
267
|
+
# this attribute generate a new Ruby string which is a copy of that array of
|
268
|
+
# char.
|
269
|
+
#
|
270
|
+
# Some CNativeAttribute classes are included for int, long, double, double *,
|
271
|
+
# char *, etc.
|
272
|
+
#
|
273
|
+
# Uninitialized numeric members are 0. Accessors for uninitialized strings
|
274
|
+
# return +nil+.
|
275
|
+
#
|
276
|
+
class CNativeAttribute < Attribute
|
277
|
+
|
278
|
+
attr_reader :ctype
|
279
|
+
|
280
|
+
@pattern = nil
|
281
|
+
|
282
|
+
def CNativeAttribute.match decl
|
283
|
+
if decl.is_a? String and @pattern and @pattern =~ decl
|
284
|
+
Regexp.last_match
|
285
|
+
else
|
286
|
+
false
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def initialize(*args)
|
291
|
+
super
|
292
|
+
@cdecl = @match[0]
|
293
|
+
@ctype = @match[1]
|
294
|
+
@cvar = @match[2]
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
class IntAttribute < CNativeAttribute
|
300
|
+
@pattern = /\A(int)\s+(\w+)\z/
|
301
|
+
def initialize(*args)
|
302
|
+
super
|
303
|
+
@reader = "result = INT2NUM(shadow->#{@cvar})"
|
304
|
+
@writer = "shadow->#{@cvar} = NUM2INT(arg)" # type check and conversion
|
305
|
+
@dump = "rb_ary_push(result, INT2NUM(shadow->#{@cvar}))"
|
306
|
+
@load = "tmp = rb_ary_shift(from_array); shadow->#{@cvar} = NUM2INT(tmp)"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
# Does not check for overflow.
|
311
|
+
class ShortAttribute < IntAttribute
|
312
|
+
@pattern = /\A(short)\s+(\w+)\z/
|
313
|
+
def initialize(*args)
|
314
|
+
super
|
315
|
+
@reader = "result = INT2NUM(shadow->#{@cvar})"
|
316
|
+
@writer = "shadow->#{@cvar} = NUM2INT(arg)" # type check and conversion
|
317
|
+
@dump = "rb_ary_push(result, INT2NUM(shadow->#{@cvar}))"
|
318
|
+
@load = "tmp = rb_ary_shift(from_array); shadow->#{@cvar} = NUM2INT(tmp)"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
class LongAttribute < CNativeAttribute
|
323
|
+
@pattern = /\A(long)\s+(\w+)\z/
|
324
|
+
def initialize(*args)
|
325
|
+
super
|
326
|
+
@reader = "result = INT2NUM(shadow->#{@cvar})"
|
327
|
+
@writer = "shadow->#{@cvar} = NUM2LONG(arg)" # type check and conversion
|
328
|
+
@dump = "rb_ary_push(result, INT2NUM(shadow->#{@cvar}))"
|
329
|
+
@load = "tmp = rb_ary_shift(from_array); shadow->#{@cvar} = NUM2LONG(tmp)"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
class DoubleAttribute < CNativeAttribute
|
334
|
+
@pattern = /\A(double)\s+(\w+)\z/
|
335
|
+
def initialize(*args)
|
336
|
+
super
|
337
|
+
@reader = "result = rb_float_new(shadow->#{@cvar})"
|
338
|
+
@writer = "shadow->#{@cvar} = NUM2DBL(arg)" # type check and conversion
|
339
|
+
@dump = "rb_ary_push(result, rb_float_new(shadow->#{@cvar}))"
|
340
|
+
@load = "tmp = rb_ary_shift(from_array); shadow->#{@cvar} = NUM2DBL(tmp)"
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
class PointerAttribute < CNativeAttribute
|
345
|
+
@pattern = nil
|
346
|
+
def initialize(*args)
|
347
|
+
super
|
348
|
+
@free = "free(shadow->#{@cvar})"
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Stores a null-terminated string
|
353
|
+
class CharPointerAttribute < PointerAttribute
|
354
|
+
@pattern = /\A(char)\s*\*\s*(\w+)\z/
|
355
|
+
def initialize(*args)
|
356
|
+
super
|
357
|
+
@reader =
|
358
|
+
"result = shadow->#{@cvar} ? rb_str_new2(shadow->#{@cvar}) : Qnil"
|
359
|
+
@writer = %{
|
360
|
+
{
|
361
|
+
int len;
|
362
|
+
char *str;
|
363
|
+
|
364
|
+
free(shadow->#{@cvar});
|
365
|
+
|
366
|
+
if (arg == Qnil)
|
367
|
+
shadow->#{@cvar} = 0;
|
368
|
+
else {
|
369
|
+
StringValueCStr(arg);
|
370
|
+
len = RSTRING(arg)->len;
|
371
|
+
str = RSTRING(arg)->ptr;
|
372
|
+
shadow->#{@cvar} = ALLOC_N(char, len + 1);
|
373
|
+
|
374
|
+
if (str)
|
375
|
+
memcpy(shadow->#{@cvar}, str, len);
|
376
|
+
|
377
|
+
shadow->#{@cvar}[len] = '\\0';
|
378
|
+
}
|
379
|
+
}
|
380
|
+
}
|
381
|
+
@dump = %{
|
382
|
+
rb_ary_push(result,
|
383
|
+
shadow->#{@cvar} ? rb_str_new2(shadow->#{@cvar}) : 0);
|
384
|
+
}
|
385
|
+
@load = %{
|
386
|
+
{
|
387
|
+
VALUE arg = rb_ary_shift(from_array);
|
388
|
+
int len;
|
389
|
+
char *str;
|
390
|
+
|
391
|
+
if (arg == Qnil)
|
392
|
+
shadow->#{@cvar} = 0;
|
393
|
+
else {
|
394
|
+
len = RSTRING(arg)->len;
|
395
|
+
str = RSTRING(arg)->ptr;
|
396
|
+
shadow->#{@cvar} = ALLOC_N(char, len + 1);
|
397
|
+
|
398
|
+
if (str)
|
399
|
+
memcpy(shadow->#{@cvar}, str, len);
|
400
|
+
|
401
|
+
shadow->#{@cvar}[len] = '\\0';
|
402
|
+
}
|
403
|
+
}
|
404
|
+
}
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# can be used for variable length arrays--see examples/matrix.rb.
|
409
|
+
class DoublePointerAttribute < PointerAttribute
|
410
|
+
@pattern = /\A(double)\s*\*\s*(\w+)\z/
|
411
|
+
# can't do anything else in general
|
412
|
+
end
|
413
|
+
|
414
|
+
end
|
data/lib/cgen/cgen.rb
ADDED
@@ -0,0 +1,2041 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'cgen/inherit'
|
3
|
+
|
4
|
+
# ==Overview
|
5
|
+
#
|
6
|
+
# The CGenerator module is a framework for dynamically generating C
|
7
|
+
# extensions. It is a bit like Perl's +inline+ but intended for a different
|
8
|
+
# purpose: managing incremental, structured additions to C source files, and
|
9
|
+
# compiling the code and loading the library just in time for execution. Whereas
|
10
|
+
# +inline+ helps you write a C extension, CGenerator helps you write a Ruby
|
11
|
+
# program that generates C extensions. To put it another way, this is a Ruby
|
12
|
+
# interface to the Ruby C API.
|
13
|
+
#
|
14
|
+
# The original use of CGenerator was as the back end of a compiler for
|
15
|
+
# mathematical expressions in C-like syntax involving limited Ruby
|
16
|
+
# subexpressions. In that case, CGenerator allowed the compiler to think
|
17
|
+
# about the syntax and semantics of the input expressions without having to
|
18
|
+
# worry about the high-level structure of the generated .c and .h files.
|
19
|
+
#
|
20
|
+
# One potential use is quick-turnaround development and testing of C code,
|
21
|
+
# possibly using Ruby as a driver environment; the library under construction
|
22
|
+
# needn't be Ruby-specific. If SWIG didn't support Ruby, this framework could be
|
23
|
+
# the starting point for a program that generates wrapper code for existing
|
24
|
+
# libraries. Finally, a Ruby package that includes C extensions could benefit
|
25
|
+
# from being able to use Ruby code to dynamically specify the contents and
|
26
|
+
# control the build process during installation.
|
27
|
+
#
|
28
|
+
# The CGenerator framework consists of two base classes, Accumulator and
|
29
|
+
# Template. Think of accumulators as blanks in a form and templates as the form
|
30
|
+
# around the blanks, except that accumulators and templates can nest within each
|
31
|
+
# other. The base classes have subclasses which hierarchically decompose the
|
32
|
+
# information managed by the framework. This hierarchy is achieved by
|
33
|
+
# inheritance along the +parent+ attribute, which is secondary to subclass
|
34
|
+
# inheritance.
|
35
|
+
#
|
36
|
+
# ==Templates
|
37
|
+
#
|
38
|
+
# The main template in the CGenerator module is Library. It has accumulators for
|
39
|
+
# such constructs as including header files, declaring variables, declaring Ruby
|
40
|
+
# symbols, declaring classes, defining functions, and defining structs. Some
|
41
|
+
# accumulators, such as those for adding function and struct definitions, return
|
42
|
+
# a new template each time they are called. Those templates, in turn, have their
|
43
|
+
# own accumulators for structure members, function arguments, declarations,
|
44
|
+
# initialization, scope, etc.
|
45
|
+
#
|
46
|
+
# ===Library templates
|
47
|
+
#
|
48
|
+
# A Library corresponds to one main C source file and one shared C library (.so
|
49
|
+
# or .dll). It manages the +Init_library+ code (including registration of
|
50
|
+
# methods), as well as user-specified declaration and initialization in the
|
51
|
+
# scope of the .c file and its corresponding .h file. All files generated in the
|
52
|
+
# process of building the library are kept in a directory with the same name as
|
53
|
+
# the library. Additional C files in this directory will be compiled and linked
|
54
|
+
# to the library.
|
55
|
+
#
|
56
|
+
# Each library is the root of a template containment hierarchy, and it alone has
|
57
|
+
# a #commit method. After client code has sent all desired fragments to the
|
58
|
+
# accumulators, calling the commit method uses the structure imposed by the
|
59
|
+
# sub-templates of the library to joins the fragments into two strings, one for
|
60
|
+
# the .h file and one for the .c file. Then each string is written to the
|
61
|
+
# corresponding file (only if the string is different from the current file
|
62
|
+
# contents), and the library is compiled (if necessary) and loaded.
|
63
|
+
#
|
64
|
+
# ===Function templates
|
65
|
+
#
|
66
|
+
# Function templates are used to define the functions in a Library. The base
|
67
|
+
# class, CGenerator::Function, generates a function (by default, static) in the
|
68
|
+
# library without registering it with Ruby in any way.
|
69
|
+
#
|
70
|
+
# The CGenerator::RubyFunction templates define the function as above and also
|
71
|
+
# register the function as an instance method, module function, or singleton
|
72
|
+
# method of a specified class or module, or as a global function (a private
|
73
|
+
# method of +Kernel+).
|
74
|
+
#
|
75
|
+
# Client code does not instantiate these templates directly, but instead uses
|
76
|
+
# the library's #define accumulator methods, which return the new template.
|
77
|
+
#
|
78
|
+
# The function template for the library's initialization function can be
|
79
|
+
# accessed using the library's #init_library_function method, although direct
|
80
|
+
# access to this template is typically not needed. (Use the library's #setup
|
81
|
+
# method to write code to the #init_library_function.)
|
82
|
+
#
|
83
|
+
# ===Struct templates
|
84
|
+
#
|
85
|
+
# A struct template generates a typedef for a C struct. It can be external, in
|
86
|
+
# which case it is written to the .h file. It has a #declare accumulator for
|
87
|
+
# adding data members.
|
88
|
+
#
|
89
|
+
# ==Accumulators
|
90
|
+
#
|
91
|
+
# Accumulators are a way of defining a hierarchical structure and populating it
|
92
|
+
# with data in such a way that the data can be serialized to a string at any
|
93
|
+
# point during the process without side effects. Templates are Accumulators
|
94
|
+
# which contain other accumulators and have convenience methods for accessing
|
95
|
+
# them from client code.
|
96
|
+
#
|
97
|
+
# Accumulators can be fairly unstructured--they just accumulate in sequence
|
98
|
+
# whatever is sent to them, possibly with some filtering, which may include
|
99
|
+
# other accumulators. Templates are usually more more structured. In general,
|
100
|
+
# only Templates can be parents; other accumulators set the #parent of each
|
101
|
+
# accumulated item to be the accumulator's #parent, simplifying the #parent
|
102
|
+
# hierarchy.
|
103
|
+
#
|
104
|
+
# Accumulators are responsible for the format of each accumulated item, for
|
105
|
+
# joining the items to form a string when requested to do so, and for doing any
|
106
|
+
# necessary preprocessing on the items (e.g., discarding duplicates).
|
107
|
+
#
|
108
|
+
# From the point of view of client code, accumulators are methods for "filling
|
109
|
+
# in the blanks" in templates. Client code doesn't access the accumulator object
|
110
|
+
# directly, only through a method on the template. For example:
|
111
|
+
#
|
112
|
+
# lib.declare :global_int_array =>
|
113
|
+
# 'int global_int_array[100]',
|
114
|
+
# :name =>
|
115
|
+
# 'char *name'
|
116
|
+
#
|
117
|
+
# is used to access the "declare" accumulator of the library (which is actually
|
118
|
+
# delegated to a file template).
|
119
|
+
#
|
120
|
+
# Providing a key for each declaration (in the example, the keys are symbols,
|
121
|
+
# but they can be any hash keys) helps CGenerator reject repeated declarations.
|
122
|
+
# (Redundancy checking by simple string comparison is inadequate, because it
|
123
|
+
# would allow two declarations of different types, but the same name, or two
|
124
|
+
# declarations with insignificant whitespace differences.)
|
125
|
+
#
|
126
|
+
# The basic Accumulator class adds fragments to an array in sequence. When
|
127
|
+
# converted to a string with #to_s, it joins the fragments with newline
|
128
|
+
# separators. These behaviors change as needed in the subclasses. <b>Note that
|
129
|
+
# the accumulated items need not all be strings, they need only respond to
|
130
|
+
# +to_s+.</b>
|
131
|
+
#
|
132
|
+
# Return values of accumulators are not very consistent: in general, an
|
133
|
+
# accumulator returns whatever is needed for the caller to continue working with
|
134
|
+
# the thing that was just accumulated. It might be a template which supports
|
135
|
+
# some other accumulators, or it might be a string which can be inserted in C
|
136
|
+
# code.
|
137
|
+
#
|
138
|
+
# Some accumulators take existing Ruby objects as an argument. These
|
139
|
+
# accumulators typically return, as a Ruby symbol, the C identifier that has
|
140
|
+
# been defined or declared to refer to that Ruby object. This can be
|
141
|
+
# interpolated into C code to refer to the Ruby object from C.
|
142
|
+
#
|
143
|
+
# <b>Note about argument order:</b> Since hashes are unordered, passing a hash
|
144
|
+
# of key-value pairs to #declare or similar methods will not preserve the
|
145
|
+
# textual ordering. Internally, cgen sorts this hash into an array of pairs so
|
146
|
+
# that at least the result is deterministic, reducing recompilation. One can
|
147
|
+
# force an argument order by using an array of pairs.
|
148
|
+
#
|
149
|
+
# lib.declare [[:global_int_array,
|
150
|
+
# 'int global_int_array[100]'],
|
151
|
+
# [:name =>
|
152
|
+
# 'char *name']
|
153
|
+
#
|
154
|
+
# Alternately, simply break the declaration into multiple declares.
|
155
|
+
#
|
156
|
+
# ==C code output
|
157
|
+
#
|
158
|
+
# ===Format
|
159
|
+
#
|
160
|
+
# Some effort is made to generate readable code. Relative tabbing within code
|
161
|
+
# fragments is preserved. One goal of CGenerator is producing Ruby extensions
|
162
|
+
# that can be saved and distributed with little or no modification (as opposed
|
163
|
+
# to just created and loaded on the fly).
|
164
|
+
#
|
165
|
+
# ===Use of C identifiers
|
166
|
+
#
|
167
|
+
# CGenerator attempts to generate C identifiers in non-conflicting ways...
|
168
|
+
# (prove some nice property)
|
169
|
+
#
|
170
|
+
# ==Usage
|
171
|
+
#
|
172
|
+
# Create a library with:
|
173
|
+
#
|
174
|
+
# lib = CGenerator::Library.new "my_lib_name"
|
175
|
+
#
|
176
|
+
# The name must be an identifier: +/[A-Za-z0-9_]*/+.
|
177
|
+
#
|
178
|
+
# It is useful to keep a reference to +lib+ around to send define and declare
|
179
|
+
# messages to.
|
180
|
+
#
|
181
|
+
# ===Templates
|
182
|
+
#
|
183
|
+
# All templates respond to #library and #file methods, which return the library
|
184
|
+
# or file object which contains the template. (The library itself does not
|
185
|
+
# respond to #file.) They also respond to #name and #parent.
|
186
|
+
#
|
187
|
+
# ===Library
|
188
|
+
#
|
189
|
+
# ---Library#use_work_dir dir_name
|
190
|
+
#
|
191
|
+
# Changes into +dir_name+, creating it first if necessary. Does nothing if
|
192
|
+
# alread in a diredctory of that name. Often used with +"tmp"+.
|
193
|
+
#
|
194
|
+
# ---Library#commit
|
195
|
+
#
|
196
|
+
# Writes the files to disk, and makes and loads the library.
|
197
|
+
#
|
198
|
+
# Note that #commit must be called after all C code definitions for the library,
|
199
|
+
# but before instantiation of any objects that use those definitions. If a
|
200
|
+
# definition occurs after commit, or if instantiation occurs before commit, then
|
201
|
+
# a CGenerator::Library::CommitError is raised, with an appropriate message.
|
202
|
+
# Sometimes, this forces you to use many small libraries, each committed just in
|
203
|
+
# time for use. See examples/fixed-array.rb.
|
204
|
+
#
|
205
|
+
# ---Library#committed?
|
206
|
+
#
|
207
|
+
# True if the library has been committed.
|
208
|
+
#
|
209
|
+
# ---Library#before_commit(&block)
|
210
|
+
# ---Library#after_commit(&block)
|
211
|
+
#
|
212
|
+
# Schedules block to run before or after Library#commit. The before blocks are
|
213
|
+
# run in the same order in which they were scheduled; the after blocks run in
|
214
|
+
# the reverse order (analogously with +BEGIN+/+END+). Each block is evaluated in
|
215
|
+
# the context in which it was created (instance_eval is *not* used), and it is
|
216
|
+
# passed the library as an argument.
|
217
|
+
#
|
218
|
+
# ---Library#empty?
|
219
|
+
#
|
220
|
+
# True if no content has been added to the library.
|
221
|
+
#
|
222
|
+
# ---Library#add_file name
|
223
|
+
#
|
224
|
+
# Creates templates for two files, a source (.c) file and an include (.h) file
|
225
|
+
# that will be generated in the same dir as the library. The base file name is
|
226
|
+
# taken from the argument. Returns an array containing the include file template
|
227
|
+
# and the source file template, in that order.
|
228
|
+
#
|
229
|
+
# Functions can be added to the source file by calling #define_method and
|
230
|
+
# similar methods on the source file template. Their +rb_init+ calls are done in
|
231
|
+
# #init_library_function in the main library source file. The new source file
|
232
|
+
# automatically #includes the library's main header file, as well as its own
|
233
|
+
# header file, and the library's main source file also #includes the new header
|
234
|
+
# file. Declarations can be added to the header file by calling #declare on it,
|
235
|
+
# but in many cases this is taken care of automatically.
|
236
|
+
#
|
237
|
+
# ---Library#extconf
|
238
|
+
#
|
239
|
+
# Override #extconf if you want to do more than just #create_makefile. Note that
|
240
|
+
# #create_makefile recognizes all .c files in the library directory, and
|
241
|
+
# generates a makefile that compiles them and links them into the dynamic
|
242
|
+
# library.
|
243
|
+
#
|
244
|
+
# ---Library#write
|
245
|
+
# ---Library#makedepend
|
246
|
+
# ---Library#mkmf
|
247
|
+
# ---Library#make arg = nil
|
248
|
+
#
|
249
|
+
# Internal methods called, in sequence, by #commit:
|
250
|
+
#
|
251
|
+
# * #write dumps each file template to disk, if needed
|
252
|
+
# * #makedepend executes +makedepend+
|
253
|
+
# * #mkmf calls Library#extconf
|
254
|
+
# * #make executes the system's +make+ program.
|
255
|
+
#
|
256
|
+
# These methods can be overridden, but are more typically called directly. The
|
257
|
+
# argument to #make is interpolated into the system call as a command line
|
258
|
+
# argument to the +make+ program. If the argument is 'clean' or 'distclean' then
|
259
|
+
# the make log is deleted; if the argument is 'distclean' then all .c and .h
|
260
|
+
# files generated by #write are deleted (additional user-supplied .c and .h
|
261
|
+
# files in the library dir are not affected).
|
262
|
+
#
|
263
|
+
# ---Library#update_file f, template
|
264
|
+
#
|
265
|
+
# Called by write on each .c and .h file to actually write +template+ to the
|
266
|
+
# open file +f+. The default behavior is to compare the existing data with the
|
267
|
+
# generated data, and leave the file untouched if nothing changed. Subclasses
|
268
|
+
# may have more efficient ways of doing this. (For instance, check a version
|
269
|
+
# indicator in the file on disk, perhaps stored using the file's preamble
|
270
|
+
# accumulator. It is even possible to defer some entries in the template until
|
271
|
+
# after this check has been made: code that only needs to be regenerated if some
|
272
|
+
# specification has changed)
|
273
|
+
#
|
274
|
+
# ---Library#purge_source_dir
|
275
|
+
# ---Library#purge_source_dir= flag
|
276
|
+
#
|
277
|
+
# Access the #purge_source_dir attribute of a library, which controls what
|
278
|
+
# happens to .c, .h, and .o files in the source dir of the library that are not
|
279
|
+
# among those generated as part of the library. If this is set to +:delete+,
|
280
|
+
# then those files are deleted. Other true values cause the .c, .h, and .o files
|
281
|
+
# to be renamed with the .hide extension. (Note that this makes it difficult to
|
282
|
+
# keep manually written C files in the same dir.) False +flag+ values (the
|
283
|
+
# default) cause CGen to leave the files untouched.
|
284
|
+
#
|
285
|
+
# Note that, regardless of this setting, #mkmf will construct a Makefile which
|
286
|
+
# lists all .c files that are in the source dir. If you do not delete obsolete
|
287
|
+
# files, they will be compiled into your library!
|
288
|
+
#
|
289
|
+
# ---Library#init_library_function
|
290
|
+
#
|
291
|
+
# Returns a Function template object; see below. This function is called when
|
292
|
+
# the library is loaded. Method definitions put stuff here to register methods
|
293
|
+
# with Ruby. Usually, there is no need to bother this guy directly. Use
|
294
|
+
# Library#setup instead.
|
295
|
+
#
|
296
|
+
# ---Library#setup key => "statements", ...
|
297
|
+
#
|
298
|
+
# Inserts code in the #init_library_function, which is called when the library
|
299
|
+
# is loaded. The +key+ is used for redundancy checking, as in the #declare
|
300
|
+
# accumulators. Note that hashes are unordered, so constructs like
|
301
|
+
#
|
302
|
+
# setup :x => "...", :y => "..."
|
303
|
+
#
|
304
|
+
# can result in unpredictable order. To avoid this, use several #setup calls.
|
305
|
+
#
|
306
|
+
# ---Library#source_file ---Library#include_file
|
307
|
+
#
|
308
|
+
# Returns the template for the main source or include file of the library.
|
309
|
+
# Usually, there is no need to access these directly.
|
310
|
+
#
|
311
|
+
# ---Library#define_c_function name, type
|
312
|
+
#
|
313
|
+
# Defines a plain ol' C function. Returns a Function template (see below), or a
|
314
|
+
# template of the specified +type+, if given.
|
315
|
+
#
|
316
|
+
# ---Library#define_c_method mod, name, subclass
|
317
|
+
# ---Library#define_c_module_function mod, name, subclass
|
318
|
+
# ---Library#define_c_global_function name, subclass
|
319
|
+
# ---Library#define_c_singleton_method mod, name, subclass
|
320
|
+
# ---Library#define_c_class_method mod, name, subclass
|
321
|
+
#
|
322
|
+
# Defines a function of the specified name and type in the given class/module
|
323
|
+
# (or in the global scope), and returns the function template (often used with
|
324
|
+
# #instance_eval to add arguments, code, etc.). The +subclass+ argument is
|
325
|
+
# optional and allows the template to belong to a subclass of the function
|
326
|
+
# template it would normally belong to.
|
327
|
+
#
|
328
|
+
# For example,
|
329
|
+
#
|
330
|
+
# define_c_method String, "reverse"
|
331
|
+
#
|
332
|
+
# The arguments accepted by the method automatically include +self+. By default,
|
333
|
+
# arguments are passed as individual C arguments, but the can be passed in a
|
334
|
+
# Ruby or C array. The latter has the advantage of argument parsing (based on
|
335
|
+
# rb_scan_args), defaults, and typechecking. See Method#c_array_args.
|
336
|
+
# #define_c_class_method is just an alias for #define_c_singleton_method.
|
337
|
+
#
|
338
|
+
# ---Library#include "file1.h", "<file2.h>", ...
|
339
|
+
#
|
340
|
+
# Insert the include statement(s) at the top of the library's main .c file. For
|
341
|
+
# convenience, <ruby.h> is included automatically, as is the header file of the
|
342
|
+
# library itself.
|
343
|
+
#
|
344
|
+
# ---Library#declare :x => "int x", ... ---Library#declare_extern :x => "int x",
|
345
|
+
# ...
|
346
|
+
#
|
347
|
+
# Puts the string in the declaration area of the .c or .h file, respectively.
|
348
|
+
# The declaration area is before the function definitions, and after the
|
349
|
+
# structure declarations.
|
350
|
+
#
|
351
|
+
# ---Library#declare_struct name, attributes=nil
|
352
|
+
# ---Library#declare_extern_struct name, attributes=nil
|
353
|
+
#
|
354
|
+
# Returns a Structure template, which generates to a typedefed C struct in the
|
355
|
+
# .c or .h file. The #declare method of this template is used to add members.
|
356
|
+
#
|
357
|
+
# ---Library#declare_class cl ---Library#declare_module mod
|
358
|
+
# ---Library#declare_symbol sym
|
359
|
+
#
|
360
|
+
# Define a C variable which will be initialized to refer to the class, module,
|
361
|
+
# or symbol. These accumulators return the name of the C variable which will be
|
362
|
+
# generated and initialized to the ID of the symbol, and this return value can
|
363
|
+
# be interpolated into C calls to the Ruby API. (The arguments are the actual
|
364
|
+
# Ruby objects.) This is very useful in #rb_ivar_get/#rb_ivar_set calls, and it
|
365
|
+
# avoids doing the lookup more than once:
|
366
|
+
#
|
367
|
+
# ...
|
368
|
+
# declare :my_ivar => "VALUE my_ivar"
|
369
|
+
# body %{
|
370
|
+
# my_ivar = rb_ivar_get(shadow->self, #{declare_symbol :@my_ivar});
|
371
|
+
# rb_ivar_set(shadow->self, #{declare_symbol :@my_ivar}, Qnil);
|
372
|
+
# }
|
373
|
+
#
|
374
|
+
# The second declaration notices that the library already has a variable that
|
375
|
+
# will be initialized to the ID of the symbol, and uses it.
|
376
|
+
#
|
377
|
+
# ---Library#literal_symbol sym
|
378
|
+
#
|
379
|
+
# Like Library#declare_symbol, but converts the ID to a VALUE at library
|
380
|
+
# initialization time. Useful for looking up hash values keyed by symbol
|
381
|
+
# objects, for example. +sym+ is a string or symbol.
|
382
|
+
#
|
383
|
+
# ---Library#show_times message
|
384
|
+
#
|
385
|
+
# If the attribute #show_times_flag is set to true, print the user and system
|
386
|
+
# times (and child user and child system on some platforms) and real time for
|
387
|
+
# each major step of the commit process. Display +message+.
|
388
|
+
#
|
389
|
+
# ===File
|
390
|
+
#
|
391
|
+
# File templates are managed by the Library, and most users do not need to
|
392
|
+
# interact with them directly. They are structured into four sections: includes,
|
393
|
+
# structure declarations, variable and function declarations, and function
|
394
|
+
# definitions. Each source file automatically includes its corresponding header
|
395
|
+
# file and the main header file for the library (which includes ruby.h). The
|
396
|
+
# main source file for the library includes each additional header file.
|
397
|
+
#
|
398
|
+
# ---File#define_c_method
|
399
|
+
# ---File#define_c_module_function
|
400
|
+
# ---File#define_c_global_function
|
401
|
+
# ---File#define_c_singleton_method
|
402
|
+
#
|
403
|
+
# As for the Library, but can be used on any source file within the library.
|
404
|
+
# Used to break large projects up into many files.
|
405
|
+
#
|
406
|
+
# ---File#preamble
|
407
|
+
#
|
408
|
+
# An accumulator that wraps its input in C comments and places it at the head of
|
409
|
+
# the source file.
|
410
|
+
#
|
411
|
+
# ===Function
|
412
|
+
#
|
413
|
+
# ---Funtion#scope :static ---Funtion#scope :extern ---Funtion#arguments 'int
|
414
|
+
# x', 'double y', 'VALUE obj', ... ---Funtion#return_type 'void'
|
415
|
+
#
|
416
|
+
# These accumulators affect the prototype of the function, which will be placed
|
417
|
+
# in the declaration section of either the .h or the .c file, depending on the
|
418
|
+
# scope setting. The default scope is static. The default return type is 'void'.
|
419
|
+
#
|
420
|
+
# For the Method subclasses of Function, argument and return types can be
|
421
|
+
# omitted, in which case they default to 'VALUE'.
|
422
|
+
#
|
423
|
+
# ---Funtion#declare :x => "static double x", ... ---Funtion#init "x = 0", ...
|
424
|
+
# ---Funtion#setup 'x' => "x += 1", ... ---Funtion#body 'y = sin(x);
|
425
|
+
# printf("%d\n", y)', ...
|
426
|
+
#
|
427
|
+
# These four accumulators determine the contents of the function between the
|
428
|
+
# opening and closing braces. The #init code is executed once when the function
|
429
|
+
# first runs; it's useful for initializing static data. The #setup code runs
|
430
|
+
# each time the function is called, as does the #body. Distinguishing #setup
|
431
|
+
# from #body is useful for two reasons: first, #setup is guaranteed to execute
|
432
|
+
# before #body, and, second, one can avoid setting up the same variable twice,
|
433
|
+
# because of the key.
|
434
|
+
#
|
435
|
+
# ---Funtion#returns "2*x"
|
436
|
+
#
|
437
|
+
# Specifies the string used in the final return statement of the function.
|
438
|
+
# Subsequent uses of this method clobber the previous value. Alternately, one
|
439
|
+
# can simply insert a "return" manually in the body.
|
440
|
+
#
|
441
|
+
# ===Method ===ModuleFunction ===GlobalFunction ===SingletonMethod
|
442
|
+
#
|
443
|
+
# These subclasses of the Function template are designed for coding Ruby
|
444
|
+
# methods. The necessary registration (+rb_define_method+, etc.) is handled
|
445
|
+
# automatically. Defaults are different from Function: +'VALUE self'+ is
|
446
|
+
# automatically an argument, and argument and return types are assumed to be
|
447
|
+
# +'VALUE'+ and can be omitted by the caller. The return value is +nil+ by
|
448
|
+
# default.
|
449
|
+
#
|
450
|
+
# ---Method#arguments :arg1, :arg2, ...
|
451
|
+
#
|
452
|
+
# The default way of specifying arguments. Allows a fixed number of VALUE
|
453
|
+
# arguments.
|
454
|
+
#
|
455
|
+
# ---Method#c_array_args argc_name = 'argc', argv_name = 'argv', &block
|
456
|
+
# ---Method#rb_array_args args_name = 'args'
|
457
|
+
#
|
458
|
+
# Specifies that arguments are to be collected and passed in a C or Ruby array,
|
459
|
+
# instead of individually (which is the default). In each case, the array of
|
460
|
+
# actual arguments will be bound to a C parameter with the name specified. See
|
461
|
+
# the Ruby API documentation for details.
|
462
|
+
#
|
463
|
+
# If a block is given to Method#c_array_args, it will be used to specify a call
|
464
|
+
# to the API function +rb_scan_args+ and to declare the associated variables.
|
465
|
+
# For example:
|
466
|
+
#
|
467
|
+
# c_array_args('argc', 'argv') {
|
468
|
+
# required :arg0, :arg1
|
469
|
+
# optional :arg2, :arg3, :arg4
|
470
|
+
# rest :rest
|
471
|
+
# block :block
|
472
|
+
# }
|
473
|
+
#
|
474
|
+
# declares all the listed symbols as variables of type +VALUE+ in function
|
475
|
+
# scope, and arranges for the following to be called in the #setup clause (i.e.,
|
476
|
+
# before the #body):
|
477
|
+
#
|
478
|
+
# rb_scan_args(argc, argv, "23*&", &arg0, &arg1, &arg2, &arg3, &arg4, &rest, &block);
|
479
|
+
#
|
480
|
+
# The <tt>'argc', 'argv'</tt> are the default values and are usually omitted.
|
481
|
+
#
|
482
|
+
# The lines in the block can occur in any order, and any line can be omitted.
|
483
|
+
# However, only one line of each kind should be used. In addition, each optional
|
484
|
+
# argument can be associated with a fragment of C code that will be executed to
|
485
|
+
# assign it a default value, if needed. For example, one can add the following
|
486
|
+
# lines to the above block:
|
487
|
+
#
|
488
|
+
# default :arg3 => "INT2NUM(7)",
|
489
|
+
# :arg4 => "INT2NUM(NUM2INT(arg2) + NUM2INT(arg3))"
|
490
|
+
#
|
491
|
+
# Otherwise, optional arguments are assigned nil.
|
492
|
+
#
|
493
|
+
# In this case, if +arg4+ is not provided by +argv+, then it is initialized
|
494
|
+
# using the code given. If, in addition, +arg3+ is not provided, then it too is
|
495
|
+
# initialized. These initializations happen in the #setup clause of the Function
|
496
|
+
# template and are executed in the same order as the arguments are given in the
|
497
|
+
# +optional+ line.
|
498
|
+
#
|
499
|
+
# Finally, argument types can be checked automatically:
|
500
|
+
#
|
501
|
+
# typecheck :arg2 => Numeric, :arg3 => Numeric
|
502
|
+
#
|
503
|
+
# The value passed to the function must either be +nil+ or match the type. Note
|
504
|
+
# that type checking happens *before* default assignment, so that default
|
505
|
+
# calculation code can assume types are correct. No typechecking code is
|
506
|
+
# generated if the type is Object.
|
507
|
+
#
|
508
|
+
# ===Structure
|
509
|
+
#
|
510
|
+
# ---Structure#declare :x => "int x"
|
511
|
+
#
|
512
|
+
# Adds the specified string to define a structure member.
|
513
|
+
#
|
514
|
+
# ===Utility functions
|
515
|
+
#
|
516
|
+
# ---CGenerator.make_c_name s
|
517
|
+
#
|
518
|
+
# Geenrates a unique C itentifier from the given Ruby identifier, which may
|
519
|
+
# include +/[@$?!]/+, +'::'+, and even +'.'+. (Some special globals are not yet
|
520
|
+
# supported: +$:+ and +$-I+, for example.)
|
521
|
+
#
|
522
|
+
# It is unique in the sense that distinct Ruby identifiers map to distinct C
|
523
|
+
# identifiers. (Not completely checked. Might fail for some really obscure
|
524
|
+
# cases.)
|
525
|
+
#
|
526
|
+
# ---String.tab n
|
527
|
+
#
|
528
|
+
# Tabs left or right by n chars, using spaces.
|
529
|
+
#
|
530
|
+
# ---String.tabto n
|
531
|
+
#
|
532
|
+
# The first non-empty line is adjusted to have n spaces before the first
|
533
|
+
# nonspace. Additional lines are changed to preserve relative tabbing.
|
534
|
+
#
|
535
|
+
# ---String.taballto n
|
536
|
+
#
|
537
|
+
# Aligns each line to have n spaces before the first non-space.
|
538
|
+
#
|
539
|
+
# (These routines probably don't work well, if at all, with "hard" tabs.)
|
540
|
+
#
|
541
|
+
# ==Example
|
542
|
+
#
|
543
|
+
# require 'cgen'
|
544
|
+
#
|
545
|
+
# lib = CGenerator::Library.new "sample_lib"
|
546
|
+
#
|
547
|
+
# class Point; end
|
548
|
+
#
|
549
|
+
# lib.declare_extern_struct(:point).instance_eval {
|
550
|
+
# # make it extern so we can see it from another lib
|
551
|
+
# declare :x => "double x"
|
552
|
+
# declare :y => "double y"
|
553
|
+
# }
|
554
|
+
#
|
555
|
+
# lib.define_c_global_function(:new_point).instance_eval {
|
556
|
+
# arguments "x", "y" # 'VALUE' is assumed
|
557
|
+
# declare :p => "point *p"
|
558
|
+
# declare :result => "VALUE result"
|
559
|
+
# # semicolons are added automatically
|
560
|
+
# body %{
|
561
|
+
# result = Data_Make_Struct(#{lib.declare_class Point}, point, 0, free, p);
|
562
|
+
# p->x = NUM2DBL(x);
|
563
|
+
# p->y = NUM2DBL(y);
|
564
|
+
#
|
565
|
+
# // might want to do something like this, too:
|
566
|
+
# // rb_funcall(result, #{lib.declare_symbol :initialize}, 0);
|
567
|
+
# }
|
568
|
+
# returns "result"
|
569
|
+
# # can put a return statement in the body, if preferred
|
570
|
+
# }
|
571
|
+
#
|
572
|
+
# for var in [:x, :y] # metaprogramming in C!
|
573
|
+
# lib.define_c_method(Point, var).instance_eval {
|
574
|
+
# declare :p => "point *p"
|
575
|
+
# body %{
|
576
|
+
# Data_Get_Struct(self, point, p);
|
577
|
+
# }
|
578
|
+
# returns "rb_float_new(p->#{var})"
|
579
|
+
# }
|
580
|
+
# end
|
581
|
+
#
|
582
|
+
# # A utility function, available to other C files
|
583
|
+
# lib.define_c_function("distance").instance_eval {
|
584
|
+
# arguments "point *p1", "point *p2"
|
585
|
+
# return_type "double"
|
586
|
+
# scope :extern
|
587
|
+
# returns "sqrt(pow(p1->x - p2->x, 2) + pow(p1->y - p2->y, 2))"
|
588
|
+
# include "<math.h>"
|
589
|
+
# # The include accumulator call propagates up the parent
|
590
|
+
# # hierarchy until something handles it. In this case,
|
591
|
+
# # the Library lib handles it by adding an include
|
592
|
+
# # directive to the .c file. This allows related, but
|
593
|
+
# # separate aspects of the C source to be handled in
|
594
|
+
# # the same place in the Ruby code. We could also have
|
595
|
+
# # called include directly on lib.
|
596
|
+
# }
|
597
|
+
#
|
598
|
+
# lib.define_c_method(Point, :distance).instance_eval {
|
599
|
+
# # no name conflict between this "distance" and the previous one,
|
600
|
+
# # because "method" and "Point" are both part of the C identifier
|
601
|
+
# # for this method
|
602
|
+
# arguments "other"
|
603
|
+
# declare :p => "point *p"
|
604
|
+
# declare :q => "point *q"
|
605
|
+
# body %{
|
606
|
+
# Data_Get_Struct(self, point, p);
|
607
|
+
# Data_Get_Struct(other, point, q);
|
608
|
+
# }
|
609
|
+
# returns "rb_float_new(distance(p, q))"
|
610
|
+
# }
|
611
|
+
#
|
612
|
+
# lib.commit # now you can use the new definitions
|
613
|
+
#
|
614
|
+
# p1 = new_point(1, 2)
|
615
|
+
# puts "p1: x is #{p1.x}, y is #{p1.y}"
|
616
|
+
#
|
617
|
+
# p2 = new_point(5, 8)
|
618
|
+
# puts "p2: x is #{p2.x}, y is #{p2.y}"
|
619
|
+
#
|
620
|
+
# puts "distance from p1 to p2 is #{p1.distance p2}"
|
621
|
+
#
|
622
|
+
# Output is:
|
623
|
+
#
|
624
|
+
# p1: x is 1.0, y is 2.0
|
625
|
+
# p2: x is 5.0, y is 8.0
|
626
|
+
# distance from p1 to p2 is 7.211102551
|
627
|
+
#
|
628
|
+
# That's a lot of code to do a simple operation, compared with an Inline-style
|
629
|
+
# construct. CGenerator's value shows up with more complex tasks. The
|
630
|
+
# +sample.rb+ file extends this example.
|
631
|
+
#
|
632
|
+
# ==Notes
|
633
|
+
#
|
634
|
+
# * My first Ruby extension was built with this module. That speaks well of the
|
635
|
+
# elegance, simplicity, and utter coolness of Ruby and its extension
|
636
|
+
# architecture. Thanks matz!
|
637
|
+
#
|
638
|
+
# * Some accumulators, like declare_symbol and declare_class, operate by default
|
639
|
+
# on the file scope, even if called on a method definition, so the declarations
|
640
|
+
# are shared across the library. This reduces redundancy with no disadvantage.
|
641
|
+
# (In general, accumulator calls propagate first thru the inheritance hierarchy
|
642
|
+
# and then thru the parent Template hierarchy.)
|
643
|
+
#
|
644
|
+
# * Note that accumulators can nest within accumulators, because #to_s is
|
645
|
+
# applied recursively. This is *very* useful (see Library#initialize for
|
646
|
+
# example). This defines a many-to-one dataflow pattern among accumulators. A
|
647
|
+
# one-to-many dataflow pattern arises when a method calls several accumulators,
|
648
|
+
# as in #define_c_method and kin.
|
649
|
+
#
|
650
|
+
# * CGenerator makes no attempt to check for C syntax errors in code supplied to
|
651
|
+
# the accumulators.
|
652
|
+
#
|
653
|
+
# * It may help to think of templates as heterogeneous collections, like
|
654
|
+
# structs, and accumulators as homogeneous collections, like arrays.
|
655
|
+
#
|
656
|
+
# * The containment hierarchy is represented by the #parent accessor in
|
657
|
+
# Accumulators and Templates. It provides a secondary inheritance of calls to
|
658
|
+
# accumulators. (As a result, doing #include at the function level adds an
|
659
|
+
# #include directive at the file level.)
|
660
|
+
#
|
661
|
+
# * The basic Template and Accumulator class are more general than C source, or
|
662
|
+
# even strings. The Module#inherit method is also reusable.
|
663
|
+
#
|
664
|
+
# * CGenerator does not allow more than one C function with the same name. This
|
665
|
+
# could be changed fairly easily.
|
666
|
+
#
|
667
|
+
# * You can subclass Library and override #extconf to do more complex processing
|
668
|
+
# than just #create_makefile.
|
669
|
+
#
|
670
|
+
# * Calling a #to_s method on an accumulator more than once has no unexpected
|
671
|
+
# side effects. It can be called at any time for a snapshot of the whole library
|
672
|
+
# or of a subtemplate.
|
673
|
+
#
|
674
|
+
# * CGenerator is probably not very efficient, so it may not be useful with
|
675
|
+
# large amounts of C code.
|
676
|
+
#
|
677
|
+
# * Library#commit will try to commit even if already comitted (in which case it
|
678
|
+
# raises a CommitError) or if the lib is empty. Use #committed? and #empty? to
|
679
|
+
# check for these cases. (Should these checks, or just the latter, be
|
680
|
+
# automatic?)
|
681
|
+
#
|
682
|
+
# * Library#commit first reads the .c and .h file and checks for changes. If
|
683
|
+
# there are none, it doesn't write to the file. If neither file gets written to,
|
684
|
+
# make won't need to compile them...
|
685
|
+
#
|
686
|
+
# * CGenerator generates header file entries for any non-static functions or
|
687
|
+
# data. This can be used for communication between files in the library without
|
688
|
+
# using Ruby calls, and to provide an API for other C libraries and executables.
|
689
|
+
#
|
690
|
+
# * Accumulator#inspect is a nice hierarchy-aware inspector.
|
691
|
+
#
|
692
|
+
# ==To do
|
693
|
+
#
|
694
|
+
# * Automatically generate inner and outer functions as in Michael Neumann's
|
695
|
+
# cplusruby. Similarly, should there be another field to refer to a struct of
|
696
|
+
# function pointers, so that C code can call the inner functions without
|
697
|
+
# funcall?
|
698
|
+
#
|
699
|
+
# * Try CFLAGS for optimization: with gcc, -march, -O3, -fomit-frame-pointer
|
700
|
+
#
|
701
|
+
# * Rename to something less generic (cgen --> "sagehen"?)
|
702
|
+
#
|
703
|
+
# * Option to target ruby/ext dir: generate MANIFEST file, but no Makefile. What
|
704
|
+
# about depend? how to generate it in installation-independent format?
|
705
|
+
#
|
706
|
+
# * Let user set dir to build in, rather than rely on chdir, which is not thread
|
707
|
+
# safe.
|
708
|
+
#
|
709
|
+
# * Instead of using an external program to makedepend, do it manually based on
|
710
|
+
# include operations? (Might have to do this anyway on mswin.)
|
711
|
+
#
|
712
|
+
# * Investigate Tiny C--Linux only, but fast, and libtcc allows dynamic codegen.
|
713
|
+
# Maybe best used in "develop" mode, rather than for production code (no -O).
|
714
|
+
#
|
715
|
+
# * Option in define_c_method to make method private/protected (see
|
716
|
+
# rb_define_private/protected_method in intern.h).
|
717
|
+
#
|
718
|
+
# * Optimization: declare_symbol and declare_module should do less work if the
|
719
|
+
# declaration has already neen done.
|
720
|
+
#
|
721
|
+
# * Macros, e.g. something for rb_funcall that does the declare_class for you.
|
722
|
+
#
|
723
|
+
# * Extend c_array_args features to rb_array_args and fixed length arglists.
|
724
|
+
#
|
725
|
+
# * Exception if modify descendant of Library after committed. Accumulators
|
726
|
+
# always notify parent before admitting any changes.
|
727
|
+
#
|
728
|
+
# * Freeze data structures after commit?
|
729
|
+
#
|
730
|
+
# * More wrappers: define_class, globals (in C and in Ruby), funcalls,
|
731
|
+
# iterators, argument type conversion, etc. Really, all of Chapter 17 of Thomas
|
732
|
+
# & Hunt.
|
733
|
+
#
|
734
|
+
# * Make commit happen automatically when the first call is made to a method in
|
735
|
+
# the library. (Use alias, maybe, since method_missing won't work--won't let you
|
736
|
+
# override.)
|
737
|
+
#
|
738
|
+
# * Finer granularity in accumulators. For example, #init could take a (lvalue,
|
739
|
+
# rvalue) pair, which would allow it to detect initialization of the same var
|
740
|
+
# with different values.
|
741
|
+
#
|
742
|
+
# * make this into Inline for ruby:
|
743
|
+
#
|
744
|
+
# Module#define_c_method ("name") { ... }
|
745
|
+
#
|
746
|
+
# (use instance_eval, so that accumulators can be used in the block?) The main
|
747
|
+
# drawback is that no Library is specified, so where does it go? (Actually,
|
748
|
+
# CShadow solves this problem, if you don't mind having a struct as overhead.)
|
749
|
+
#
|
750
|
+
# * investigate unloading a .so/.dll. Or: maybe rb_define_* can be called again,
|
751
|
+
# but in a different library (append version number to the lib name, but not to
|
752
|
+
# the dir name). See Ruby/DL in RAA. (See dln.c, eval.c, ruby.c in ruby source.
|
753
|
+
# It all seems possible, but a bit of work.)
|
754
|
+
#
|
755
|
+
# * parser/generator for (at first) simple ruby code, like '@x.y': one option
|
756
|
+
# would be to use init to define a Proc and use setup to call the Proc
|
757
|
+
#
|
758
|
+
# * check ANSI, check w/ other compilers
|
759
|
+
#
|
760
|
+
# * Improve space recognition in the tab routines (check for \t and handle
|
761
|
+
# intelligently, etc.).
|
762
|
+
#
|
763
|
+
# * Formalize the relation between templates and accumulators. Make it easier
|
764
|
+
# for client code to use its own templates and accumulators.
|
765
|
+
#
|
766
|
+
# * Double-ended accumulators (add_at_end vs. add_at_beginning, or push vs.
|
767
|
+
# unshift).
|
768
|
+
#
|
769
|
+
# * Automatically load/link other dynamic or static libs in the same dir. For
|
770
|
+
# static, use 'have_library' in #extconf; see p.185 of pickaxe. For dynamic, use
|
771
|
+
# Ruby/DL from RAA, or just require. (Currently, this can be done manually by
|
772
|
+
# subclassing and overriding extconf.)
|
773
|
+
#
|
774
|
+
# * More thorough checking for assert_uncommitted. Currently, just a few
|
775
|
+
# top-level methods (Library#commit and some of the define methods) check if the
|
776
|
+
# library has already been committed. Ideally, #commit would freeze all
|
777
|
+
# accumulators. But then the problem is how to report a freeze exception in a
|
778
|
+
# way that makes clear that the problem is really with commit.
|
779
|
+
module CGenerator
|
780
|
+
|
781
|
+
VERSION = '0.16.0'
|
782
|
+
|
783
|
+
class Accumulator ## should be a mixin? "Cumulative"?
|
784
|
+
|
785
|
+
## should delegate Accs into two objects with two inheritance hierarchies,
|
786
|
+
## one for adding items, one for generating strings
|
787
|
+
##OR:
|
788
|
+
## two hierarchies of modules that get included into Template subclasses
|
789
|
+
|
790
|
+
attr_reader :name, :parent
|
791
|
+
|
792
|
+
def initialize name, parent = nil
|
793
|
+
@name = name; @parent = parent
|
794
|
+
@pile = []
|
795
|
+
end
|
796
|
+
|
797
|
+
def accept? item
|
798
|
+
true
|
799
|
+
end
|
800
|
+
|
801
|
+
def add_one_really item
|
802
|
+
@pile << item
|
803
|
+
end
|
804
|
+
|
805
|
+
def add_one item
|
806
|
+
add_one_really item if accept? item
|
807
|
+
end
|
808
|
+
|
809
|
+
def add(*items)
|
810
|
+
for item in items
|
811
|
+
add_one item
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
def output_one item
|
816
|
+
item
|
817
|
+
end
|
818
|
+
|
819
|
+
def output items
|
820
|
+
items.collect { |item|
|
821
|
+
output_one item
|
822
|
+
}.select { |item|
|
823
|
+
item && item != ""
|
824
|
+
}
|
825
|
+
end
|
826
|
+
|
827
|
+
def separator; "\n"; end
|
828
|
+
|
829
|
+
def to_s
|
830
|
+
output(@pile).join(separator)
|
831
|
+
end
|
832
|
+
|
833
|
+
def inspect
|
834
|
+
eol = "\n" if @pile.size > 0
|
835
|
+
s = "s" if @pile.size != 1
|
836
|
+
%{<#{self.class} "#{@name}": #{@pile.size} item#{s}>#{eol}} +
|
837
|
+
@pile.collect { |item| inspect_one item }.join("\n").tabto(2)
|
838
|
+
end
|
839
|
+
|
840
|
+
def inspect_one item
|
841
|
+
item.inspect
|
842
|
+
end
|
843
|
+
|
844
|
+
end # class Accumulator
|
845
|
+
|
846
|
+
module SetAccumulator
|
847
|
+
def accept? item
|
848
|
+
not @pile.include? item
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
module KeyAccumulator
|
853
|
+
def initialize(*args)
|
854
|
+
super
|
855
|
+
@hash = {} # @pile maintains ordered list of keys for @hash
|
856
|
+
end
|
857
|
+
|
858
|
+
def add_one item
|
859
|
+
## why not do this?
|
860
|
+
# unless item.is_a? Hash
|
861
|
+
# raise ArgumentError,
|
862
|
+
# "Tried to add non-hash '#{item}' to KeyAccumulator."
|
863
|
+
# end
|
864
|
+
|
865
|
+
item = item.sort_by {|k,v| k.to_s} unless item.is_a?(Array)
|
866
|
+
|
867
|
+
for key, value in item
|
868
|
+
if not @hash.has_key? key
|
869
|
+
super key
|
870
|
+
end
|
871
|
+
@hash[key] = value_filter(value)
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
def value_filter(value)
|
876
|
+
value
|
877
|
+
end
|
878
|
+
|
879
|
+
def output_one item
|
880
|
+
super @hash[item]
|
881
|
+
end
|
882
|
+
|
883
|
+
def inspect_one item
|
884
|
+
@hash[item].inspect
|
885
|
+
end
|
886
|
+
|
887
|
+
def [](item)
|
888
|
+
@hash[item]
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
class Template < Accumulator
|
893
|
+
|
894
|
+
def initialize name = "", parent = nil, &block
|
895
|
+
super
|
896
|
+
instance_eval(&block) if block
|
897
|
+
## not very useful: users call an accumulator, rather than Template#new
|
898
|
+
end
|
899
|
+
|
900
|
+
def Template.accumulator(*names)
|
901
|
+
kind = if block_given? then yield else Accumulator end
|
902
|
+
|
903
|
+
for name in names
|
904
|
+
|
905
|
+
module_eval %{
|
906
|
+
def #{name}(*items)
|
907
|
+
#{name}!.add(*items)
|
908
|
+
end
|
909
|
+
def #{name}!
|
910
|
+
@#{name} ||= #{kind}.new(:#{name}, self)
|
911
|
+
end
|
912
|
+
}
|
913
|
+
|
914
|
+
unless Template.respond_to? name
|
915
|
+
Template.inherit :@parent, name
|
916
|
+
end
|
917
|
+
|
918
|
+
end
|
919
|
+
|
920
|
+
end
|
921
|
+
|
922
|
+
end # class Template
|
923
|
+
|
924
|
+
|
925
|
+
class Library < Template
|
926
|
+
|
927
|
+
class CommitError < RuntimeError; end
|
928
|
+
|
929
|
+
attr_reader :init_library_function, :include_file, :source_file
|
930
|
+
attr_accessor :purge_source_dir, :show_times_flag
|
931
|
+
|
932
|
+
def initialize name
|
933
|
+
super name
|
934
|
+
|
935
|
+
@show_times_flag = @purge_source_dir = false
|
936
|
+
@committed = false
|
937
|
+
@source_file = nil
|
938
|
+
|
939
|
+
@rtime = Time.now.to_f
|
940
|
+
@ptime = process_times
|
941
|
+
|
942
|
+
unless name =~ /\A[A-Za-z_]\w*\z/
|
943
|
+
raise NameError,
|
944
|
+
"\n Not a valid library name: '#{name}'." +
|
945
|
+
"\n Name must be a C identifier."
|
946
|
+
end
|
947
|
+
|
948
|
+
@include_file, @source_file = add_file name
|
949
|
+
@include_file.include '<ruby.h>'
|
950
|
+
|
951
|
+
@init_library_function = define_c_function "Init_" + name
|
952
|
+
@init_library_function.scope :extern
|
953
|
+
|
954
|
+
@init_library_function.body \
|
955
|
+
rb_define_method!,
|
956
|
+
rb_define_module_function!,
|
957
|
+
rb_define_global_function!,
|
958
|
+
rb_define_singleton_method!
|
959
|
+
## odd, putting an accum inside
|
960
|
+
## a template which is not the parent
|
961
|
+
end
|
962
|
+
|
963
|
+
def use_work_dir dir_name
|
964
|
+
if File.basename(Dir.pwd) == dir_name
|
965
|
+
yield
|
966
|
+
else
|
967
|
+
require 'fileutils'
|
968
|
+
FileUtils.makedirs dir_name
|
969
|
+
Dir.chdir dir_name do
|
970
|
+
yield
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
def add_file name, opts = {}
|
976
|
+
pair = @pile.detect {|p| p[0].name == name + ".h"}
|
977
|
+
|
978
|
+
if not pair
|
979
|
+
new_include_file = CFile.new name + ".h", self
|
980
|
+
new_source_file = CFile.new name + ".c", self, new_include_file
|
981
|
+
|
982
|
+
if @source_file
|
983
|
+
new_source_file.include @include_file unless opts[:independent]
|
984
|
+
@source_file.include new_include_file
|
985
|
+
end
|
986
|
+
new_source_file.include new_include_file
|
987
|
+
|
988
|
+
pair = [new_include_file, new_source_file]
|
989
|
+
add pair # for inspect and commit
|
990
|
+
end
|
991
|
+
|
992
|
+
return pair
|
993
|
+
end
|
994
|
+
|
995
|
+
def assert_uncommitted
|
996
|
+
if @committed
|
997
|
+
raise CommitError, "\nLibrary #{@name} has already been committed."
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
def committed?
|
1002
|
+
@committed
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def empty?
|
1006
|
+
@init_library_function.empty? ## is this enough?
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
def before_commit(&block)
|
1010
|
+
(@before_commit ||= []) << block
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def after_commit(&block)
|
1014
|
+
(@after_commit ||= []) << block
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def commit(build = true)
|
1018
|
+
assert_uncommitted
|
1019
|
+
|
1020
|
+
while @before_commit
|
1021
|
+
bc = @before_commit; @before_commit = nil
|
1022
|
+
bc.each {|block| block[self]}
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
@committed = true
|
1026
|
+
|
1027
|
+
if build
|
1028
|
+
@logname ||= "make.log"
|
1029
|
+
|
1030
|
+
show_times "precommit"
|
1031
|
+
show_times "write" do write end
|
1032
|
+
show_times "makedepend" do makedepend end
|
1033
|
+
show_times "mkmf" do mkmf end
|
1034
|
+
show_times "make" do make end
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
show_times "loadlib" do loadlib end
|
1038
|
+
|
1039
|
+
while @after_commit
|
1040
|
+
ac = @after_commit; @after_commit = nil
|
1041
|
+
ac.reverse.each {|block| block[self]}
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def process_times
|
1046
|
+
RUBY_VERSION.to_f >= 1.7 ? Process.times : Time.times
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def show_times message
|
1050
|
+
yield if block_given?
|
1051
|
+
if @show_times_flag
|
1052
|
+
unless @show_times_started
|
1053
|
+
printf "\n%20s %6s %6s %6s %6s %7s\n",
|
1054
|
+
"__step__", "utime", "stime", "cutime", "cstime", "real"
|
1055
|
+
@show_times_started = true
|
1056
|
+
end
|
1057
|
+
ptime = self.process_times
|
1058
|
+
rtime = Time.now.to_f
|
1059
|
+
printf "%20s %6.2f %6.2f %6.2f %6.2f %7.3f\n", message,
|
1060
|
+
ptime.utime - @ptime.utime, ptime.stime - @ptime.stime,
|
1061
|
+
ptime.cutime - @ptime.cutime, ptime.cstime - @ptime.cstime,
|
1062
|
+
rtime - @rtime
|
1063
|
+
@ptime = ptime
|
1064
|
+
@rtime = rtime
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def write
|
1069
|
+
build_wrapper do
|
1070
|
+
templates = @pile.flatten.sort_by { |t| t.name }
|
1071
|
+
|
1072
|
+
if @purge_source_dir
|
1073
|
+
# hide or delete files not listed in templates
|
1074
|
+
files = Dir["*.{c,h,o}"]
|
1075
|
+
template_files = templates.map { |t| t.name }
|
1076
|
+
template_files +=
|
1077
|
+
template_files.grep(/\.c$/).map { |f| f.sub(/\.c$/, ".o") }
|
1078
|
+
for file in files - template_files
|
1079
|
+
if @purge_source_dir == :delete
|
1080
|
+
File.delete(file) rescue SystemCallError
|
1081
|
+
else
|
1082
|
+
File.rename(file, file + ".hide") rescue SystemCallError
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
for template in templates do
|
1088
|
+
begin
|
1089
|
+
File.open(template.name, 'r+') {|f| update_file f, template}
|
1090
|
+
rescue SystemCallError
|
1091
|
+
File.open(template.name, 'w+') {|f| update_file f, template}
|
1092
|
+
end
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
def update_file f, template
|
1098
|
+
template_str = template.to_s
|
1099
|
+
file_data = f.gets(nil) ## sysread is faster?
|
1100
|
+
unless file_data == template_str
|
1101
|
+
if defined?($CGEN_VERBOSE) and $CGEN_VERBOSE
|
1102
|
+
print_update_reason(template, template_str, file_data)
|
1103
|
+
end
|
1104
|
+
f.rewind
|
1105
|
+
f.print template_str
|
1106
|
+
f.truncate f.pos
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
def print_update_reason template, template_str, file_data
|
1111
|
+
puts "\nUpdating file #{template.name}"
|
1112
|
+
|
1113
|
+
if file_data == nil
|
1114
|
+
puts "File on disk is empty"
|
1115
|
+
return
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
s = file_data
|
1119
|
+
t = template_str
|
1120
|
+
slines = s.split "\n"
|
1121
|
+
tlines = t.split "\n"
|
1122
|
+
i = 0
|
1123
|
+
sline = slines[i]
|
1124
|
+
tline = tlines[i]
|
1125
|
+
catch :done do
|
1126
|
+
while sline and tline
|
1127
|
+
if sline != tline
|
1128
|
+
puts "Line #{i+1}",
|
1129
|
+
"On disk: #{sline.inspect}",
|
1130
|
+
"In memory: #{tline.inspect}",
|
1131
|
+
""
|
1132
|
+
throw :done
|
1133
|
+
end
|
1134
|
+
i += 1
|
1135
|
+
sline = slines[i]
|
1136
|
+
tline = tlines[i]
|
1137
|
+
end
|
1138
|
+
if sline == nil and tline == nil
|
1139
|
+
puts "Very strange, no difference found!"
|
1140
|
+
else
|
1141
|
+
if slines.size > tlines.size
|
1142
|
+
puts "file on disk is longer"
|
1143
|
+
else
|
1144
|
+
puts "file in memory is longer"
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
def makedepend
|
1151
|
+
return if /mswin/i =~ RUBY_PLATFORM ## what else can we do?
|
1152
|
+
build_wrapper do
|
1153
|
+
cfg = Config::CONFIG
|
1154
|
+
dirs = [cfg["sitearchdir"], cfg["archdir"], cfg["includedir"]]
|
1155
|
+
result = system %{
|
1156
|
+
touch depend
|
1157
|
+
makedepend -fdepend *.c -I#{dirs.join " -I"} >#{@logname} 2>&1
|
1158
|
+
} ## what about "gcc -MM" as in step 6 in README.EXT ?
|
1159
|
+
unless result
|
1160
|
+
log_data = File.read(@logname) rescue nil
|
1161
|
+
msg = "\n makedepend failed for #{@name}."
|
1162
|
+
if log_data
|
1163
|
+
msg <<
|
1164
|
+
"\n Transcript is saved in #{@name}/#{@logname} and follows:" +
|
1165
|
+
"\n " + "_" * 60 +
|
1166
|
+
"\n" + log_data.tabto(3).gsub(/^ /, " |") +
|
1167
|
+
" " + "_" * 60 + "\n"
|
1168
|
+
else
|
1169
|
+
msg <<
|
1170
|
+
"\n No log available.\n"
|
1171
|
+
end
|
1172
|
+
raise CommitError, msg
|
1173
|
+
end
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
def mkmf
|
1178
|
+
need_to_make_clean = false
|
1179
|
+
|
1180
|
+
# Need to do this in a separate process because mkmf.rb pollutes
|
1181
|
+
# the global namespace.
|
1182
|
+
build_wrapper do
|
1183
|
+
require 'rbconfig'
|
1184
|
+
ruby = Config::CONFIG["RUBY_INSTALL_NAME"]
|
1185
|
+
|
1186
|
+
old_contents = File.read("extconf.rb") rescue nil
|
1187
|
+
contents = extconf
|
1188
|
+
require 'stringio'
|
1189
|
+
s = StringIO.new
|
1190
|
+
s.puts(contents)
|
1191
|
+
s.rewind
|
1192
|
+
contents = s.read
|
1193
|
+
|
1194
|
+
if old_contents != contents
|
1195
|
+
File.open("extconf.rb", "w") do |f|
|
1196
|
+
f.puts contents
|
1197
|
+
end
|
1198
|
+
need_to_make_clean = true
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
system %{
|
1202
|
+
#{ruby} extconf.rb > #{@logname}
|
1203
|
+
}
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
make "clean" if need_to_make_clean
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
## use -j and -l make switches for multiprocessor builds (man sysconf)
|
1210
|
+
## see http://www.gnu.org/manual/make/html_chapter/make_5.html#SEC47
|
1211
|
+
def make arg = nil
|
1212
|
+
build_wrapper do
|
1213
|
+
unless system "#{make_program} #{arg} >>#{@logname} 2>&1"
|
1214
|
+
raise CommitError,
|
1215
|
+
"\n Make #{arg} failed for #{@name}." +
|
1216
|
+
"\n Transcript is saved in #{@name}/#{@logname} and follows:" +
|
1217
|
+
"\n " + "_" * 60 +
|
1218
|
+
"\n" + File.read(@logname).tabto(3).gsub(/^ /, " |") +
|
1219
|
+
" " + "_" * 60 + "\n"
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
if arg == 'clean' or arg == 'distclean'
|
1223
|
+
File.delete(@logname) rescue SystemCallError
|
1224
|
+
if arg == 'distclean'
|
1225
|
+
for template in @pile.flatten do
|
1226
|
+
File.delete(template.name) rescue SystemCallError
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
def make_program
|
1234
|
+
case RUBY_PLATFORM
|
1235
|
+
when /mswin/i
|
1236
|
+
"nmake"
|
1237
|
+
when /mingw/i
|
1238
|
+
"make"
|
1239
|
+
# "mingw32-make" is the MSYS-independent, MSVC native version
|
1240
|
+
# which is supposedly less useful
|
1241
|
+
else
|
1242
|
+
"make"
|
1243
|
+
end
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
def build_wrapper
|
1247
|
+
if File.exists? @name
|
1248
|
+
unless File.directory? @name
|
1249
|
+
raise CommitError, "Library #{@name}: Can't mkdir; file exists."
|
1250
|
+
end
|
1251
|
+
else
|
1252
|
+
Dir.mkdir @name
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
Dir.chdir @name do yield end
|
1256
|
+
### this is fragile--should record abs path when Lib is created
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
# Yields the array of lines being constructed so that additional configuration
|
1260
|
+
# can be added. See the ruby documentation on mkmf.
|
1261
|
+
def extconf # :yields: lines_array
|
1262
|
+
a = []
|
1263
|
+
a << "require 'mkmf'"
|
1264
|
+
a << "$CFLAGS = \"#$CFLAGS\"" if defined?($CFLAGS)
|
1265
|
+
yield a if block_given?
|
1266
|
+
a << "create_makefile '#{@name}'"
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
def loadlib
|
1270
|
+
require File.join(".", @name, @name)
|
1271
|
+
rescue ScriptError, StandardError => e
|
1272
|
+
raise e.class, "\nCgen: problem loading library:\n" + e.message
|
1273
|
+
end
|
1274
|
+
|
1275
|
+
class RbDefineAccumulator < Accumulator
|
1276
|
+
def add spec
|
1277
|
+
c_name = spec[:c_name]
|
1278
|
+
mod = spec[:mod]
|
1279
|
+
rb_name = spec[:rb_name]
|
1280
|
+
|
1281
|
+
meth_rec =
|
1282
|
+
if c_name
|
1283
|
+
@pile.find { |s| s[:c_name] == c_name }
|
1284
|
+
else
|
1285
|
+
@pile.find { |s| s[:mod] == mod and s[:rb_name] == rb_name }
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
if meth_rec
|
1289
|
+
meth_rec.update spec
|
1290
|
+
else
|
1291
|
+
meth_rec = spec
|
1292
|
+
|
1293
|
+
unless rb_name
|
1294
|
+
raise ArgumentError, "define: must provide method name."
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
kind = @name.to_s.sub(/\Arb_define_/, "")
|
1298
|
+
if mod
|
1299
|
+
meth_rec[:mod_c_name] ||= @parent.declare_module(mod) ## @parent ?
|
1300
|
+
meth_rec[:c_name] ||=
|
1301
|
+
("#{CGenerator::make_c_name rb_name}" +
|
1302
|
+
"_#{meth_rec[:mod_c_name]}_#{kind}").intern
|
1303
|
+
else
|
1304
|
+
meth_rec[:c_name] ||=
|
1305
|
+
"#{CGenerator::make_c_name rb_name}_#{kind}".intern
|
1306
|
+
end
|
1307
|
+
meth_rec[:argc] ||= 0
|
1308
|
+
@pile << meth_rec
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
meth_rec[:c_name]
|
1312
|
+
end
|
1313
|
+
|
1314
|
+
def to_s
|
1315
|
+
@pile.collect { |m|
|
1316
|
+
rb_name = m[:rb_name]
|
1317
|
+
c_name = m[:c_name]
|
1318
|
+
argc = m[:argc]
|
1319
|
+
if m[:mod]
|
1320
|
+
mod_c_name = m[:mod_c_name]
|
1321
|
+
"#{@name}(#{mod_c_name}, \"#{rb_name}\", #{c_name}, #{argc});"
|
1322
|
+
else
|
1323
|
+
"#{@name}(\"#{rb_name}\", #{c_name}, #{argc});"
|
1324
|
+
end
|
1325
|
+
}.join "\n"
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
end # class RbDefineAccumulator
|
1329
|
+
|
1330
|
+
accumulator(:rb_define_method,
|
1331
|
+
:rb_define_module_function,
|
1332
|
+
:rb_define_global_function,
|
1333
|
+
:rb_define_singleton_method) {RbDefineAccumulator}
|
1334
|
+
|
1335
|
+
def define_c_method(*args)
|
1336
|
+
@source_file.define_c_method(*args)
|
1337
|
+
end
|
1338
|
+
|
1339
|
+
def define_c_module_function(*args)
|
1340
|
+
@source_file.define_c_module_function(*args)
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
def define_c_global_function(*args)
|
1344
|
+
@source_file.define_c_global_function(*args)
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
def define_c_singleton_method(*args)
|
1348
|
+
@source_file.define_c_singleton_method(*args)
|
1349
|
+
end
|
1350
|
+
alias define_c_class_method define_c_singleton_method
|
1351
|
+
|
1352
|
+
def include(*args)
|
1353
|
+
@source_file.include(*args)
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
def declare(*args)
|
1357
|
+
@source_file.declare(*args)
|
1358
|
+
end
|
1359
|
+
alias declare_static declare
|
1360
|
+
|
1361
|
+
def declare_extern(*args)
|
1362
|
+
@include_file.declare(*args)
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
def declare_struct struct_name, *rest
|
1366
|
+
@source_file.declare_struct struct_name, *rest
|
1367
|
+
end
|
1368
|
+
alias declare_static_struct declare_struct
|
1369
|
+
|
1370
|
+
def declare_extern_struct struct_name, *rest
|
1371
|
+
@include_file.declare_struct struct_name, *rest
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
def define(*args)
|
1375
|
+
@source_file.define(*args)
|
1376
|
+
end
|
1377
|
+
alias define_c_function define
|
1378
|
+
|
1379
|
+
def declare_module mod
|
1380
|
+
c_name = "module_#{CGenerator::make_c_name mod.to_s}"
|
1381
|
+
declare mod => "VALUE #{c_name}"
|
1382
|
+
declare_extern mod => "extern VALUE #{c_name}"
|
1383
|
+
setup mod => "#{c_name} = rb_path2class(\"#{mod}\")"
|
1384
|
+
c_name.intern
|
1385
|
+
end
|
1386
|
+
alias declare_class declare_module
|
1387
|
+
|
1388
|
+
def declare_symbol sym
|
1389
|
+
c_name = "ID_#{CGenerator::make_c_name sym}"
|
1390
|
+
declare sym => "ID #{c_name}"
|
1391
|
+
declare_extern sym => "extern ID #{c_name}"
|
1392
|
+
setup sym => "#{c_name} = rb_intern(\"#{sym}\")"
|
1393
|
+
c_name.intern
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def literal_symbol sym
|
1397
|
+
c_name = "SYM_#{CGenerator::make_c_name sym}"
|
1398
|
+
declare sym => "VALUE #{c_name}"
|
1399
|
+
declare_extern sym => "extern VALUE #{c_name}"
|
1400
|
+
setup sym => "#{c_name} = ID2SYM(rb_intern(\"#{sym}\"))"
|
1401
|
+
c_name.intern
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
def setup(*args)
|
1405
|
+
@init_library_function.setup(*args)
|
1406
|
+
end
|
1407
|
+
|
1408
|
+
Template.inherit :parent,
|
1409
|
+
:declare_module, :declare_symbol,
|
1410
|
+
:declare_static, :declare_extern,
|
1411
|
+
:declare_class, :declare_module,
|
1412
|
+
:literal_symbol,
|
1413
|
+
:library, :file,
|
1414
|
+
:assert_uncommitted
|
1415
|
+
|
1416
|
+
def library
|
1417
|
+
self
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
end # class Library
|
1421
|
+
|
1422
|
+
|
1423
|
+
class CFragment < Template
|
1424
|
+
|
1425
|
+
class StatementAccumulator < Accumulator
|
1426
|
+
def add_one_really item
|
1427
|
+
if item.kind_of? String
|
1428
|
+
super item.tabto(0)
|
1429
|
+
else
|
1430
|
+
super
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
def output_one item
|
1435
|
+
str = item.to_s
|
1436
|
+
# if str =~ /(?:\A|[;}])\s*\z/
|
1437
|
+
if str.empty? or str =~ /[\s;}]\z/ or str =~ /\A\s*(?:\/\/|#)/
|
1438
|
+
str
|
1439
|
+
else
|
1440
|
+
str + ';'
|
1441
|
+
end
|
1442
|
+
end
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
class StatementKeyAccumulator < StatementAccumulator
|
1446
|
+
include KeyAccumulator
|
1447
|
+
def value_filter(value)
|
1448
|
+
if value.kind_of? String
|
1449
|
+
super value.tabto(0)
|
1450
|
+
else
|
1451
|
+
super
|
1452
|
+
end
|
1453
|
+
end
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
class BlockAccumulator < StatementAccumulator
|
1457
|
+
def add(*args)
|
1458
|
+
super
|
1459
|
+
return self
|
1460
|
+
end
|
1461
|
+
def to_s
|
1462
|
+
["{", super.tabto(4), "}"].join "\n"
|
1463
|
+
end
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
class SingletonAccumulator < Accumulator
|
1467
|
+
def add_one item
|
1468
|
+
@pile = [item]
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
end # class CFragment
|
1473
|
+
|
1474
|
+
|
1475
|
+
class CFile < CFragment
|
1476
|
+
|
1477
|
+
attr_reader :include_file
|
1478
|
+
|
1479
|
+
def initialize name, library, include_file = nil
|
1480
|
+
super name, library
|
1481
|
+
@include_file = include_file
|
1482
|
+
if include_file
|
1483
|
+
add preamble!, include!, declare!, define!
|
1484
|
+
else
|
1485
|
+
## it's a little hacky to decide in this way that this is a .h file
|
1486
|
+
sym = name.gsub(/\W/, '_')
|
1487
|
+
add "#ifndef #{sym}\n#define #{sym}",
|
1488
|
+
preamble!, include!, declare!, define!,
|
1489
|
+
"#endif"
|
1490
|
+
end
|
1491
|
+
end
|
1492
|
+
|
1493
|
+
def separator
|
1494
|
+
"\n\n"
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
class FunctionAccumulator < Accumulator
|
1498
|
+
|
1499
|
+
def add name, kind = Function
|
1500
|
+
@parent.assert_uncommitted
|
1501
|
+
name = name.intern if name.is_a? String
|
1502
|
+
|
1503
|
+
if kind.is_a? Symbol or kind.is_a? String
|
1504
|
+
kind = eval "CGenerator::#{kind}"
|
1505
|
+
end
|
1506
|
+
|
1507
|
+
unless kind <= Function
|
1508
|
+
raise ArgumentError,
|
1509
|
+
"#{kind.class} #{kind} is not a subclass of CGenerator::Function."
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
fn = @pile.find { |f| f.name == name }
|
1513
|
+
unless fn
|
1514
|
+
fn = kind.new name, @parent
|
1515
|
+
super fn
|
1516
|
+
end
|
1517
|
+
fn
|
1518
|
+
end
|
1519
|
+
|
1520
|
+
def separator
|
1521
|
+
"\n\n"
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
class IncludeAccumulator < Accumulator
|
1527
|
+
include SetAccumulator
|
1528
|
+
def output_one item
|
1529
|
+
item = item.name unless item.is_a? String
|
1530
|
+
"#include " +
|
1531
|
+
if item =~ /\A<.*>\z/
|
1532
|
+
item
|
1533
|
+
else
|
1534
|
+
'"' + item + '"'
|
1535
|
+
end
|
1536
|
+
end
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
class CommentAccumulator < Accumulator
|
1540
|
+
def to_s
|
1541
|
+
str = super
|
1542
|
+
if str.length > 0
|
1543
|
+
str.gsub(/^(?!\/\/)/, "// ")
|
1544
|
+
else
|
1545
|
+
str
|
1546
|
+
end
|
1547
|
+
end
|
1548
|
+
end
|
1549
|
+
|
1550
|
+
accumulator(:preamble) {CommentAccumulator}
|
1551
|
+
accumulator(:include) {IncludeAccumulator}
|
1552
|
+
accumulator(:declare) {StatementKeyAccumulator}
|
1553
|
+
accumulator(:define) {FunctionAccumulator}
|
1554
|
+
|
1555
|
+
def define_c_function c_name, subclass = Function
|
1556
|
+
define c_name, subclass
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
def define_c_method mod, name, subclass = Method
|
1560
|
+
unless subclass <= Method ## should use assert
|
1561
|
+
raise "#{subclass.name} is not <= Method"
|
1562
|
+
end
|
1563
|
+
c_name = library.rb_define_method :mod => mod, :rb_name => name
|
1564
|
+
define c_name, subclass
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
def define_c_module_function mod, name, subclass = ModuleFunction
|
1568
|
+
raise unless subclass <= ModuleFunction
|
1569
|
+
c_name = library.rb_define_module_function :mod => mod, :rb_name => name
|
1570
|
+
define c_name, subclass
|
1571
|
+
end
|
1572
|
+
|
1573
|
+
def define_c_global_function name, subclass = GlobalFunction
|
1574
|
+
raise unless subclass <= GlobalFunction
|
1575
|
+
c_name = library.rb_define_global_function :rb_name => name
|
1576
|
+
define c_name, subclass
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
def define_c_singleton_method mod, name, subclass = SingletonMethod
|
1580
|
+
raise unless subclass <= SingletonMethod
|
1581
|
+
c_name = library.rb_define_singleton_method :mod => mod, :rb_name => name
|
1582
|
+
define c_name, subclass
|
1583
|
+
end
|
1584
|
+
alias define_c_class_method define_c_singleton_method
|
1585
|
+
|
1586
|
+
# For ruby 1.7/1.8 after 20Dec2002
|
1587
|
+
def define_alloc_func klass
|
1588
|
+
klass_c_name = declare_class klass
|
1589
|
+
c_name = "alloc_func_#{klass_c_name}"
|
1590
|
+
library.init_library_function.body \
|
1591
|
+
%{rb_define_alloc_func(#{klass_c_name}, #{c_name})}
|
1592
|
+
define c_name, Function
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
def declare_struct struct_name, *rest
|
1596
|
+
struct = CGenerator::Structure.new struct_name, self, *rest
|
1597
|
+
declare struct_name => ["\n", struct, ";\n"]
|
1598
|
+
struct
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
def declare_extern_struct struct_name, *rest
|
1602
|
+
@include_file.declare_struct struct_name, *rest
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
alias declare_static declare
|
1606
|
+
|
1607
|
+
def declare_extern(*args)
|
1608
|
+
if @include_file
|
1609
|
+
@include_file.declare(*args)
|
1610
|
+
else
|
1611
|
+
declare(*args)
|
1612
|
+
end
|
1613
|
+
end
|
1614
|
+
|
1615
|
+
def to_s
|
1616
|
+
super + "\n"
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
def file
|
1620
|
+
self
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
end # class CFile
|
1624
|
+
|
1625
|
+
|
1626
|
+
class Prototype < CFragment
|
1627
|
+
|
1628
|
+
class ArgumentAccumulator < Accumulator
|
1629
|
+
include SetAccumulator
|
1630
|
+
def to_s
|
1631
|
+
if @pile.size > 0
|
1632
|
+
"(" + @pile.join(", ") + ")"
|
1633
|
+
else
|
1634
|
+
'(void)'
|
1635
|
+
end
|
1636
|
+
end
|
1637
|
+
|
1638
|
+
def size
|
1639
|
+
@pile.size
|
1640
|
+
end
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
accumulator(:scope,
|
1644
|
+
:return_type) {SingletonAccumulator}
|
1645
|
+
accumulator(:arguments) {ArgumentAccumulator}
|
1646
|
+
|
1647
|
+
def initialize name, parent
|
1648
|
+
super
|
1649
|
+
add scope!, return_type!, " ", name, arguments!
|
1650
|
+
end
|
1651
|
+
|
1652
|
+
def separator; ""; end
|
1653
|
+
|
1654
|
+
def argc
|
1655
|
+
arguments!.size
|
1656
|
+
end
|
1657
|
+
|
1658
|
+
end # class Prototype
|
1659
|
+
|
1660
|
+
class Function < CFragment
|
1661
|
+
|
1662
|
+
def initialize name, parent
|
1663
|
+
super
|
1664
|
+
|
1665
|
+
scope :static
|
1666
|
+
return_type 'void'
|
1667
|
+
|
1668
|
+
add prototype,
|
1669
|
+
block(declare!, init!, setup!, body!, returns!)
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
def empty?
|
1673
|
+
block!.to_s =~ /\A\{\s*\}\z/m
|
1674
|
+
end
|
1675
|
+
|
1676
|
+
def prototype
|
1677
|
+
@prototype ||= Prototype.new(name, self)
|
1678
|
+
end
|
1679
|
+
|
1680
|
+
def scope s
|
1681
|
+
scope_str = s.to_s
|
1682
|
+
unless defined?(@scope) and scope_str == @scope
|
1683
|
+
@scope = scope_str
|
1684
|
+
case scope_str
|
1685
|
+
when "static"
|
1686
|
+
prototype.scope "static " ## this is kludgy
|
1687
|
+
declare_static @name => prototype
|
1688
|
+
declare_extern @name => nil
|
1689
|
+
when "extern"
|
1690
|
+
prototype.scope "" ## would be too much work to do "extern "
|
1691
|
+
declare_extern @name => prototype
|
1692
|
+
declare_static @name => nil
|
1693
|
+
end
|
1694
|
+
end
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
class ReturnAccumulator < SingletonAccumulator
|
1698
|
+
def to_s
|
1699
|
+
if @pile.size > 0
|
1700
|
+
"return #{@pile.join};"
|
1701
|
+
else
|
1702
|
+
""
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
end
|
1706
|
+
|
1707
|
+
class InitAccumulator < BlockAccumulator
|
1708
|
+
def add_one_really(*args)
|
1709
|
+
super
|
1710
|
+
@parent.declare :first_time => "static int first_time = 1"
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
def to_s
|
1714
|
+
if @pile.size > 0
|
1715
|
+
["\nif (first_time)", super].join(" ") +
|
1716
|
+
"\nif (first_time) first_time = 0;\n"
|
1717
|
+
else
|
1718
|
+
""
|
1719
|
+
end
|
1720
|
+
end
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
accumulator(:block) {BlockAccumulator}
|
1724
|
+
accumulator(:declare) {StatementKeyAccumulator}
|
1725
|
+
accumulator(:init) {InitAccumulator}
|
1726
|
+
accumulator(:setup) {StatementKeyAccumulator}
|
1727
|
+
accumulator(:body) {StatementAccumulator}
|
1728
|
+
accumulator(:returns) {ReturnAccumulator}
|
1729
|
+
|
1730
|
+
def return_type(*args)
|
1731
|
+
prototype.return_type(*args)
|
1732
|
+
end
|
1733
|
+
|
1734
|
+
def arguments(*args)
|
1735
|
+
prototype.arguments(*args)
|
1736
|
+
end
|
1737
|
+
|
1738
|
+
end # class Function
|
1739
|
+
|
1740
|
+
|
1741
|
+
class MethodPrototype < Prototype
|
1742
|
+
|
1743
|
+
class MethodArgumentAccumulator < ArgumentAccumulator
|
1744
|
+
def add_one item
|
1745
|
+
item = item.to_s
|
1746
|
+
unless item =~ /\AVALUE /
|
1747
|
+
item = "VALUE " + item
|
1748
|
+
end
|
1749
|
+
super
|
1750
|
+
end
|
1751
|
+
def reset
|
1752
|
+
@pile = []
|
1753
|
+
end
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
class RbScanArgsSpec
|
1757
|
+
def initialize bl
|
1758
|
+
@required = @optional = @typecheck = @default = @rest = @block = nil
|
1759
|
+
if bl
|
1760
|
+
instance_eval(&bl)
|
1761
|
+
end
|
1762
|
+
end
|
1763
|
+
|
1764
|
+
def required(*args); @required = args; end
|
1765
|
+
def optional(*args); @optional = args; end
|
1766
|
+
def typecheck(arg); @typecheck = arg; end
|
1767
|
+
def default(arg); @default = arg; end
|
1768
|
+
def rest(arg); @rest = arg; end
|
1769
|
+
def block(arg); @block = arg; end
|
1770
|
+
|
1771
|
+
def get_required; @required; end
|
1772
|
+
def get_optional; @optional; end
|
1773
|
+
def get_typecheck; @typecheck; end
|
1774
|
+
def get_default; @default; end
|
1775
|
+
def get_rest; @rest; end
|
1776
|
+
def get_block; @block; end
|
1777
|
+
end
|
1778
|
+
|
1779
|
+
accumulator(:arguments) {MethodArgumentAccumulator}
|
1780
|
+
|
1781
|
+
def c_array_args(argc_name = 'argc', argv_name = 'argv', &bl)
|
1782
|
+
arguments!.reset
|
1783
|
+
arguments!.add_one_really "int #{argc_name}"
|
1784
|
+
arguments!.add "*#{argv_name}", "self"
|
1785
|
+
@pile.freeze
|
1786
|
+
|
1787
|
+
scan_spec = RbScanArgsSpec.new(bl)
|
1788
|
+
|
1789
|
+
fmt_str = '"'
|
1790
|
+
arg_list = [fmt_str]
|
1791
|
+
|
1792
|
+
required = scan_spec.get_required
|
1793
|
+
count_required = required ? required.size : 0
|
1794
|
+
if count_required > 0
|
1795
|
+
fmt_str << "#{required.size}"
|
1796
|
+
for arg in required
|
1797
|
+
arg_list << arg
|
1798
|
+
declare arg => "VALUE #{arg}"
|
1799
|
+
end
|
1800
|
+
end
|
1801
|
+
|
1802
|
+
optional = scan_spec.get_optional
|
1803
|
+
|
1804
|
+
count_optional = optional ? optional.size : 0
|
1805
|
+
if count_optional > 0
|
1806
|
+
fmt_str << "0" unless count_required > 0
|
1807
|
+
fmt_str << "#{optional.size}"
|
1808
|
+
for arg in optional
|
1809
|
+
arg_list << arg
|
1810
|
+
declare arg => "VALUE #{arg}"
|
1811
|
+
end
|
1812
|
+
end
|
1813
|
+
|
1814
|
+
rest = scan_spec.get_rest
|
1815
|
+
if rest
|
1816
|
+
fmt_str << "*"
|
1817
|
+
arg_list << rest
|
1818
|
+
declare rest => "VALUE #{rest}"
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
block = scan_spec.get_block
|
1822
|
+
if block
|
1823
|
+
fmt_str << "&"
|
1824
|
+
arg_list << block
|
1825
|
+
declare block => "VALUE #{block}"
|
1826
|
+
end
|
1827
|
+
|
1828
|
+
fmt_str << '"'
|
1829
|
+
arg_str = arg_list.join ", &"
|
1830
|
+
|
1831
|
+
unless arg_str == '""'
|
1832
|
+
setup :rb_scan_args => %{
|
1833
|
+
rb_scan_args(#{argc_name}, #{argv_name}, #{arg_str});
|
1834
|
+
}.tabto(0)
|
1835
|
+
|
1836
|
+
typecheck = scan_spec.get_typecheck
|
1837
|
+
if typecheck
|
1838
|
+
for arg, argtype in typecheck
|
1839
|
+
next unless argtype and argtype != Object
|
1840
|
+
## this could be a function call
|
1841
|
+
setup "#{arg} typecheck" => %{\
|
1842
|
+
if (!NIL_P(#{arg}) &&
|
1843
|
+
rb_obj_is_kind_of(#{arg}, #{declare_class argtype}) != Qtrue)
|
1844
|
+
rb_raise(#{declare_class TypeError},
|
1845
|
+
"argument #{arg} declared #{argtype} but passed %s.",
|
1846
|
+
STR2CSTR(rb_funcall(
|
1847
|
+
rb_funcall(#{arg}, #{declare_symbol :class}, 0),
|
1848
|
+
#{declare_symbol :to_s}, 0)));
|
1849
|
+
}.tabto(0)
|
1850
|
+
end
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
default = scan_spec.get_default
|
1854
|
+
if default and default.size > 0
|
1855
|
+
cases =
|
1856
|
+
(0..count_optional-1).map { |i|
|
1857
|
+
"case #{i}:" +
|
1858
|
+
if default[optional[i]]
|
1859
|
+
" #{optional[i]} = #{default[optional[i]]};"
|
1860
|
+
else
|
1861
|
+
""
|
1862
|
+
end
|
1863
|
+
}.join("\n")
|
1864
|
+
|
1865
|
+
setup :rb_scan_args_defaults => %{\
|
1866
|
+
switch (argc - #{count_required}) {\n#{cases}
|
1867
|
+
}
|
1868
|
+
}.tabto(0)
|
1869
|
+
end
|
1870
|
+
end
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
def rb_array_args args_name = 'args'
|
1874
|
+
arguments "#{args_name}"
|
1875
|
+
@pile.freeze
|
1876
|
+
end
|
1877
|
+
|
1878
|
+
end # class MethodPrototype
|
1879
|
+
|
1880
|
+
class RubyFunction < Function
|
1881
|
+
|
1882
|
+
def initialize name, parent
|
1883
|
+
super
|
1884
|
+
return_type 'VALUE'
|
1885
|
+
arguments 'self'
|
1886
|
+
returns 'Qnil'
|
1887
|
+
end
|
1888
|
+
|
1889
|
+
def prototype
|
1890
|
+
@prototype ||= MethodPrototype.new(name, self)
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
def c_array_args(*args, &bl)
|
1894
|
+
prototype.c_array_args(*args, &bl)
|
1895
|
+
register_args :c_name => @name, :argc => -1 # code used by Ruby C API
|
1896
|
+
end
|
1897
|
+
|
1898
|
+
def rb_array_args(*args, &bl)
|
1899
|
+
prototype.rb_array_args(*args, &bl)
|
1900
|
+
register_args :c_name => @name, :argc => -2 # code used by Ruby C API
|
1901
|
+
end
|
1902
|
+
|
1903
|
+
def arguments(*args, &bl)
|
1904
|
+
prototype.arguments(*args, &bl)
|
1905
|
+
register_args :c_name => @name, :argc => prototype.argc - 1
|
1906
|
+
end
|
1907
|
+
|
1908
|
+
end
|
1909
|
+
|
1910
|
+
|
1911
|
+
class Method < RubyFunction
|
1912
|
+
def register_args(*args)
|
1913
|
+
rb_define_method(*args)
|
1914
|
+
end
|
1915
|
+
end
|
1916
|
+
|
1917
|
+
class ModuleFunction < RubyFunction
|
1918
|
+
def register_args(*args)
|
1919
|
+
rb_define_module_function(*args)
|
1920
|
+
end
|
1921
|
+
end
|
1922
|
+
|
1923
|
+
class GlobalFunction < RubyFunction
|
1924
|
+
def register_args(*args)
|
1925
|
+
rb_define_global_function(*args)
|
1926
|
+
end
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
class SingletonMethod < RubyFunction
|
1930
|
+
def register_args(*args)
|
1931
|
+
rb_define_singleton_method(*args)
|
1932
|
+
end
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
|
1936
|
+
class Structure < CFragment
|
1937
|
+
|
1938
|
+
class InheritAccumulator < Accumulator; include SetAccumulator; end
|
1939
|
+
|
1940
|
+
accumulator(:block) {BlockAccumulator}
|
1941
|
+
accumulator(:inherit) {InheritAccumulator}
|
1942
|
+
accumulator(:declare) {StatementKeyAccumulator}
|
1943
|
+
|
1944
|
+
def initialize name, parent, attribute = nil
|
1945
|
+
super(name, parent)
|
1946
|
+
if attribute
|
1947
|
+
add "typedef struct #{name}", block!, "#{attribute} #{name}"
|
1948
|
+
else
|
1949
|
+
add "typedef struct #{name}", block!, name
|
1950
|
+
end
|
1951
|
+
block inherit!, declare!
|
1952
|
+
end
|
1953
|
+
|
1954
|
+
def separator; " "; end
|
1955
|
+
|
1956
|
+
end # class Structure
|
1957
|
+
|
1958
|
+
|
1959
|
+
OpName= {
|
1960
|
+
'<' => :op_lt,
|
1961
|
+
'<=' => :op_le,
|
1962
|
+
'>' => :op_gt,
|
1963
|
+
'>=' => :op_ge,
|
1964
|
+
'==' => :op_eqeq
|
1965
|
+
}
|
1966
|
+
|
1967
|
+
def CGenerator.make_c_name s
|
1968
|
+
s = s.to_s
|
1969
|
+
OpName[s] || translate_ruby_identifier(s)
|
1970
|
+
end
|
1971
|
+
|
1972
|
+
def CGenerator.translate_ruby_identifier(s)
|
1973
|
+
# For uniqueness, we use a single '_' to indicate our subs
|
1974
|
+
# and translate pre-existing '_' to '__'
|
1975
|
+
# It should be possible to write another method which
|
1976
|
+
# converts the output back to the original.
|
1977
|
+
c_name = s.gsub(/_/, '__')
|
1978
|
+
|
1979
|
+
|
1980
|
+
# Ruby identifiers can include prefix $, @, or @@, or suffix ?, !, or =
|
1981
|
+
# and they can be [] or []=
|
1982
|
+
c_name.gsub!(/\$/, 'global_')
|
1983
|
+
c_name.gsub!(/@/, 'attr_')
|
1984
|
+
c_name.gsub!(/\?/, '_query')
|
1985
|
+
c_name.gsub!(/!/, '_bang')
|
1986
|
+
c_name.gsub!(/=/, '_equals')
|
1987
|
+
c_name.gsub!(/::/, '_')
|
1988
|
+
c_name.gsub!(/\[\]/, '_brackets')
|
1989
|
+
|
1990
|
+
# so that some Ruby expressions can be associated with a name,
|
1991
|
+
# we allow '.' in the str. Eventually, handle more Ruby exprs.
|
1992
|
+
c_name.gsub!(/\./, '_dot_')
|
1993
|
+
|
1994
|
+
# we should also make an attempt to encode special globals
|
1995
|
+
# like $: and $-I
|
1996
|
+
|
1997
|
+
unless c_name =~ /\A[A-Za-z_]\w*\z/
|
1998
|
+
raise SyntaxError,
|
1999
|
+
"Cgen's encoding cannot handle #{s.inspect}; " +
|
2000
|
+
"best try is #{c_name.inspect}."
|
2001
|
+
end
|
2002
|
+
|
2003
|
+
c_name.intern
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
end # module CGenerator
|
2007
|
+
|
2008
|
+
|
2009
|
+
##class Array
|
2010
|
+
## def join_nonempty str
|
2011
|
+
## map { |x|. x.to_s }.reject { |s| s == "" }.join(str)
|
2012
|
+
## end
|
2013
|
+
##end
|
2014
|
+
|
2015
|
+
class String
|
2016
|
+
|
2017
|
+
# tabs left or right by n chars, using spaces
|
2018
|
+
def tab n
|
2019
|
+
if n >= 0
|
2020
|
+
gsub(/^/, ' ' * n)
|
2021
|
+
else
|
2022
|
+
gsub(/^ {0,#{-n}}/, "")
|
2023
|
+
end
|
2024
|
+
end
|
2025
|
+
|
2026
|
+
# preserves relative tabbing
|
2027
|
+
# the first non-empty line ends up with n spaces before nonspace
|
2028
|
+
def tabto n
|
2029
|
+
if self =~ /^( *)\S/
|
2030
|
+
tab(n - $1.length)
|
2031
|
+
else
|
2032
|
+
self
|
2033
|
+
end
|
2034
|
+
end
|
2035
|
+
|
2036
|
+
# aligns each line
|
2037
|
+
def taballto n
|
2038
|
+
gsub(/^ */, ' ' * n)
|
2039
|
+
end
|
2040
|
+
|
2041
|
+
end # class String
|