RubyInline 3.7.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/History.txt +18 -0
  2. data/Rakefile +3 -0
  3. data/lib/inline.rb +243 -86
  4. data/test/test_inline.rb +371 -20
  5. metadata +15 -4
@@ -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
@@ -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.7.0'
68
+ VERSION = '3.8.0'
67
69
 
68
- WINDOZE = /win(32|64)/ =~ RUBY_PLATFORM
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
- # in case both INLINEDIR and HOME aren't defined, and under Windows
89
- # default to HOMEDRIVE + HOMEPATH values
90
- env = ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if env.nil? and WINDOZE
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
- protected unless $TESTING
148
+ include ZenTestMapping
123
149
 
124
150
  MAGIC_ARITY_THRESHOLD = 15
125
151
  MAGIC_ARITY = -1
126
152
 
127
- @@type_map = {
128
- 'char' => [ 'NUM2CHR', 'CHR2FIX' ],
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
- def ruby2c(type)
142
- raise ArgumentError, "Unknown type #{type.inspect}" unless @@type_map.has_key? type
143
- @@type_map[type].first
144
- end
156
+ TYPE_MAP = {
157
+ 'char' => [ 'NUM2CHR', 'CHR2FIX' ],
145
158
 
146
- def c2ruby(type)
147
- raise ArgumentError, "Unknown type #{type.inspect}" unless @@type_map.has_key? type
148
- @@type_map[type].last
149
- end
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|' + @@type_map.keys.map{|x| Regexp.escape(x)}.join('|')
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] || function_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
- if $TESTING then
284
- attr_writer :mod
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
- public
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 = real_caller.split(/:/, 3)[0..1]
299
- @real_caller = real_caller.join ':'
300
- @rb_file = File.expand_path real_caller.first
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
- if @include_ruby_first
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 @@type_map.has_key? type
522
- @@type_map[type] = [r2c, c2r]
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 (with the same
537
- # name). +names_and_types+ is a hash that maps the name of the
538
- # constant to its C type.
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
- self.add_to_init " rb_define_const(c, #{name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
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
 
@@ -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', x.ruby2c("char")
107
- assert_equal 'STR2CSTR', x.ruby2c("char *")
108
- assert_equal 'FIX2INT', x.ruby2c("int")
109
- assert_equal 'NUM2INT', x.ruby2c("long")
110
- assert_equal 'NUM2UINT', x.ruby2c("unsigned int")
111
- assert_equal 'NUM2UINT', x.ruby2c("unsigned long")
112
- assert_equal 'NUM2UINT', x.ruby2c("unsigned")
113
- assert_equal '', x.ruby2c("VALUE")
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', x.c2ruby("char")
123
- assert_equal 'rb_str_new2', x.c2ruby("char *")
124
- assert_equal 'INT2FIX', x.c2ruby("int")
125
- assert_equal 'INT2NUM', x.c2ruby("long")
126
- assert_equal 'UINT2NUM', x.c2ruby("unsigned int")
127
- assert_equal 'UINT2NUM', x.c2ruby("unsigned long")
128
- assert_equal 'UINT2NUM', x.c2ruby("unsigned")
129
- assert_equal '', x.c2ruby("VALUE")
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", 'FIX2INT', 'INT2FIX')
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 UINT2NUM(42)}"
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 = STR2CSTR(_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.7.0
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-06-09 00:00:00 -07:00
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.5.3
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.1.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