RubyInline 3.7.0 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +18 -0
- data/Rakefile +3 -0
- data/lib/inline.rb +243 -86
- data/test/test_inline.rb +371 -20
- metadata +15 -4
data/History.txt
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 3.8.0 / 2008-10-22
|
2
|
+
|
3
|
+
* 8 minor enhancements:
|
4
|
+
|
5
|
+
* Inline::C now uses ZenTestMapping to automatically map C functions to ruby
|
6
|
+
names.
|
7
|
+
* Inline::C#map_c_const can now map to a ruby constant name.
|
8
|
+
* Added accessors for members of Data_Wrap_Structs. See Inline::C#accessor.
|
9
|
+
* Added long long, unsigned long long, off_t types to built-in type map.
|
10
|
+
* Added #alias_type_converter and #remove_type_converter.
|
11
|
+
* Consider MinGW (mingw) as valid Windows platform.
|
12
|
+
* Properly fallback to all the environment options under Windows. #22511.
|
13
|
+
* Properly join path under OS with drive letters. #22292.
|
14
|
+
|
15
|
+
* 1 bug fixes:
|
16
|
+
|
17
|
+
* Inline::C now uses rb_define_alloc_func().
|
18
|
+
|
1
19
|
=== 3.7.0 / 2008-06-09
|
2
20
|
|
3
21
|
* 1 major enhancements:
|
data/Rakefile
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'hoe'
|
5
5
|
|
6
|
+
Hoe.add_include_dirs "../../ZenTest/dev/lib"
|
7
|
+
|
6
8
|
require './lib/inline.rb'
|
7
9
|
|
8
10
|
Hoe.new("RubyInline", Inline::VERSION) do |inline|
|
@@ -11,6 +13,7 @@ Hoe.new("RubyInline", Inline::VERSION) do |inline|
|
|
11
13
|
inline.clean_globs << File.expand_path("~/.ruby_inline")
|
12
14
|
inline.spec_extras[:requirements] =
|
13
15
|
"A POSIX environment and a compiler for your language."
|
16
|
+
inline.extra_deps << 'ZenTest' # ZenTest mapping
|
14
17
|
end
|
15
18
|
|
16
19
|
task :test => :clean
|
data/lib/inline.rb
CHANGED
@@ -52,6 +52,8 @@ require "digest/md5"
|
|
52
52
|
require 'fileutils'
|
53
53
|
require 'rubygems'
|
54
54
|
|
55
|
+
require 'zentest_mapping'
|
56
|
+
|
55
57
|
$TESTING = false unless defined? $TESTING
|
56
58
|
|
57
59
|
class CompilationError < RuntimeError; end
|
@@ -63,9 +65,9 @@ class CompilationError < RuntimeError; end
|
|
63
65
|
# the current namespace.
|
64
66
|
|
65
67
|
module Inline
|
66
|
-
VERSION = '3.
|
68
|
+
VERSION = '3.8.0'
|
67
69
|
|
68
|
-
WINDOZE = /
|
70
|
+
WINDOZE = /mswin|mingw/ =~ RUBY_PLATFORM
|
69
71
|
RUBINIUS = defined? RUBY_ENGINE
|
70
72
|
DEV_NULL = (WINDOZE ? 'nul' : '/dev/null')
|
71
73
|
GEM = (WINDOZE ? 'gem.bat' : 'gem')
|
@@ -82,12 +84,34 @@ module Inline
|
|
82
84
|
|
83
85
|
protected
|
84
86
|
|
87
|
+
# rootdir can be forced using INLINEDIR variable
|
88
|
+
# if not defined, it should store in user HOME folder
|
89
|
+
#
|
90
|
+
# Under Windows user data can be stored in several locations:
|
91
|
+
#
|
92
|
+
# HOME
|
93
|
+
# HOMEDRIVE + HOMEPATH
|
94
|
+
# APPDATA
|
95
|
+
# USERPROFILE
|
96
|
+
#
|
97
|
+
# Perform a check in that other to see if the environment is defined
|
98
|
+
# and if so, use it. only try this on Windows.
|
99
|
+
|
85
100
|
def self.rootdir
|
86
101
|
env = ENV['INLINEDIR'] || ENV['HOME']
|
87
102
|
|
88
|
-
|
89
|
-
|
90
|
-
|
103
|
+
if env.nil? and WINDOZE then
|
104
|
+
# try HOMEDRIVE + HOMEPATH combination
|
105
|
+
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
|
106
|
+
env = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
|
107
|
+
end
|
108
|
+
|
109
|
+
# no HOMEDRIVE? use APPDATA
|
110
|
+
env = ENV['APPDATA'] if env.nil? and ENV['APPDATA']
|
111
|
+
|
112
|
+
# bummer, still no env? then fall to USERPROFILE
|
113
|
+
env = ENV['USERPROFILE'] if env.nil? and ENV['USERPROFILE']
|
114
|
+
end
|
91
115
|
|
92
116
|
if env.nil? then
|
93
117
|
abort "Define INLINEDIR or HOME in your environment and try again"
|
@@ -112,41 +136,46 @@ module Inline
|
|
112
136
|
@@directory
|
113
137
|
end
|
114
138
|
|
139
|
+
##
|
115
140
|
# Inline::C is the default builder used and the only one provided by
|
116
141
|
# Inline. It can be used as a template to write builders for other
|
117
142
|
# languages. It understands type-conversions for the basic types and
|
118
|
-
# can be extended as needed
|
143
|
+
# can be extended as needed using #add_type_converter, #alias_type_converter
|
144
|
+
# and #remove_type_converter.
|
119
145
|
|
120
146
|
class C
|
121
147
|
|
122
|
-
|
148
|
+
include ZenTestMapping
|
123
149
|
|
124
150
|
MAGIC_ARITY_THRESHOLD = 15
|
125
151
|
MAGIC_ARITY = -1
|
126
152
|
|
127
|
-
|
128
|
-
|
129
|
-
'char *' => [ 'STR2CSTR', 'rb_str_new2' ],
|
130
|
-
'double' => [ 'NUM2DBL', 'rb_float_new' ],
|
131
|
-
'int' => [ 'F'+'IX2INT', 'INT2FIX' ],
|
132
|
-
'long' => [ 'NUM2INT', 'INT2NUM' ],
|
133
|
-
'unsigned int' => [ 'NUM2UINT', 'UINT2NUM' ],
|
134
|
-
'unsigned long' => [ 'NUM2UINT', 'UINT2NUM' ],
|
135
|
-
'unsigned' => [ 'NUM2UINT', 'UINT2NUM' ],
|
136
|
-
'VALUE' => [ '', '' ],
|
137
|
-
# Can't do these converters because they conflict with the above:
|
138
|
-
# ID2SYM(x), SYM2ID(x), NUM2DBL(x), F\IX2UINT(x)
|
139
|
-
}
|
153
|
+
##
|
154
|
+
# Default C to ruby and ruby to C type map
|
140
155
|
|
141
|
-
|
142
|
-
|
143
|
-
@@type_map[type].first
|
144
|
-
end
|
156
|
+
TYPE_MAP = {
|
157
|
+
'char' => [ 'NUM2CHR', 'CHR2FIX' ],
|
145
158
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
159
|
+
'char *' => [ 'StringValuePtr', 'rb_str_new2' ],
|
160
|
+
|
161
|
+
'double' => [ 'NUM2DBL', 'rb_float_new' ],
|
162
|
+
|
163
|
+
'int' => [ "FI\X2INT", 'INT2FIX' ],
|
164
|
+
'unsigned int' => [ 'NUM2UINT', 'UINT2NUM' ],
|
165
|
+
'unsigned' => [ 'NUM2UINT', 'UINT2NUM' ],
|
166
|
+
|
167
|
+
'long' => [ 'NUM2LONG', 'LONG2NUM' ],
|
168
|
+
'unsigned long' => [ 'NUM2ULONG', 'ULONG2NUM' ],
|
169
|
+
|
170
|
+
'long long' => [ 'NUM2LL', 'LL2NUM' ],
|
171
|
+
'unsigned long long' => [ 'NUM2ULL', 'ULL2NUM' ],
|
172
|
+
|
173
|
+
'off_t' => [ 'NUM2OFFT', 'OFFT2NUM' ],
|
174
|
+
|
175
|
+
'VALUE' => [ '', '' ],
|
176
|
+
# Can't do these converters because they conflict with the above:
|
177
|
+
# ID2SYM(x), SYM2ID(x), F\IX2UINT(x)
|
178
|
+
}
|
150
179
|
|
151
180
|
def strip_comments(src)
|
152
181
|
# strip c-comments
|
@@ -168,7 +197,7 @@ module Inline
|
|
168
197
|
sig.gsub!(/\s+/, ' ')
|
169
198
|
|
170
199
|
unless defined? @types then
|
171
|
-
@types = 'void|' +
|
200
|
+
@types = 'void|' + @type_map.keys.map{|x| Regexp.escape(x)}.join('|')
|
172
201
|
end
|
173
202
|
|
174
203
|
if /(#{@types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then
|
@@ -209,7 +238,8 @@ module Inline
|
|
209
238
|
|
210
239
|
signature = parse_signature(src, !expand_types)
|
211
240
|
function_name = signature['name']
|
212
|
-
method_name = options[:method_name]
|
241
|
+
method_name = options[:method_name]
|
242
|
+
method_name ||= test_to_normal function_name
|
213
243
|
return_type = signature['return']
|
214
244
|
arity = signature['arity']
|
215
245
|
|
@@ -262,6 +292,68 @@ module Inline
|
|
262
292
|
return result if $TESTING
|
263
293
|
end # def generate
|
264
294
|
|
295
|
+
##
|
296
|
+
# Builds a complete C extension suitable for writing to a file and
|
297
|
+
# compiling.
|
298
|
+
|
299
|
+
def generate_ext
|
300
|
+
ext = []
|
301
|
+
|
302
|
+
if @include_ruby_first
|
303
|
+
@inc.unshift "#include \"ruby.h\""
|
304
|
+
else
|
305
|
+
@inc.push "#include \"ruby.h\""
|
306
|
+
end
|
307
|
+
|
308
|
+
ext << @inc
|
309
|
+
ext << nil
|
310
|
+
ext << @src.join("\n\n")
|
311
|
+
ext << nil
|
312
|
+
ext << nil
|
313
|
+
ext << "#ifdef __cplusplus"
|
314
|
+
ext << "extern \"C\" {"
|
315
|
+
ext << "#endif"
|
316
|
+
ext << " __declspec(dllexport)" if WINDOZE
|
317
|
+
ext << " void Init_#{module_name}() {"
|
318
|
+
ext << " VALUE c = rb_cObject;"
|
319
|
+
|
320
|
+
# TODO: use rb_class2path
|
321
|
+
# ext << " VALUE c = rb_path2class(#{@mod.name.inspect});"
|
322
|
+
ext << @mod.name.split("::").map { |n|
|
323
|
+
" c = rb_const_get(c, rb_intern(\"#{n}\"));"
|
324
|
+
}.join("\n")
|
325
|
+
|
326
|
+
ext << nil
|
327
|
+
|
328
|
+
@sig.keys.sort.each do |name|
|
329
|
+
method = ''
|
330
|
+
arity, singleton, method_name = @sig[name]
|
331
|
+
if singleton then
|
332
|
+
if method_name == 'allocate' then
|
333
|
+
raise "#{@mod}::allocate must have an arity of zero" if arity > 0
|
334
|
+
ext << " rb_define_alloc_func(c, (VALUE(*)(VALUE))#{name});"
|
335
|
+
next
|
336
|
+
end
|
337
|
+
method << " rb_define_singleton_method(c, \"#{method_name}\", "
|
338
|
+
else
|
339
|
+
method << " rb_define_method(c, \"#{method_name}\", "
|
340
|
+
end
|
341
|
+
method << "(VALUE(*)(ANYARGS))#{name}, #{arity});"
|
342
|
+
ext << method
|
343
|
+
end
|
344
|
+
|
345
|
+
ext << @init_extra.join("\n") unless @init_extra.empty?
|
346
|
+
|
347
|
+
ext << nil
|
348
|
+
ext << " }"
|
349
|
+
ext << "#ifdef __cplusplus"
|
350
|
+
ext << "}"
|
351
|
+
ext << "#endif"
|
352
|
+
ext << nil
|
353
|
+
|
354
|
+
ext.join "\n"
|
355
|
+
end
|
356
|
+
|
265
357
|
def module_name
|
266
358
|
unless defined? @module_name then
|
267
359
|
module_name = @mod.name.gsub('::','__')
|
@@ -280,12 +372,14 @@ module Inline
|
|
280
372
|
end
|
281
373
|
|
282
374
|
attr_reader :rb_file, :mod
|
283
|
-
|
284
|
-
|
285
|
-
attr_accessor :src, :sig, :flags, :libs
|
286
|
-
end
|
375
|
+
attr_writer :mod
|
376
|
+
attr_accessor :src, :sig, :flags, :libs, :init_extra
|
287
377
|
|
288
|
-
|
378
|
+
##
|
379
|
+
# Sets the name of the C struct for generating accessors. Used with
|
380
|
+
# #accessor, #reader, #writer.
|
381
|
+
|
382
|
+
attr_accessor :struct_name
|
289
383
|
|
290
384
|
def initialize(mod)
|
291
385
|
raise ArgumentError, "Class/Module arg is required" unless Module === mod
|
@@ -295,9 +389,9 @@ module Inline
|
|
295
389
|
raise "Couldn't discover caller" if stack.empty?
|
296
390
|
real_caller = stack.first
|
297
391
|
real_caller = stack[3] if real_caller =~ /\(eval\)/
|
298
|
-
real_caller
|
299
|
-
|
300
|
-
@rb_file = File.expand_path real_caller
|
392
|
+
real_caller =~ /(.*):(\d+)/
|
393
|
+
real_caller = $1
|
394
|
+
@rb_file = File.expand_path real_caller
|
301
395
|
|
302
396
|
@mod = mod
|
303
397
|
@src = []
|
@@ -307,6 +401,87 @@ module Inline
|
|
307
401
|
@libs = []
|
308
402
|
@init_extra = []
|
309
403
|
@include_ruby_first = true
|
404
|
+
@inherited_methods = {}
|
405
|
+
@struct_name = nil
|
406
|
+
|
407
|
+
@type_map = TYPE_MAP.dup
|
408
|
+
end
|
409
|
+
|
410
|
+
##
|
411
|
+
# Adds a #reader and #writer for a C struct member wrapped via
|
412
|
+
# Data_Wrap_Struct. +method+ is the ruby name to give the accessor,
|
413
|
+
# +type+ is the C type. Unless the C member name is overridden with
|
414
|
+
# +member+, the method name is used as the struct member.
|
415
|
+
#
|
416
|
+
# builder.struct_name = 'MyStruct'
|
417
|
+
# builder.accessor :title, 'char *'
|
418
|
+
# builder.accessor :stream_index, 'int', :index
|
419
|
+
#
|
420
|
+
# The latter accesses MyStruct->index via the stream_index method.
|
421
|
+
|
422
|
+
def accessor(method, type, member = method)
|
423
|
+
reader method, type, member
|
424
|
+
writer method, type, member
|
425
|
+
end
|
426
|
+
|
427
|
+
##
|
428
|
+
# Adds a reader for a C struct member wrapped via Data_Wrap_Struct.
|
429
|
+
# +method+ is the ruby name to give the reader, +type+ is the C type.
|
430
|
+
# Unless the C member name is overridden with +member+, the method
|
431
|
+
# name is used as the struct member. See #accessor for an example.
|
432
|
+
|
433
|
+
def reader(method, type, member = method)
|
434
|
+
raise "struct name not set for reader #{method} #{type}" unless
|
435
|
+
@struct_name
|
436
|
+
|
437
|
+
c <<-C
|
438
|
+
VALUE #{method}() {
|
439
|
+
#{@struct_name} *pointer;
|
440
|
+
|
441
|
+
Data_Get_Struct(self, #{@struct_name}, pointer);
|
442
|
+
|
443
|
+
return #{c2ruby type}(pointer->#{member});
|
444
|
+
}
|
445
|
+
C
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Adds a writer for a C struct member wrapped via Data_Get_Struct.
|
450
|
+
# +method+ is the ruby name to give the writer, +type+ is the C type.
|
451
|
+
# Unless the C member name is overridden with +member+, the method
|
452
|
+
# name is used as the struct member. See #accessor for an example.
|
453
|
+
|
454
|
+
def writer(method, type, member = method)
|
455
|
+
raise "struct name not set for writer #{method} #{type}" unless
|
456
|
+
@struct_name
|
457
|
+
|
458
|
+
c <<-C
|
459
|
+
VALUE #{method}_equals(VALUE value) {
|
460
|
+
#{@struct_name} *pointer;
|
461
|
+
|
462
|
+
Data_Get_Struct(self, #{@struct_name}, pointer);
|
463
|
+
|
464
|
+
pointer->#{member} = #{ruby2c type}(value);
|
465
|
+
|
466
|
+
return value;
|
467
|
+
}
|
468
|
+
C
|
469
|
+
end
|
470
|
+
|
471
|
+
##
|
472
|
+
# Converts ruby type +type+ to a C type
|
473
|
+
|
474
|
+
def ruby2c(type)
|
475
|
+
raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
|
476
|
+
@type_map[type].first
|
477
|
+
end
|
478
|
+
|
479
|
+
##
|
480
|
+
# Converts C type +type+ to a ruby type
|
481
|
+
|
482
|
+
def c2ruby(type)
|
483
|
+
raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
|
484
|
+
@type_map[type].last
|
310
485
|
end
|
311
486
|
|
312
487
|
##
|
@@ -348,48 +523,7 @@ module Inline
|
|
348
523
|
src_name = "#{Inline.directory}/#{module_name}.c"
|
349
524
|
old_src_name = "#{src_name}.old"
|
350
525
|
should_compare = File.write_with_backup(src_name) do |io|
|
351
|
-
|
352
|
-
@inc.unshift "#include \"ruby.h\""
|
353
|
-
else
|
354
|
-
@inc.push "#include \"ruby.h\""
|
355
|
-
end
|
356
|
-
|
357
|
-
io.puts
|
358
|
-
io.puts @inc.join("\n")
|
359
|
-
io.puts
|
360
|
-
io.puts @src.join("\n\n")
|
361
|
-
io.puts
|
362
|
-
io.puts
|
363
|
-
io.puts "#ifdef __cplusplus"
|
364
|
-
io.puts "extern \"C\" {"
|
365
|
-
io.puts "#endif"
|
366
|
-
io.puts " __declspec(dllexport)" if WINDOZE
|
367
|
-
io.puts " void Init_#{module_name}() {"
|
368
|
-
io.puts " VALUE c = rb_cObject;"
|
369
|
-
|
370
|
-
# TODO: use rb_class2path
|
371
|
-
# io.puts " VALUE c = rb_path2class(#{@mod.name.inspect});"
|
372
|
-
io.puts @mod.name.split("::").map { |n|
|
373
|
-
" c = rb_const_get(c,rb_intern(\"#{n}\"));"
|
374
|
-
}.join("\n")
|
375
|
-
|
376
|
-
@sig.keys.sort.each do |name|
|
377
|
-
arity, singleton, method_name = @sig[name]
|
378
|
-
if singleton then
|
379
|
-
io.print " rb_define_singleton_method(c, \"#{method_name}\", "
|
380
|
-
else
|
381
|
-
io.print " rb_define_method(c, \"#{method_name}\", "
|
382
|
-
end
|
383
|
-
io.puts "(VALUE(*)(ANYARGS))#{name}, #{arity});"
|
384
|
-
end
|
385
|
-
io.puts @init_extra.join("\n") unless @init_extra.empty?
|
386
|
-
|
387
|
-
io.puts
|
388
|
-
io.puts " }"
|
389
|
-
io.puts "#ifdef __cplusplus"
|
390
|
-
io.puts "}"
|
391
|
-
io.puts "#endif"
|
392
|
-
io.puts
|
526
|
+
io.puts generate_ext
|
393
527
|
end
|
394
528
|
|
395
529
|
# recompile only if the files are different
|
@@ -518,8 +652,25 @@ module Inline
|
|
518
652
|
# Registers C type-casts +r2c+ and +c2r+ for +type+.
|
519
653
|
|
520
654
|
def add_type_converter(type, r2c, c2r)
|
521
|
-
warn "WAR\NING: overridding #{type} on #{caller[0]}" if
|
522
|
-
|
655
|
+
warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
|
656
|
+
@type_map[type] = [r2c, c2r]
|
657
|
+
end
|
658
|
+
|
659
|
+
##
|
660
|
+
# Registers C type +alias_type+ as an alias of +existing_type+
|
661
|
+
|
662
|
+
def alias_type_converter(existing_type, alias_type)
|
663
|
+
warn "WAR\NING: overridding #{type} on #{caller[0]}" if
|
664
|
+
@type_map.has_key? alias_type
|
665
|
+
|
666
|
+
@type_map[alias_type] = @type_map[existing_type]
|
667
|
+
end
|
668
|
+
|
669
|
+
##
|
670
|
+
# Unregisters C type-casts for +type+.
|
671
|
+
|
672
|
+
def remove_type_converter(type)
|
673
|
+
@type_map.delete type
|
523
674
|
end
|
524
675
|
|
525
676
|
##
|
@@ -533,13 +684,19 @@ module Inline
|
|
533
684
|
end
|
534
685
|
|
535
686
|
##
|
536
|
-
# Maps a C constant to ruby
|
537
|
-
# name
|
538
|
-
#
|
687
|
+
# Maps a C constant to ruby. +names_and_types+ is a hash that maps the
|
688
|
+
# name of the constant to its C type.
|
689
|
+
#
|
690
|
+
# builder.map_c_const :C_NAME => :int
|
691
|
+
#
|
692
|
+
# If you wish to give the constant a different ruby name:
|
693
|
+
#
|
694
|
+
# builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
|
539
695
|
|
540
696
|
def map_c_const(names_and_types)
|
541
697
|
names_and_types.each do |name, typ|
|
542
|
-
|
698
|
+
typ, ruby_name = Array === typ ? typ : [typ, name]
|
699
|
+
self.add_to_init " rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
|
543
700
|
end
|
544
701
|
end
|
545
702
|
|
data/test/test_inline.rb
CHANGED
@@ -16,7 +16,6 @@ class InlineTestCase < Test::Unit::TestCase
|
|
16
16
|
@rootdir = File.join(Dir.tmpdir, "test_inline.#{$$}")
|
17
17
|
Dir.mkdir @rootdir, 0700 unless test ?d, @rootdir
|
18
18
|
ENV['INLINEDIR'] = @rootdir
|
19
|
-
Dir.mkdir Inline.directory, 0700
|
20
19
|
end
|
21
20
|
|
22
21
|
def teardown
|
@@ -101,16 +100,265 @@ class TestC < InlineTestCase
|
|
101
100
|
assert_equal [], x.libs
|
102
101
|
end
|
103
102
|
|
103
|
+
def test_accessor
|
104
|
+
builder = Inline::C.new self.class
|
105
|
+
|
106
|
+
builder.struct_name = 'MyStruct'
|
107
|
+
builder.accessor 'method_name', 'int'
|
108
|
+
|
109
|
+
source = util_strip_lines builder.src
|
110
|
+
|
111
|
+
expected = []
|
112
|
+
expected << <<-READER
|
113
|
+
# line N "./lib/inline.rb"
|
114
|
+
static VALUE method_name(VALUE self) {
|
115
|
+
|
116
|
+
MyStruct *pointer;
|
117
|
+
|
118
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
119
|
+
|
120
|
+
return (INT2FIX(pointer->method_name));
|
121
|
+
}
|
122
|
+
READER
|
123
|
+
|
124
|
+
expected << <<-WRITER
|
125
|
+
# line N "./lib/inline.rb"
|
126
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
127
|
+
VALUE value = (_value);
|
128
|
+
|
129
|
+
MyStruct *pointer;
|
130
|
+
|
131
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
132
|
+
|
133
|
+
pointer->method_name = FIX2INT(value);
|
134
|
+
|
135
|
+
return (value);
|
136
|
+
}
|
137
|
+
WRITER
|
138
|
+
|
139
|
+
assert_equal expected, source
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_accessor_member_name
|
143
|
+
builder = Inline::C.new self.class
|
144
|
+
|
145
|
+
builder.struct_name = 'MyStruct'
|
146
|
+
builder.accessor 'method_name', 'int', 'member_name'
|
147
|
+
|
148
|
+
source = util_strip_lines builder.src
|
149
|
+
|
150
|
+
expected = []
|
151
|
+
expected << <<-READER
|
152
|
+
# line N "./lib/inline.rb"
|
153
|
+
static VALUE method_name(VALUE self) {
|
154
|
+
|
155
|
+
MyStruct *pointer;
|
156
|
+
|
157
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
158
|
+
|
159
|
+
return (INT2FIX(pointer->member_name));
|
160
|
+
}
|
161
|
+
READER
|
162
|
+
|
163
|
+
expected << <<-WRITER
|
164
|
+
# line N "./lib/inline.rb"
|
165
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
166
|
+
VALUE value = (_value);
|
167
|
+
|
168
|
+
MyStruct *pointer;
|
169
|
+
|
170
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
171
|
+
|
172
|
+
pointer->member_name = FIX2INT(value);
|
173
|
+
|
174
|
+
return (value);
|
175
|
+
}
|
176
|
+
WRITER
|
177
|
+
|
178
|
+
assert_equal expected, source
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_accessor_no_struct_name
|
182
|
+
builder = Inline::C.new self.class
|
183
|
+
|
184
|
+
e = assert_raises RuntimeError do
|
185
|
+
builder.accessor 'method_name', 'int'
|
186
|
+
end
|
187
|
+
|
188
|
+
assert_equal "struct name not set for reader method_name int", e.message
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_add_type_converter
|
192
|
+
builder = Inline::C.new self.class
|
193
|
+
|
194
|
+
builder.add_type_converter 'my_type', 'ruby_type2my_type',
|
195
|
+
'my_type2ruby_type'
|
196
|
+
|
197
|
+
assert_equal 'my_type2ruby_type', builder.c2ruby('my_type')
|
198
|
+
assert_equal 'ruby_type2my_type', builder.ruby2c('my_type')
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_alias_type_converter
|
202
|
+
builder = Inline::C.new self.class
|
203
|
+
|
204
|
+
builder.alias_type_converter 'long long', 'int64_t'
|
205
|
+
|
206
|
+
assert_equal 'LL2NUM', builder.c2ruby('int64_t')
|
207
|
+
assert_equal 'NUM2LL', builder.ruby2c('int64_t')
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_reader
|
211
|
+
builder = Inline::C.new self.class
|
212
|
+
|
213
|
+
builder.struct_name = 'MyStruct'
|
214
|
+
builder.reader 'method_name', 'int'
|
215
|
+
|
216
|
+
source = util_strip_lines builder.src
|
217
|
+
|
218
|
+
expected = []
|
219
|
+
expected << <<-READER
|
220
|
+
# line N "./lib/inline.rb"
|
221
|
+
static VALUE method_name(VALUE self) {
|
222
|
+
|
223
|
+
MyStruct *pointer;
|
224
|
+
|
225
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
226
|
+
|
227
|
+
return (INT2FIX(pointer->method_name));
|
228
|
+
}
|
229
|
+
READER
|
230
|
+
|
231
|
+
assert_equal expected, source
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_reader_member_name
|
235
|
+
builder = Inline::C.new self.class
|
236
|
+
|
237
|
+
builder.struct_name = 'MyStruct'
|
238
|
+
builder.reader 'method_name', 'int', 'member_name'
|
239
|
+
|
240
|
+
source = util_strip_lines builder.src
|
241
|
+
|
242
|
+
expected = []
|
243
|
+
expected << <<-READER
|
244
|
+
# line N "./lib/inline.rb"
|
245
|
+
static VALUE method_name(VALUE self) {
|
246
|
+
|
247
|
+
MyStruct *pointer;
|
248
|
+
|
249
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
250
|
+
|
251
|
+
return (INT2FIX(pointer->member_name));
|
252
|
+
}
|
253
|
+
READER
|
254
|
+
|
255
|
+
assert_equal expected, source
|
256
|
+
end
|
257
|
+
|
258
|
+
def test_reader_no_struct_name
|
259
|
+
builder = Inline::C.new self.class
|
260
|
+
|
261
|
+
e = assert_raises RuntimeError do
|
262
|
+
builder.reader 'method_name', 'int'
|
263
|
+
end
|
264
|
+
|
265
|
+
assert_equal "struct name not set for reader method_name int", e.message
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_remove_type_converter
|
269
|
+
builder = Inline::C.new self.class
|
270
|
+
|
271
|
+
builder.remove_type_converter 'long'
|
272
|
+
|
273
|
+
assert_raises ArgumentError do
|
274
|
+
builder.c2ruby 'long'
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_writer
|
279
|
+
builder = Inline::C.new self.class
|
280
|
+
|
281
|
+
builder.struct_name = 'MyStruct'
|
282
|
+
builder.writer 'method_name', 'int'
|
283
|
+
|
284
|
+
source = util_strip_lines builder.src
|
285
|
+
|
286
|
+
expected = []
|
287
|
+
expected << <<-WRITER
|
288
|
+
# line N "./lib/inline.rb"
|
289
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
290
|
+
VALUE value = (_value);
|
291
|
+
|
292
|
+
MyStruct *pointer;
|
293
|
+
|
294
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
295
|
+
|
296
|
+
pointer->method_name = FIX2INT(value);
|
297
|
+
|
298
|
+
return (value);
|
299
|
+
}
|
300
|
+
WRITER
|
301
|
+
|
302
|
+
assert_equal expected, source
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_writer_member_name
|
306
|
+
builder = Inline::C.new self.class
|
307
|
+
|
308
|
+
builder.struct_name = 'MyStruct'
|
309
|
+
builder.writer 'method_name', 'int', 'member_name'
|
310
|
+
|
311
|
+
source = util_strip_lines builder.src
|
312
|
+
|
313
|
+
expected = []
|
314
|
+
expected << <<-WRITER
|
315
|
+
# line N "./lib/inline.rb"
|
316
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
317
|
+
VALUE value = (_value);
|
318
|
+
|
319
|
+
MyStruct *pointer;
|
320
|
+
|
321
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
322
|
+
|
323
|
+
pointer->member_name = FIX2INT(value);
|
324
|
+
|
325
|
+
return (value);
|
326
|
+
}
|
327
|
+
WRITER
|
328
|
+
|
329
|
+
assert_equal expected, source
|
330
|
+
end
|
331
|
+
|
332
|
+
def test_writer_no_struct_name
|
333
|
+
builder = Inline::C.new self.class
|
334
|
+
|
335
|
+
e = assert_raises RuntimeError do
|
336
|
+
builder.writer 'method_name', 'int'
|
337
|
+
end
|
338
|
+
|
339
|
+
assert_equal "struct name not set for writer method_name int", e.message
|
340
|
+
end
|
341
|
+
|
104
342
|
def test_ruby2c
|
105
343
|
x = Inline::C.new(self.class)
|
106
|
-
assert_equal 'NUM2CHR',
|
107
|
-
assert_equal '
|
108
|
-
|
109
|
-
assert_equal
|
110
|
-
assert_equal 'NUM2UINT',
|
111
|
-
assert_equal 'NUM2UINT',
|
112
|
-
|
113
|
-
assert_equal '',
|
344
|
+
assert_equal 'NUM2CHR', x.ruby2c("char")
|
345
|
+
assert_equal 'StringValuePtr', x.ruby2c("char *")
|
346
|
+
|
347
|
+
assert_equal "FI\X2INT", x.ruby2c("int")
|
348
|
+
assert_equal 'NUM2UINT', x.ruby2c("unsigned")
|
349
|
+
assert_equal 'NUM2UINT', x.ruby2c("unsigned int")
|
350
|
+
|
351
|
+
assert_equal 'NUM2LONG', x.ruby2c("long")
|
352
|
+
assert_equal 'NUM2ULONG', x.ruby2c("unsigned long")
|
353
|
+
|
354
|
+
assert_equal 'NUM2LL', x.ruby2c("long long")
|
355
|
+
assert_equal 'NUM2ULL', x.ruby2c("unsigned long long")
|
356
|
+
|
357
|
+
assert_equal 'NUM2DBL', x.ruby2c("double")
|
358
|
+
|
359
|
+
assert_equal 'NUM2OFFT', x.ruby2c("off_t")
|
360
|
+
|
361
|
+
assert_equal '', x.ruby2c("VALUE")
|
114
362
|
|
115
363
|
assert_raises ArgumentError do
|
116
364
|
x.ruby2c('blah')
|
@@ -119,14 +367,25 @@ class TestC < InlineTestCase
|
|
119
367
|
|
120
368
|
def test_c2ruby
|
121
369
|
x = Inline::C.new(self.class)
|
122
|
-
assert_equal 'CHR2FIX',
|
123
|
-
|
124
|
-
assert_equal '
|
125
|
-
|
126
|
-
assert_equal '
|
127
|
-
assert_equal 'UINT2NUM',
|
128
|
-
assert_equal 'UINT2NUM',
|
129
|
-
|
370
|
+
assert_equal 'CHR2FIX', x.c2ruby("char")
|
371
|
+
|
372
|
+
assert_equal 'rb_str_new2', x.c2ruby("char *")
|
373
|
+
|
374
|
+
assert_equal 'INT2FIX', x.c2ruby("int")
|
375
|
+
assert_equal 'UINT2NUM', x.c2ruby("unsigned int")
|
376
|
+
assert_equal 'UINT2NUM', x.c2ruby("unsigned")
|
377
|
+
|
378
|
+
assert_equal 'LONG2NUM', x.c2ruby("long")
|
379
|
+
assert_equal 'ULONG2NUM', x.c2ruby("unsigned long")
|
380
|
+
|
381
|
+
assert_equal 'LL2NUM', x.c2ruby("long long")
|
382
|
+
assert_equal 'ULL2NUM', x.c2ruby("unsigned long long")
|
383
|
+
|
384
|
+
assert_equal 'rb_float_new', x.c2ruby("double")
|
385
|
+
|
386
|
+
assert_equal 'OFFT2NUM', x.c2ruby("off_t")
|
387
|
+
|
388
|
+
assert_equal '', x.c2ruby("VALUE")
|
130
389
|
|
131
390
|
assert_raises ArgumentError do
|
132
391
|
x.c2ruby('blah')
|
@@ -244,11 +503,12 @@ class TestC < InlineTestCase
|
|
244
503
|
|
245
504
|
|
246
505
|
util_parse_signature(src, expected,
|
247
|
-
"register int",
|
506
|
+
"register int", "FI\X2INT", "INT2FI\X")
|
248
507
|
end
|
249
508
|
|
250
509
|
def util_generate(src, expected, expand_types=true)
|
251
510
|
result = @builder.generate src, expand_types
|
511
|
+
result = util_strip_lines result
|
252
512
|
result.gsub!(/\# line \d+/, '# line N')
|
253
513
|
expected = "# line N \"#{$0}\"\n" + expected
|
254
514
|
assert_equal(expected, result)
|
@@ -258,6 +518,17 @@ class TestC < InlineTestCase
|
|
258
518
|
util_generate(src, expected, false)
|
259
519
|
end
|
260
520
|
|
521
|
+
def util_strip_lines(src)
|
522
|
+
case src
|
523
|
+
when String then
|
524
|
+
src.gsub(/\# line \d+/, '# line N')
|
525
|
+
when Array then
|
526
|
+
src.map do |chunk|
|
527
|
+
util_strip_lines chunk
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
261
532
|
# Ruby Arity Rules, from the mouth of Matz:
|
262
533
|
# -2 = ruby array argv
|
263
534
|
# -1 = c array argv
|
@@ -409,6 +680,16 @@ static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
|
409
680
|
util_generate(src, expected)
|
410
681
|
end
|
411
682
|
|
683
|
+
def test_generate_map_name
|
684
|
+
src = "VALUE y_equals(VALUE self) {blah;}"
|
685
|
+
|
686
|
+
expected = "static VALUE y_equals(VALUE self) {blah;}"
|
687
|
+
|
688
|
+
util_generate_raw(src, expected)
|
689
|
+
|
690
|
+
assert_equal [-1, nil, 'y='], @builder.sig['y_equals']
|
691
|
+
end
|
692
|
+
|
412
693
|
def test_generate_system_header
|
413
694
|
src = "// stupid cpp comment
|
414
695
|
#include <header>
|
@@ -433,7 +714,7 @@ static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
|
433
714
|
src = "unsigned\nlong z(void) {return 42}"
|
434
715
|
|
435
716
|
expected = "static VALUE z(VALUE self) {
|
436
|
-
return
|
717
|
+
return ULONG2NUM(42)}"
|
437
718
|
|
438
719
|
util_generate(src, expected)
|
439
720
|
end
|
@@ -453,12 +734,62 @@ return INT2FIX(x+y)}"
|
|
453
734
|
src = "char\n\*\n blah( char*s) {puts(s); return s}"
|
454
735
|
|
455
736
|
expected = "static VALUE blah(VALUE self, VALUE _s) {
|
456
|
-
char * s =
|
737
|
+
char * s = StringValuePtr(_s);
|
457
738
|
puts(s); return rb_str_new2(s)}"
|
458
739
|
|
459
740
|
util_generate(src, expected)
|
460
741
|
end
|
461
742
|
|
743
|
+
def test_generate_ext
|
744
|
+
@builder.c_singleton "VALUE allocate() { return Qnil; }"
|
745
|
+
|
746
|
+
@builder.c "VALUE my_method() { return Qnil; }"
|
747
|
+
|
748
|
+
windoze = "\n __declspec(dllexport)" if Inline::WINDOZE
|
749
|
+
|
750
|
+
expected = <<-EXT
|
751
|
+
#include "ruby.h"
|
752
|
+
|
753
|
+
# line N "./test/test_inline.rb"
|
754
|
+
static VALUE allocate(VALUE self) {
|
755
|
+
return (Qnil); }
|
756
|
+
|
757
|
+
# line N "./test/test_inline.rb"
|
758
|
+
static VALUE my_method(VALUE self) {
|
759
|
+
return (Qnil); }
|
760
|
+
|
761
|
+
|
762
|
+
#ifdef __cplusplus
|
763
|
+
extern \"C\" {
|
764
|
+
#endif#{windoze}
|
765
|
+
void Init_Inline_TestInline__TestC_eba5() {
|
766
|
+
VALUE c = rb_cObject;
|
767
|
+
c = rb_const_get(c, rb_intern("TestInline"));
|
768
|
+
c = rb_const_get(c, rb_intern("TestC"));
|
769
|
+
|
770
|
+
rb_define_alloc_func(c, (VALUE(*)(VALUE))allocate);
|
771
|
+
rb_define_method(c, "my_method", (VALUE(*)(ANYARGS))my_method, 0);
|
772
|
+
|
773
|
+
}
|
774
|
+
#ifdef __cplusplus
|
775
|
+
}
|
776
|
+
#endif
|
777
|
+
EXT
|
778
|
+
|
779
|
+
assert_equal expected, util_strip_lines(@builder.generate_ext)
|
780
|
+
end
|
781
|
+
|
782
|
+
def test_generate_ext_bad_allocate
|
783
|
+
@builder.c_singleton "VALUE allocate(VALUE bad) { return Qnil; }"
|
784
|
+
|
785
|
+
e = assert_raise RuntimeError do
|
786
|
+
@builder.generate_ext
|
787
|
+
end
|
788
|
+
|
789
|
+
assert_equal 'TestInline::TestC::allocate must have an arity of zero',
|
790
|
+
e.message
|
791
|
+
end
|
792
|
+
|
462
793
|
def test_c
|
463
794
|
result = @builder.c "int add(int a, int b) { return a + b; }"
|
464
795
|
|
@@ -481,6 +812,26 @@ puts(s); return rb_str_new2(s)}"
|
|
481
812
|
assert_equal [expected], @builder.src
|
482
813
|
end
|
483
814
|
|
815
|
+
def test_map_c_const
|
816
|
+
@builder.map_c_const :C_NAME => :int
|
817
|
+
|
818
|
+
expected = [
|
819
|
+
" rb_define_const(c, \"C_NAME\", INT2FIX(C_NAME));"
|
820
|
+
]
|
821
|
+
|
822
|
+
assert_equal expected, @builder.init_extra
|
823
|
+
end
|
824
|
+
|
825
|
+
def test_map_c_const_named
|
826
|
+
@builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
|
827
|
+
|
828
|
+
expected = [
|
829
|
+
" rb_define_const(c, \"RUBY_NAME\", INT2FIX(C_NAME));"
|
830
|
+
]
|
831
|
+
|
832
|
+
assert_equal expected, @builder.init_extra
|
833
|
+
end
|
834
|
+
|
484
835
|
# TODO: fix ?
|
485
836
|
def util_simple_code(klassname, c_src)
|
486
837
|
result = "
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: RubyInline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Davis
|
@@ -9,17 +9,28 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-10-22 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ZenTest
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
15
25
|
- !ruby/object:Gem::Dependency
|
16
26
|
name: hoe
|
27
|
+
type: :development
|
17
28
|
version_requirement:
|
18
29
|
version_requirements: !ruby/object:Gem::Requirement
|
19
30
|
requirements:
|
20
31
|
- - ">="
|
21
32
|
- !ruby/object:Gem::Version
|
22
|
-
version: 1.
|
33
|
+
version: 1.8.0
|
23
34
|
version:
|
24
35
|
description: Inline allows you to write foreign code within your ruby code. It automatically determines if the code in question has changed and builds it only when necessary. The extensions are then automatically loaded into the class/module that defines it. You can even write extra builders that will allow you to write inlined code in any language. Use Inline::C as a template and look at Module#inline for the required API.
|
25
36
|
email:
|
@@ -68,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
79
|
requirements:
|
69
80
|
- A POSIX environment and a compiler for your language.
|
70
81
|
rubyforge_project: rubyinline
|
71
|
-
rubygems_version: 1.
|
82
|
+
rubygems_version: 1.3.0
|
72
83
|
signing_key:
|
73
84
|
specification_version: 2
|
74
85
|
summary: Inline allows you to write foreign code within your ruby code
|