RubyInline 3.3.1 → 3.3.2
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/History.txt +8 -0
- data/README.txt +1 -0
- data/inline.rb +84 -48
- data/test_inline.rb +37 -9
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
*** 3.3.2 / 2005-05-19
|
2
|
+
|
3
|
+
+ 4 bug fixes
|
4
|
+
+ Fixed a line number issue when using c_raw w/ multi-line signatures.
|
5
|
+
+ Inline can now be invoked multiple times per class.
|
6
|
+
+ Filled out rdoc.
|
7
|
+
+ Fixed some old tests.
|
8
|
+
|
1
9
|
*** 3.3.1 / 2005-05-03
|
2
10
|
|
3
11
|
+ 3 bug fixes
|
data/README.txt
CHANGED
data/inline.rb
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
# = DESCRIPTION
|
24
24
|
#
|
25
|
-
# Inline allows you to write
|
25
|
+
# Inline allows you to write foreign code within your ruby code. It
|
26
26
|
# automatically determines if the code in question has changed and
|
27
27
|
# builds it only when necessary. The extensions are then automatically
|
28
28
|
# loaded into the class/module that defines it.
|
@@ -51,7 +51,7 @@ class CompilationError < RuntimeError; end
|
|
51
51
|
# the current namespace.
|
52
52
|
|
53
53
|
module Inline
|
54
|
-
VERSION = '3.3.
|
54
|
+
VERSION = '3.3.2'
|
55
55
|
|
56
56
|
$stderr.puts "RubyInline v #{VERSION}" if $DEBUG
|
57
57
|
|
@@ -204,7 +204,7 @@ module Inline
|
|
204
204
|
prefix += " #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
|
205
205
|
end
|
206
206
|
end
|
207
|
-
# replace the function signature (hopefully) with new
|
207
|
+
# replace the function signature (hopefully) with new sig (prefix)
|
208
208
|
result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
|
209
209
|
result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
|
210
210
|
unless return_type == "void" then
|
@@ -222,8 +222,15 @@ module Inline
|
|
222
222
|
result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
|
223
223
|
end
|
224
224
|
|
225
|
+
delta = if result =~ /\A(static.*?\{)/m then
|
226
|
+
$1.split(/\n/).size
|
227
|
+
else
|
228
|
+
warn "WARNING: Can't find signature in #{result.inspect}\n" unless $TESTING
|
229
|
+
0
|
230
|
+
end
|
231
|
+
|
225
232
|
file, line = caller[1].split(/:/)
|
226
|
-
result = "# line #{line.to_i
|
233
|
+
result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless $DEBUG
|
227
234
|
|
228
235
|
@src << result
|
229
236
|
@sig[function_name] = [arity,singleton]
|
@@ -231,13 +238,49 @@ module Inline
|
|
231
238
|
return result if $TESTING
|
232
239
|
end # def generate
|
233
240
|
|
241
|
+
def module_name
|
242
|
+
unless defined? @module_name then
|
243
|
+
module_name = @mod.name.gsub('::','__')
|
244
|
+
md5 = Digest::MD5.new
|
245
|
+
@sig.keys.sort_by{|x| x.to_s}.each { |m| md5 << m.to_s }
|
246
|
+
@module_name = "Inline_#{module_name}_#{md5.to_s[0,4]}"
|
247
|
+
end
|
248
|
+
@module_name
|
249
|
+
end
|
250
|
+
|
251
|
+
def so_name
|
252
|
+
unless defined? @so_name then
|
253
|
+
@so_name = "#{Inline.directory}/#{module_name}.#{Config::CONFIG["DLEXT"]}"
|
254
|
+
end
|
255
|
+
@so_name
|
256
|
+
end
|
257
|
+
|
258
|
+
attr_reader :rb_file, :mod
|
234
259
|
attr_accessor :mod, :src, :sig, :flags, :libs if $TESTING
|
235
260
|
|
236
261
|
public
|
237
262
|
|
263
|
+
def initialize(mod)
|
264
|
+
raise ArgumentError, "Class/Module arg is required" unless Module === mod
|
265
|
+
|
266
|
+
real_caller = caller[2] # new -> inline -> real_caller|eval
|
267
|
+
real_caller = caller[5] if real_caller =~ /\(eval\)/
|
268
|
+
@real_caller = real_caller.split(/:/).first
|
269
|
+
@rb_file = File.expand_path(@real_caller)
|
270
|
+
|
271
|
+
@mod = mod
|
272
|
+
@src = []
|
273
|
+
@sig = {}
|
274
|
+
@flags = []
|
275
|
+
@libs = []
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Attempts to load pre-generated code returning true if it succeeds.
|
280
|
+
|
238
281
|
def load_cache
|
239
282
|
begin
|
240
|
-
file = File.join("inline", File.basename(
|
283
|
+
file = File.join("inline", File.basename(so_name))
|
241
284
|
if require file then
|
242
285
|
dir = Inline.directory
|
243
286
|
warn "WARNING: #{dir} exists but is not being used" if test ?d, dir
|
@@ -248,20 +291,23 @@ module Inline
|
|
248
291
|
return false
|
249
292
|
end
|
250
293
|
|
294
|
+
##
|
295
|
+
# Loads the generated code back into ruby
|
296
|
+
|
251
297
|
def load
|
252
|
-
require "#{
|
298
|
+
require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
|
253
299
|
end
|
254
300
|
|
301
|
+
##
|
302
|
+
# Builds the source file, if needed, and attempts to compile it.
|
303
|
+
|
255
304
|
def build
|
256
|
-
|
257
|
-
|
258
|
-
real_caller = real_caller.split(/:/).first
|
259
|
-
@rb_file = File.expand_path(real_caller) # [MS]
|
260
|
-
so_exists = File.file? @so_name
|
305
|
+
so_name = self.so_name
|
306
|
+
so_exists = File.file? so_name
|
261
307
|
|
262
|
-
unless so_exists and File.mtime(
|
308
|
+
unless so_exists and File.mtime(rb_file) < File.mtime(so_name)
|
263
309
|
|
264
|
-
src_name = "#{Inline.directory}/#{
|
310
|
+
src_name = "#{Inline.directory}/#{module_name}.c"
|
265
311
|
old_src_name = "#{src_name}.old"
|
266
312
|
should_compare = File.write_with_backup(src_name) do |io|
|
267
313
|
io.puts
|
@@ -273,8 +319,9 @@ module Inline
|
|
273
319
|
io.puts "#ifdef __cplusplus"
|
274
320
|
io.puts "extern \"C\" {"
|
275
321
|
io.puts "#endif"
|
276
|
-
io.puts " void Init_#{
|
322
|
+
io.puts " void Init_#{module_name}() {"
|
277
323
|
io.puts " VALUE c = rb_cObject;"
|
324
|
+
# TODO: use rb_class2path
|
278
325
|
@mod.name.split("::").each do |n|
|
279
326
|
io.puts " c = rb_const_get_at(c,rb_intern(\"#{n}\"));"
|
280
327
|
end
|
@@ -305,7 +352,7 @@ module Inline
|
|
305
352
|
# Prevents us from entering this conditional unless the source
|
306
353
|
# file changes again.
|
307
354
|
t = Time.now
|
308
|
-
File.utime(t, t, src_name, old_src_name,
|
355
|
+
File.utime(t, t, src_name, old_src_name, so_name)
|
309
356
|
end
|
310
357
|
|
311
358
|
if recompile then
|
@@ -327,18 +374,18 @@ module Inline
|
|
327
374
|
libs = @libs.join(' ')
|
328
375
|
libs += " #{$INLINE_LIBS}" if defined? $INLINE_LIBS # DEPRECATE
|
329
376
|
|
330
|
-
cmd = "#{Config::CONFIG['LDSHARED']} #{flags} #{Config::CONFIG['CFLAGS']} -I #{hdrdir} -o #{
|
377
|
+
cmd = "#{Config::CONFIG['LDSHARED']} #{flags} #{Config::CONFIG['CFLAGS']} -I #{hdrdir} -o #{so_name} #{src_name} #{libs}"
|
331
378
|
|
332
379
|
case RUBY_PLATFORM
|
333
380
|
when /mswin32/ then
|
334
|
-
cmd += " -link /INCREMENTAL:no /EXPORT:Init_#{
|
381
|
+
cmd += " -link /INCREMENTAL:no /EXPORT:Init_#{module_name}"
|
335
382
|
when /i386-cygwin/ then
|
336
383
|
cmd += ' -L/usr/local/lib -lruby.dll'
|
337
384
|
end
|
338
385
|
|
339
386
|
cmd += " 2> /dev/null" if $TESTING
|
340
387
|
|
341
|
-
$stderr.puts "Building #{
|
388
|
+
$stderr.puts "Building #{so_name} with '#{cmd}'" if $DEBUG
|
342
389
|
`#{cmd}`
|
343
390
|
if $? != 0 then
|
344
391
|
bad_src_name = src_name + ".bad"
|
@@ -349,36 +396,11 @@ module Inline
|
|
349
396
|
end
|
350
397
|
|
351
398
|
else
|
352
|
-
$stderr.puts "#{
|
399
|
+
$stderr.puts "#{so_name} is up to date" if $DEBUG
|
353
400
|
end # unless (file is out of date)
|
354
401
|
end # def build
|
355
402
|
|
356
|
-
|
357
|
-
def initialize(mod)
|
358
|
-
@mod = mod
|
359
|
-
if @mod then
|
360
|
-
# Figure out which script file defined the C code
|
361
|
-
|
362
|
-
real_caller = caller[2]
|
363
|
-
real_caller = caller[5] if real_caller =~ /\(eval\)/
|
364
|
-
real_caller = real_caller.split(/:/).first
|
365
|
-
|
366
|
-
@rb_file = File.expand_path(real_caller) # [MS]
|
367
|
-
|
368
|
-
# Extract the basename of the script and clean it up to be
|
369
|
-
# a valid C identifier
|
370
|
-
rb_script_name = File.basename(@rb_file).gsub(/[^a-zA-Z0-9_]/,'_')
|
371
|
-
# Hash the full path to the script
|
372
|
-
suffix = Digest::MD5.new(@rb_file).to_s[0,4]
|
373
|
-
@mod_name = "Inline_#{@mod.name.gsub('::','__')}_#{rb_script_name}_#{suffix}"
|
374
|
-
@so_name = "#{Inline.directory}/#{@mod_name}.#{Config::CONFIG["DLEXT"]}"
|
375
|
-
end
|
376
|
-
@src = []
|
377
|
-
@sig = {}
|
378
|
-
@flags = []
|
379
|
-
@libs = []
|
380
|
-
end
|
381
|
-
|
403
|
+
##
|
382
404
|
# Adds compiler options to the compiler command line. No
|
383
405
|
# preprocessing is done, so you must have all your dashes and
|
384
406
|
# everything.
|
@@ -387,6 +409,7 @@ module Inline
|
|
387
409
|
@flags.push(*flags)
|
388
410
|
end
|
389
411
|
|
412
|
+
##
|
390
413
|
# Adds linker flags to the link command line. No preprocessing is
|
391
414
|
# done, so you must have all your dashes and everything.
|
392
415
|
|
@@ -394,14 +417,15 @@ module Inline
|
|
394
417
|
@libs.push(*flags)
|
395
418
|
end
|
396
419
|
|
397
|
-
|
398
|
-
#
|
420
|
+
##
|
421
|
+
# Registers C type-casts +r2c+ and +c2r+ for +type+.
|
399
422
|
|
400
423
|
def add_type_converter(type, r2c, c2r)
|
401
424
|
$stderr.puts "WARNING: overridding #{type}" if @@type_map.has_key? type
|
402
425
|
@@type_map[type] = [r2c, c2r]
|
403
426
|
end
|
404
427
|
|
428
|
+
##
|
405
429
|
# Adds an include to the top of the file. Don't forget to use
|
406
430
|
# quotes or angle brackets.
|
407
431
|
|
@@ -409,12 +433,14 @@ module Inline
|
|
409
433
|
@src << "#include #{header}"
|
410
434
|
end
|
411
435
|
|
436
|
+
##
|
412
437
|
# Adds any amount of text/code to the source
|
413
438
|
|
414
439
|
def prefix(code)
|
415
440
|
@src << code
|
416
441
|
end
|
417
442
|
|
443
|
+
##
|
418
444
|
# Adds a C function to the source, including performing automatic
|
419
445
|
# type conversion to arguments and the return value. Unknown type
|
420
446
|
# conversions can be extended by using +add_type_converter+.
|
@@ -423,10 +449,14 @@ module Inline
|
|
423
449
|
self.generate(src,:expand_types=>true)
|
424
450
|
end
|
425
451
|
|
452
|
+
##
|
453
|
+
# Same as +c+, but adds a class function.
|
454
|
+
|
426
455
|
def c_singleton src
|
427
456
|
self.generate(src,:expand_types=>true,:singleton=>true)
|
428
457
|
end
|
429
458
|
|
459
|
+
##
|
430
460
|
# Adds a raw C function to the source. This version does not
|
431
461
|
# perform any type conversion and must conform to the ruby/C
|
432
462
|
# coding conventions.
|
@@ -435,6 +465,9 @@ module Inline
|
|
435
465
|
self.generate(src)
|
436
466
|
end
|
437
467
|
|
468
|
+
##
|
469
|
+
# Same as +c_raw+, but adds a class function.
|
470
|
+
|
438
471
|
def c_raw_singleton src
|
439
472
|
self.generate(src, :singleton=>true)
|
440
473
|
end
|
@@ -516,6 +549,7 @@ end # module Inline
|
|
516
549
|
|
517
550
|
class Module
|
518
551
|
|
552
|
+
##
|
519
553
|
# Extends the Module class to have an inline method. The default
|
520
554
|
# language/builder used is C, but can be specified with the +lang+
|
521
555
|
# parameter.
|
@@ -544,7 +578,8 @@ end
|
|
544
578
|
|
545
579
|
class File
|
546
580
|
|
547
|
-
|
581
|
+
##
|
582
|
+
# Equivalent to +File::open+ with an associated block, but moves
|
548
583
|
# any existing file with the same name to the side first.
|
549
584
|
|
550
585
|
def self.write_with_backup(path) # returns true if file already existed
|
@@ -566,6 +601,7 @@ end # class File
|
|
566
601
|
|
567
602
|
class Dir
|
568
603
|
|
604
|
+
##
|
569
605
|
# +assert_secure+ checks to see that +path+ exists and has minimally
|
570
606
|
# writable permissions. If not, it prints an error and exits. It
|
571
607
|
# only works on +POSIX+ systems. Patches for other systems are
|
data/test_inline.rb
CHANGED
@@ -96,7 +96,7 @@ class TestC < InlineTestCase
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def test_ruby2c
|
99
|
-
x = Inline::C.new(
|
99
|
+
x = Inline::C.new(self.class)
|
100
100
|
assert_equal 'NUM2CHR', x.ruby2c("char")
|
101
101
|
assert_equal 'STR2CSTR', x.ruby2c("char *")
|
102
102
|
assert_equal 'FIX2INT', x.ruby2c("int")
|
@@ -111,7 +111,7 @@ class TestC < InlineTestCase
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def test_c2ruby
|
114
|
-
x = Inline::C.new(
|
114
|
+
x = Inline::C.new(self.class)
|
115
115
|
assert_equal 'CHR2FIX', x.c2ruby("char")
|
116
116
|
assert_equal 'rb_str_new2', x.c2ruby("char *")
|
117
117
|
assert_equal 'INT2FIX', x.c2ruby("int")
|
@@ -125,6 +125,35 @@ class TestC < InlineTestCase
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
+
def util_module_name(*signatures)
|
129
|
+
md5 = Digest::MD5.new
|
130
|
+
builder = Inline::C.new(self.class)
|
131
|
+
|
132
|
+
signatures.each do |signature|
|
133
|
+
builder.sig[signature] = [nil, 0]
|
134
|
+
md5 << signature.to_s
|
135
|
+
end
|
136
|
+
|
137
|
+
assert_equal("Inline_TestInline__TestC_#{md5.to_s[0,4]}",
|
138
|
+
builder.module_name)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_module_name_0_methods
|
142
|
+
util_module_name
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_module_name_1_method
|
146
|
+
util_module_name :something1
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_module_name_2_methods
|
150
|
+
util_module_name :something2, :something3
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_module_name_2_other_methods
|
154
|
+
util_module_name :something4, :something5
|
155
|
+
end
|
156
|
+
|
128
157
|
def util_parse_signature(src, expected, t=nil, a=nil, b=nil)
|
129
158
|
|
130
159
|
result = nil
|
@@ -549,8 +578,8 @@ class TestModule < InlineTestCase
|
|
549
578
|
def test_nested
|
550
579
|
Object.class_eval $test_module_code
|
551
580
|
fb = Foo::Bar.new
|
552
|
-
assert_equal(fb.forty_two_instance
|
553
|
-
assert_equal(Foo::Bar.twenty_four_class
|
581
|
+
assert_equal(42, fb.forty_two_instance)
|
582
|
+
assert_equal(24, Foo::Bar.twenty_four_class)
|
554
583
|
|
555
584
|
tempfile = Tempfile.new("test_inline_nested")
|
556
585
|
tempfile.write($test_module_code2)
|
@@ -558,8 +587,8 @@ class TestModule < InlineTestCase
|
|
558
587
|
tempfile.rewind
|
559
588
|
`cp #{tempfile.path} #{tempfile.path}.rb`
|
560
589
|
require "#{tempfile.path}.rb"
|
561
|
-
assert_equal(fb.twelve_instance
|
562
|
-
assert_equal(Foo::Bar.twelve_class
|
590
|
+
assert_equal(12, fb.twelve_instance)
|
591
|
+
assert_equal(12, Foo::Bar.twelve_class)
|
563
592
|
`rm "#{tempfile.path}.rb"`
|
564
593
|
end
|
565
594
|
|
@@ -569,9 +598,8 @@ class TestModule < InlineTestCase
|
|
569
598
|
end
|
570
599
|
assert(test(?d, Inline.directory),
|
571
600
|
"inline dir should have been created")
|
572
|
-
matches = Dir[File.join(Inline.directory, "
|
573
|
-
assert_equal(matches.length,
|
574
|
-
"Source should have been created")
|
601
|
+
matches = Dir[File.join(Inline.directory, "Inline_TestModule_*.c")]
|
602
|
+
assert_equal(1, matches.length, "Source should have been created")
|
575
603
|
library_file = matches.first.gsub(/\.c$/) { "." + Config::CONFIG["DLEXT"] }
|
576
604
|
assert(test(?f, library_file),
|
577
605
|
"Library file should have been created")
|
metadata
CHANGED