opsb-RubyInline 3.8.6

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/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.add_include_dirs "../../ZenTest/dev/lib"
7
+ Hoe.add_include_dirs "lib"
8
+
9
+ Hoe.plugin :seattlerb
10
+
11
+ Hoe.spec "RubyInline" do
12
+ developer 'Ryan Davis', 'ryand-ruby@zenspider.com'
13
+
14
+ clean_globs << File.expand_path("~/.ruby_inline")
15
+ spec_extras[:requirements] =
16
+ "A POSIX environment and a compiler for your language."
17
+ extra_deps << ['ZenTest', '~> 4.3'] # for ZenTest mapping
18
+ end
19
+
20
+ task :test => :clean
21
+
22
+ desc "run all examples"
23
+ task :examples do
24
+ %w(example.rb example2.rb
25
+ tutorial/example1.rb
26
+ tutorial/example2.rb).each do |e|
27
+ rm_rf '~/.ruby_inline'
28
+ ruby "-Ilib -w #{e}"
29
+ end
30
+ end
31
+
32
+ desc "run simple benchmarks"
33
+ task :bench do
34
+ verbose(false) do
35
+ ruby "-Ilib ./example.rb"
36
+ ruby "-Ilib ./example.rb 1000000 12" # 12 is the bignum cutoff for factorial
37
+ end
38
+ end
data/demo/fastmath.rb ADDED
@@ -0,0 +1,27 @@
1
+
2
+ begin require 'rubygems' rescue LoadError end
3
+ require 'inline'
4
+
5
+ class FastMath
6
+ def factorial(n)
7
+ f = 1
8
+ n.downto(2) { |x| f *= x }
9
+ return f
10
+ end
11
+ inline do |builder|
12
+ builder.c "
13
+ long factorial_c(int max) {
14
+ int i=max, result=1;
15
+ while (i >= 2) { result *= i--; }
16
+ return result;
17
+ }"
18
+ end
19
+ end
20
+
21
+ math = FastMath.new
22
+
23
+ if ARGV.empty? then
24
+ 30000.times do math.factorial(20); end
25
+ else
26
+ 30000.times do math.factorial_c(20); end
27
+ end
data/demo/hello.rb ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ begin require 'rubygems' rescue LoadError end
4
+ require 'inline'
5
+
6
+ class Hello
7
+ inline do |builder|
8
+ builder.include "<stdio.h>"
9
+ builder.c 'void hello() { puts("hello world"); }'
10
+ end
11
+ end
12
+
13
+ Hello.new.hello
data/example.rb ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'rubygems'
4
+ $:.unshift 'lib'
5
+ require 'inline'
6
+
7
+ require 'fileutils'
8
+ FileUtils.rm_rf File.expand_path("~/.ruby_inline")
9
+
10
+ class MyTest
11
+
12
+ def factorial(n)
13
+ f = 1
14
+ n.downto(2) { |x| f *= x }
15
+ f
16
+ end
17
+
18
+ inline do |builder|
19
+ builder.c "
20
+ long factorial_c(int max) {
21
+ int i=max, result=1;
22
+ while (i >= 2) { result *= i--; }
23
+ return result;
24
+ }"
25
+
26
+ builder.c_raw "
27
+ static
28
+ VALUE
29
+ factorial_c_raw(int argc, VALUE *argv, VALUE self) {
30
+ int i=FIX2INT(argv[0]), result=1;
31
+ while (i >= 2) { result *= i--; }
32
+ return INT2NUM(result);
33
+ }"
34
+ end
35
+ end
36
+
37
+ # breakeven for build run vs native doing 5 factorial:
38
+ # on a PIII/750 running FreeBSD: about 5000
39
+ # on a PPC/G4/800 running Mac OSX 10.2: always faster
40
+
41
+ require 'benchmark'
42
+ puts "RubyInline #{Inline::VERSION}" if $DEBUG
43
+
44
+ MyTest.send(:alias_method, :factorial_alias, :factorial_c_raw)
45
+
46
+ t = MyTest.new()
47
+ max = (ARGV.shift || 1_000_000).to_i
48
+ n = (ARGV.shift || 5).to_i
49
+ m = t.factorial(n)
50
+
51
+ def validate(n, m)
52
+ if n != m then raise "#{n} != #{m}"; end
53
+ end
54
+
55
+ puts "# of iterations = #{max}, n = #{n}"
56
+ Benchmark::bm(20) do |x|
57
+ x.report("null_time") do
58
+ for i in 0..max do
59
+ # do nothing
60
+ end
61
+ end
62
+
63
+ x.report("c") do
64
+ for i in 0..max do
65
+ validate(t.factorial_c(n), m)
66
+ end
67
+ end
68
+
69
+ x.report("c-raw") do
70
+ for i in 0..max do
71
+ validate(t.factorial_c_raw(n), m)
72
+ end
73
+ end
74
+
75
+ x.report("c-alias") do
76
+ for i in 0..max do
77
+ validate(t.factorial_alias(n), m)
78
+ end
79
+ end
80
+
81
+ x.report("pure ruby") do
82
+ for i in 0..max do
83
+ validate(t.factorial(n), m)
84
+ end
85
+ end
86
+ end
data/example2.rb ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/local/bin/ruby17 -w
2
+
3
+ begin
4
+ require 'rubygems'
5
+ rescue LoadError
6
+ $: << 'lib'
7
+ end
8
+ require 'inline'
9
+
10
+ class MyTest
11
+
12
+ inline do |builder|
13
+
14
+ builder.add_compile_flags %q(-x c++)
15
+ builder.add_link_flags %q(-lstdc++)
16
+
17
+ builder.c "
18
+ // stupid c++ comment
19
+ #include <iostream>
20
+ /* stupid c comment */
21
+ static
22
+ void
23
+ hello(int i) {
24
+ while (i-- > 0) {
25
+ std::cout << \"hello\" << std::endl;
26
+ }
27
+ }
28
+ "
29
+ end
30
+ end
31
+
32
+ t = MyTest.new()
33
+
34
+ t.hello(3)
data/lib/inline.rb ADDED
@@ -0,0 +1,878 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ ##
4
+ # Ruby Inline is a framework for writing ruby extensions in foreign
5
+ # languages.
6
+ #
7
+ # == SYNOPSIS
8
+ #
9
+ # require 'inline'
10
+ # class MyClass
11
+ # inline do |builder|
12
+ # builder.include "<math.h>"
13
+ # builder.c %q{
14
+ # long factorial(int max) {
15
+ # int i=max, result=1;
16
+ # while (i >= 2) { result *= i--; }
17
+ # return result;
18
+ # }
19
+ # }
20
+ # end
21
+ # end
22
+ #
23
+ # == DESCRIPTION
24
+ #
25
+ # Inline allows you to write foreign code within your ruby code. It
26
+ # automatically determines if the code in question has changed and
27
+ # builds it only when necessary. The extensions are then automatically
28
+ # loaded into the class/module that defines it.
29
+ #
30
+ # You can even write extra builders that will allow you to write
31
+ # inlined code in any language. Use Inline::C as a template and look
32
+ # at Module#inline for the required API.
33
+ #
34
+ # == PACKAGING
35
+ #
36
+ # To package your binaries into a gem, use hoe's INLINE and
37
+ # FORCE_PLATFORM env vars.
38
+ #
39
+ # Example:
40
+ #
41
+ # rake package INLINE=1
42
+ #
43
+ # or:
44
+ #
45
+ # rake package INLINE=1 FORCE_PLATFORM=mswin32
46
+ #
47
+ # See hoe for more details.
48
+ #
49
+
50
+ require "rbconfig"
51
+ require "digest/md5"
52
+ require 'fileutils'
53
+ require 'rubygems'
54
+
55
+ require 'zentest_mapping'
56
+
57
+ $TESTING = false unless defined? $TESTING
58
+
59
+ class CompilationError < RuntimeError; end
60
+
61
+ ##
62
+ # The Inline module is the top-level module used. It is responsible
63
+ # for instantiating the builder for the right language used,
64
+ # compilation/linking when needed, and loading the inlined code into
65
+ # the current namespace.
66
+
67
+ module Inline
68
+ VERSION = '3.8.6'
69
+
70
+ WINDOZE = /mswin|mingw/ =~ RUBY_PLATFORM
71
+ RUBINIUS = defined? RUBY_ENGINE
72
+ DEV_NULL = (WINDOZE ? 'nul' : '/dev/null')
73
+ GEM = (WINDOZE ? 'gem.bat' : 'gem')
74
+ RAKE = if WINDOZE then
75
+ 'rake.bat'
76
+ elsif RUBINIUS then
77
+ File.join(Gem.bindir, 'rake')
78
+ else
79
+ "#{Gem.ruby} -S rake"
80
+ end
81
+
82
+ warn "RubyInline v #{VERSION}" if $DEBUG
83
+
84
+ def self.register cls
85
+ registered_inline_classes << cls
86
+ registered_inline_classes.uniq!
87
+ end
88
+
89
+ def self.registered_inline_classes
90
+ @@registered_inline_classes ||= []
91
+ end
92
+
93
+ # rootdir can be forced using INLINEDIR variable
94
+ # if not defined, it should store in user HOME folder
95
+ #
96
+ # Under Windows user data can be stored in several locations:
97
+ #
98
+ # HOME
99
+ # HOMEDRIVE + HOMEPATH
100
+ # APPDATA
101
+ # USERPROFILE
102
+ #
103
+ # Perform a check in that other to see if the environment is defined
104
+ # and if so, use it. only try this on Windows.
105
+
106
+ def self.rootdir
107
+ env = ENV['INLINEDIR'] || ENV['HOME']
108
+
109
+ if env.nil? and WINDOZE then
110
+ # try HOMEDRIVE + HOMEPATH combination
111
+ if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
112
+ env = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
113
+ end
114
+
115
+ # no HOMEDRIVE? use APPDATA
116
+ env = ENV['APPDATA'] if env.nil? and ENV['APPDATA']
117
+
118
+ # bummer, still no env? then fall to USERPROFILE
119
+ env = ENV['USERPROFILE'] if env.nil? and ENV['USERPROFILE']
120
+ end
121
+
122
+ if env.nil? then
123
+ abort "Define INLINEDIR or HOME in your environment and try again"
124
+ end
125
+
126
+ unless defined? @@rootdir and env == @@rootdir and test ?d, @@rootdir then
127
+ rootdir = env
128
+ Dir.mkdir rootdir, 0700 unless test ?d, rootdir
129
+ Dir.assert_secure rootdir
130
+ @@rootdir = rootdir
131
+ end
132
+
133
+ @@rootdir
134
+ end
135
+
136
+ def self.directory
137
+ directory = File.join(rootdir, ".ruby_inline")
138
+ unless defined? @@directory and directory == @@directory
139
+ @@directory = File.join(self.rootdir, ".ruby_inline")
140
+ end
141
+ Dir.assert_secure directory
142
+ @@directory
143
+ end
144
+
145
+ ##
146
+ # Inline::C is the default builder used and the only one provided by
147
+ # Inline. It can be used as a template to write builders for other
148
+ # languages. It understands type-conversions for the basic types and
149
+ # can be extended as needed using #add_type_converter, #alias_type_converter
150
+ # and #remove_type_converter.
151
+
152
+ class C
153
+
154
+ include ZenTestMapping
155
+
156
+ MAGIC_ARITY_THRESHOLD = 15
157
+ MAGIC_ARITY = -1
158
+
159
+ ##
160
+ # Default C to ruby and ruby to C type map
161
+
162
+ TYPE_MAP = {
163
+ 'char' => [ 'NUM2CHR', 'CHR2FIX' ],
164
+
165
+ 'char *' => [ 'StringValuePtr', 'rb_str_new2' ],
166
+
167
+ 'double' => [ 'NUM2DBL', 'rb_float_new' ],
168
+
169
+ 'int' => [ "FI\X2INT", 'INT2FIX' ],
170
+ 'unsigned int' => [ 'NUM2UINT', 'UINT2NUM' ],
171
+ 'unsigned' => [ 'NUM2UINT', 'UINT2NUM' ],
172
+
173
+ 'long' => [ 'NUM2LONG', 'LONG2NUM' ],
174
+ 'unsigned long' => [ 'NUM2ULONG', 'ULONG2NUM' ],
175
+
176
+ 'long long' => [ 'NUM2LL', 'LL2NUM' ],
177
+ 'unsigned long long' => [ 'NUM2ULL', 'ULL2NUM' ],
178
+
179
+ 'off_t' => [ 'NUM2OFFT', 'OFFT2NUM' ],
180
+
181
+ 'VALUE' => [ '', '' ],
182
+ # Can't do these converters because they conflict with the above:
183
+ # ID2SYM(x), SYM2ID(x), F\IX2UINT(x)
184
+ }
185
+
186
+ def strip_comments(src)
187
+ # strip c-comments
188
+ src = src.gsub(%r%\s*/\*.*?\*/%m, '')
189
+ # strip cpp-comments
190
+ src = src.gsub(%r%^\s*//.*?\n%, '')
191
+ src = src.gsub(%r%[ \t]*//[^\n]*%, '')
192
+ src
193
+ end
194
+
195
+ def parse_signature(src, raw=false)
196
+
197
+ sig = self.strip_comments(src)
198
+ # strip preprocessor directives
199
+ sig.gsub!(/^\s*\#.*(\\\n.*)*/, '')
200
+ # strip {}s
201
+ sig.gsub!(/\{[^\}]*\}/, '{ }')
202
+ # clean and collapse whitespace
203
+ sig.gsub!(/\s+/, ' ')
204
+
205
+ unless defined? @types then
206
+ @types = 'void|' + @type_map.keys.map{|x| Regexp.escape(x)}.join('|')
207
+ end
208
+
209
+ if /(#{@types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then
210
+ return_type, function_name, arg_string = $1, $2, $3
211
+ args = []
212
+ arg_string.split(',').each do |arg|
213
+
214
+ # helps normalize into 'char * varname' form
215
+ arg = arg.gsub(/\s*\*\s*/, ' * ').strip
216
+
217
+ if /(((#{@types})\s*\*?)+)\s+(\w+)\s*$/ =~ arg then
218
+ args.push([$4, $1])
219
+ elsif arg != "void" then
220
+ warn "WAR\NING: '#{arg}' not understood"
221
+ end
222
+ end
223
+
224
+ arity = args.size
225
+ arity = MAGIC_ARITY if raw
226
+
227
+ return {
228
+ 'return' => return_type,
229
+ 'name' => function_name,
230
+ 'args' => args,
231
+ 'arity' => arity
232
+ }
233
+ end
234
+
235
+ raise SyntaxError, "Can't parse signature: #{sig}"
236
+ end # def parse_signature
237
+
238
+ def generate(src, options={})
239
+ options = {:expand_types=>options} unless Hash === options
240
+
241
+ expand_types = options[:expand_types]
242
+ singleton = options[:singleton]
243
+ result = self.strip_comments(src)
244
+
245
+ signature = parse_signature(src, !expand_types)
246
+ function_name = signature['name']
247
+ method_name = options[:method_name]
248
+ method_name ||= test_to_normal function_name
249
+ return_type = signature['return']
250
+ arity = options[:arity] || signature['arity']
251
+
252
+ raise ArgumentError, "too many arguments" if arity > MAGIC_ARITY_THRESHOLD
253
+
254
+ if expand_types then
255
+ prefix = "static VALUE #{function_name}("
256
+ if arity <= MAGIC_ARITY then
257
+ prefix += "int argc, VALUE *argv, VALUE self"
258
+ else
259
+ prefix += "VALUE self"
260
+ prefix += signature['args'].map { |arg, type| ", VALUE _#{arg}"}.join
261
+ end
262
+ prefix += ") {\n"
263
+ prefix += signature['args'].map { |arg, type|
264
+ " #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
265
+ }.join
266
+
267
+ # replace the function signature (hopefully) with new sig (prefix)
268
+ result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
269
+ result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
270
+ unless return_type == "void" then
271
+ raise SyntaxError, "Couldn't find return statement for #{function_name}" unless
272
+ result =~ /return/
273
+ result.gsub!(/return\s+([^\;\}]+)/) do
274
+ "return #{c2ruby(return_type)}(#{$1})"
275
+ end
276
+ else
277
+ result.sub!(/\s*\}\s*\Z/, "\nreturn Qnil;\n}")
278
+ end
279
+ else
280
+ prefix = "static #{return_type} #{function_name}("
281
+ result.sub!(/[^;\/\"\>]+#{function_name}\s*\(/, prefix)
282
+ result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
283
+ end
284
+
285
+ delta = if result =~ /\A(static.*?\{)/m then
286
+ $1.split(/\n/).size
287
+ else
288
+ warn "WAR\NING: Can't find signature in #{result.inspect}\n" unless $TESTING
289
+ 0
290
+ end
291
+
292
+ file, line = caller[1].split(/:/)
293
+ result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless $DEBUG and not $TESTING
294
+
295
+ @src << result
296
+ @sig[function_name] = [arity,singleton,method_name]
297
+
298
+ return result if $TESTING
299
+ end # def generate
300
+
301
+ ##
302
+ # Builds a complete C extension suitable for writing to a file and
303
+ # compiling.
304
+
305
+ def generate_ext
306
+ ext = []
307
+
308
+ if @include_ruby_first
309
+ @inc.unshift "#include \"ruby.h\""
310
+ else
311
+ @inc.push "#include \"ruby.h\""
312
+ end
313
+
314
+ ext << @inc
315
+ ext << nil
316
+ ext << @src.join("\n\n")
317
+ ext << nil
318
+ ext << nil
319
+ ext << "#ifdef __cplusplus"
320
+ ext << "extern \"C\" {"
321
+ ext << "#endif"
322
+ ext << " __declspec(dllexport)" if WINDOZE
323
+ ext << " void Init_#{module_name}() {"
324
+ ext << " VALUE c = rb_cObject;"
325
+
326
+ # TODO: use rb_class2path
327
+ # ext << " VALUE c = rb_path2class(#{@mod.name.inspect});"
328
+ ext << @mod.name.split("::").map { |n|
329
+ " c = rb_const_get(c, rb_intern(\"#{n}\"));"
330
+ }.join("\n")
331
+
332
+ ext << nil
333
+
334
+ @sig.keys.sort.each do |name|
335
+ method = ''
336
+ arity, singleton, method_name = @sig[name]
337
+ if singleton then
338
+ if method_name == 'allocate' then
339
+ raise "#{@mod}::allocate must have an arity of zero" if arity > 0
340
+ ext << " rb_define_alloc_func(c, (VALUE(*)(VALUE))#{name});"
341
+ next
342
+ end
343
+ method << " rb_define_singleton_method(c, \"#{method_name}\", "
344
+ else
345
+ method << " rb_define_method(c, \"#{method_name}\", "
346
+ end
347
+ method << "(VALUE(*)(ANYARGS))#{name}, #{arity});"
348
+ ext << method
349
+ end
350
+
351
+ ext << @init_extra.join("\n") unless @init_extra.empty?
352
+
353
+ ext << nil
354
+ ext << " }"
355
+ ext << "#ifdef __cplusplus"
356
+ ext << "}"
357
+ ext << "#endif"
358
+ ext << nil
359
+
360
+ ext.join "\n"
361
+ end
362
+
363
+ def module_name
364
+ unless defined? @module_name then
365
+ module_name = @mod.name.gsub('::','__')
366
+ md5 = Digest::MD5.new
367
+ @sig.keys.sort_by { |x| x.to_s }.each { |m| md5 << m.to_s }
368
+ @module_name = "Inline_#{module_name}_#{md5.to_s[0,4]}"
369
+ end
370
+ @module_name
371
+ end
372
+
373
+ def so_name
374
+ unless defined? @so_name then
375
+ @so_name = "#{Inline.directory}/#{module_name}.#{Config::CONFIG["DLEXT"]}"
376
+ end
377
+ @so_name
378
+ end
379
+
380
+ attr_reader :rb_file, :mod
381
+ attr_writer :mod
382
+ attr_accessor :src, :sig, :flags, :libs, :init_extra
383
+
384
+ ##
385
+ # Sets the name of the C struct for generating accessors. Used with
386
+ # #accessor, #reader, #writer.
387
+
388
+ attr_accessor :struct_name
389
+
390
+ def initialize(mod)
391
+ raise ArgumentError, "Class/Module arg is required" unless Module === mod
392
+ # new (but not on some 1.8s) -> inline -> real_caller|eval
393
+ stack = caller
394
+ meth = stack.shift until meth =~ /in .(inline|test_|setup)/ or stack.empty?
395
+ raise "Couldn't discover caller" if stack.empty?
396
+ real_caller = stack.first
397
+ real_caller = stack[3] if real_caller =~ /\(eval\)/
398
+ real_caller =~ /(.*):(\d+)/
399
+ real_caller = $1
400
+ @rb_file = File.expand_path real_caller
401
+
402
+ @mod = mod
403
+ @src = []
404
+ @inc = []
405
+ @sig = {}
406
+ @flags = []
407
+ @libs = []
408
+ @init_extra = []
409
+ @include_ruby_first = true
410
+ @inherited_methods = {}
411
+ @struct_name = nil
412
+
413
+ @type_map = TYPE_MAP.dup
414
+ end
415
+
416
+ ##
417
+ # Adds a #reader and #writer for a C struct member wrapped via
418
+ # Data_Wrap_Struct. +method+ is the ruby name to give the accessor,
419
+ # +type+ is the C type. Unless the C member name is overridden with
420
+ # +member+, the method name is used as the struct member.
421
+ #
422
+ # builder.struct_name = 'MyStruct'
423
+ # builder.accessor :title, 'char *'
424
+ # builder.accessor :stream_index, 'int', :index
425
+ #
426
+ # The latter accesses MyStruct->index via the stream_index method.
427
+
428
+ def accessor(method, type, member = method)
429
+ reader method, type, member
430
+ writer method, type, member
431
+ end
432
+
433
+ ##
434
+ # Adds a reader for a C struct member wrapped via Data_Wrap_Struct.
435
+ # +method+ is the ruby name to give the reader, +type+ is the C type.
436
+ # Unless the C member name is overridden with +member+, the method
437
+ # name is used as the struct member. See #accessor for an example.
438
+
439
+ def reader(method, type, member = method)
440
+ raise "struct name not set for reader #{method} #{type}" unless
441
+ @struct_name
442
+
443
+ c <<-C
444
+ VALUE #{method}() {
445
+ #{@struct_name} *pointer;
446
+
447
+ Data_Get_Struct(self, #{@struct_name}, pointer);
448
+
449
+ return #{c2ruby type}(pointer->#{member});
450
+ }
451
+ C
452
+ end
453
+
454
+ ##
455
+ # Adds a writer for a C struct member wrapped via Data_Get_Struct.
456
+ # +method+ is the ruby name to give the writer, +type+ is the C type.
457
+ # Unless the C member name is overridden with +member+, the method
458
+ # name is used as the struct member. See #accessor for an example.
459
+
460
+ def writer(method, type, member = method)
461
+ raise "struct name not set for writer #{method} #{type}" unless
462
+ @struct_name
463
+
464
+ c <<-C
465
+ VALUE #{method}_equals(VALUE value) {
466
+ #{@struct_name} *pointer;
467
+
468
+ Data_Get_Struct(self, #{@struct_name}, pointer);
469
+
470
+ pointer->#{member} = #{ruby2c type}(value);
471
+
472
+ return value;
473
+ }
474
+ C
475
+ end
476
+
477
+ ##
478
+ # Converts ruby type +type+ to a C type
479
+
480
+ def ruby2c(type)
481
+ raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
482
+ @type_map[type].first
483
+ end
484
+
485
+ ##
486
+ # Converts C type +type+ to a ruby type
487
+
488
+ def c2ruby(type)
489
+ raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
490
+ @type_map[type].last
491
+ end
492
+
493
+ ##
494
+ # Attempts to load pre-generated code returning true if it succeeds.
495
+
496
+ def load_cache
497
+ begin
498
+ file = File.join("inline", File.basename(so_name))
499
+ if require file then
500
+ dir = Inline.directory
501
+ warn "WAR\NING: #{dir} exists but is not being used" if test ?d, dir and $VERBOSE
502
+ return true
503
+ end
504
+ rescue LoadError
505
+ end
506
+ return false
507
+ end
508
+
509
+ ##
510
+ # Loads the generated code back into ruby
511
+
512
+ def load
513
+ Kernel.require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
514
+ end
515
+
516
+ ##
517
+ # Builds the source file, if needed, and attempts to compile it.
518
+
519
+ def build
520
+ so_name = self.so_name
521
+ so_exists = File.file? so_name
522
+ unless so_exists and File.mtime(rb_file) < File.mtime(so_name) then
523
+
524
+ unless File.directory? Inline.directory then
525
+ warn "NOTE: creating #{Inline.directory} for RubyInline" if $DEBUG
526
+ Dir.mkdir Inline.directory, 0700
527
+ end
528
+
529
+ src_name = "#{Inline.directory}/#{module_name}.c"
530
+ old_src_name = "#{src_name}.old"
531
+ should_compare = File.write_with_backup(src_name) do |io|
532
+ io.puts generate_ext
533
+ end
534
+
535
+ # recompile only if the files are different
536
+ recompile = true
537
+ if so_exists and should_compare and
538
+ FileUtils.compare_file(old_src_name, src_name) then
539
+ recompile = false
540
+
541
+ # Updates the timestamps on all the generated/compiled files.
542
+ # Prevents us from entering this conditional unless the source
543
+ # file changes again.
544
+ t = Time.now
545
+ File.utime(t, t, src_name, old_src_name, so_name)
546
+ end
547
+
548
+ if recompile then
549
+
550
+ hdrdir = %w(srcdir archdir rubyhdrdir).map { |name|
551
+ Config::CONFIG[name]
552
+ }.find { |dir|
553
+ dir and File.exist? File.join(dir, "/ruby.h")
554
+ } or abort "ERROR: Can't find header dir for ruby. Exiting..."
555
+
556
+ flags = @flags.join(' ')
557
+ libs = @libs.join(' ')
558
+
559
+ config_hdrdir = if RUBY_VERSION > '1.9' then
560
+ "-I #{File.join hdrdir, RbConfig::CONFIG['arch']}"
561
+ else
562
+ nil
563
+ end
564
+
565
+ cmd = [ Config::CONFIG['LDSHARED'],
566
+ flags,
567
+ Config::CONFIG['DLDFLAGS'],
568
+ Config::CONFIG['CCDLFLAGS'],
569
+ Config::CONFIG['CFLAGS'],
570
+ '-I', hdrdir,
571
+ config_hdrdir,
572
+ '-I', Config::CONFIG['includedir'],
573
+ "-L#{Config::CONFIG['libdir']}",
574
+ '-o', so_name.inspect,
575
+ File.expand_path(src_name).inspect,
576
+ libs,
577
+ crap_for_windoze ].join(' ')
578
+
579
+ # TODO: remove after osx 10.5.2
580
+ cmd += ' -flat_namespace -undefined suppress' if
581
+ RUBY_PLATFORM =~ /darwin9\.[01]/
582
+ cmd += " 2> #{DEV_NULL}" if $TESTING and not $DEBUG
583
+
584
+ warn "Building #{so_name} with '#{cmd}'" if $DEBUG
585
+ result = `#{cmd}`
586
+ warn "Output:\n#{result}" if $DEBUG
587
+ if $? != 0 then
588
+ bad_src_name = src_name + ".bad"
589
+ File.rename src_name, bad_src_name
590
+ raise CompilationError, "error executing #{cmd.inspect}: #{$?}\nRenamed #{src_name} to #{bad_src_name}"
591
+ end
592
+
593
+ # NOTE: manifest embedding is only required when using VC8 ruby
594
+ # build or compiler.
595
+ # Errors from this point should be ignored if Config::CONFIG['arch']
596
+ # (RUBY_PLATFORM) matches 'i386-mswin32_80'
597
+ if WINDOZE and RUBY_PLATFORM =~ /_80$/ then
598
+ Dir.chdir Inline.directory do
599
+ cmd = "mt /manifest lib.so.manifest /outputresource:so.dll;#2"
600
+ warn "Embedding manifest with '#{cmd}'" if $DEBUG
601
+ result = `#{cmd}`
602
+ warn "Output:\n#{result}" if $DEBUG
603
+ if $? != 0 then
604
+ raise CompilationError, "error executing #{cmd}: #{$?}"
605
+ end
606
+ end
607
+ end
608
+
609
+ warn "Built successfully" if $DEBUG
610
+ end
611
+
612
+ else
613
+ warn "#{so_name} is up to date" if $DEBUG
614
+ end # unless (file is out of date)
615
+ end # def build
616
+
617
+ ##
618
+ # Returns extra compilation flags for windoze platforms. Ugh.
619
+
620
+ def crap_for_windoze
621
+ # gawd windoze land sucks
622
+ case RUBY_PLATFORM
623
+ when /mswin32/ then
624
+ " -link /LIBPATH:\"#{Config::CONFIG['libdir']}\" /DEFAULTLIB:\"#{Config::CONFIG['LIBRUBY']}\" /INCREMENTAL:no /EXPORT:Init_#{module_name}"
625
+ when /mingw32/ then
626
+ " -Wl,--enable-auto-import -L#{Config::CONFIG['libdir']} -lmsvcrt-ruby18"
627
+ when /i386-cygwin/ then
628
+ ' -L/usr/local/lib -lruby.dll'
629
+ else
630
+ ''
631
+ end
632
+ end
633
+
634
+ ##
635
+ # Adds compiler options to the compiler command line. No
636
+ # preprocessing is done, so you must have all your dashes and
637
+ # everything.
638
+
639
+ def add_compile_flags(*flags)
640
+ @flags.push(*flags)
641
+ end
642
+
643
+ ##
644
+ # Adds linker flags to the link command line. No preprocessing is
645
+ # done, so you must have all your dashes and everything.
646
+
647
+ def add_link_flags(*flags)
648
+ @libs.push(*flags)
649
+ end
650
+
651
+ ##
652
+ # Create a static variable and initialize it to a value.
653
+
654
+ def add_static name, init, type = "VALUE"
655
+ prefix "static #{type} #{name};"
656
+ add_to_init "#{name} = #{init};"
657
+ end
658
+
659
+ ##
660
+ # Adds custom content to the end of the init function.
661
+
662
+ def add_to_init(*src)
663
+ @init_extra.push(*src)
664
+ end
665
+
666
+ ##
667
+ # Registers C type-casts +r2c+ and +c2r+ for +type+.
668
+
669
+ def add_type_converter(type, r2c, c2r)
670
+ warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
671
+ @type_map[type] = [r2c, c2r]
672
+ end
673
+
674
+ ##
675
+ # Registers C type +alias_type+ as an alias of +existing_type+
676
+
677
+ def alias_type_converter(existing_type, alias_type)
678
+ warn "WAR\NING: overridding #{type} on #{caller[0]}" if
679
+ @type_map.has_key? alias_type
680
+
681
+ @type_map[alias_type] = @type_map[existing_type]
682
+ end
683
+
684
+ ##
685
+ # Unregisters C type-casts for +type+.
686
+
687
+ def remove_type_converter(type)
688
+ @type_map.delete type
689
+ end
690
+
691
+ ##
692
+ # Maps a ruby constant to C (with the same name)
693
+
694
+ def map_ruby_const(*names)
695
+ names.each do |name|
696
+ self.prefix "static VALUE #{name};"
697
+ self.add_to_init " #{name} = rb_const_get(c, rb_intern(#{name.to_s.inspect}));"
698
+ end
699
+ end
700
+
701
+ ##
702
+ # Maps a C constant to ruby. +names_and_types+ is a hash that maps the
703
+ # name of the constant to its C type.
704
+ #
705
+ # builder.map_c_const :C_NAME => :int
706
+ #
707
+ # If you wish to give the constant a different ruby name:
708
+ #
709
+ # builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
710
+
711
+ def map_c_const(names_and_types)
712
+ names_and_types.each do |name, typ|
713
+ typ, ruby_name = Array === typ ? typ : [typ, name]
714
+ self.add_to_init " rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
715
+ end
716
+ end
717
+
718
+ ##
719
+ # Adds an include to the top of the file. Don't forget to use
720
+ # quotes or angle brackets.
721
+
722
+ def include(header)
723
+ @inc << "#include #{header}"
724
+ end
725
+
726
+ ##
727
+ # Specifies that the the ruby.h header should be included *after* custom
728
+ # header(s) instead of before them.
729
+
730
+ def include_ruby_last
731
+ @include_ruby_first = false
732
+ end
733
+
734
+ ##
735
+ # Adds any amount of text/code to the source
736
+
737
+ def prefix(code)
738
+ @src << code
739
+ end
740
+
741
+ ##
742
+ # Adds a C function to the source, including performing automatic
743
+ # type conversion to arguments and the return value. The Ruby
744
+ # method name can be overridden by providing method_name. Unknown
745
+ # type conversions can be extended by using +add_type_converter+.
746
+
747
+ def c src, options = {}
748
+ options = {
749
+ :expand_types => true,
750
+ }.merge options
751
+ self.generate src, options
752
+ end
753
+
754
+ ##
755
+ # Same as +c+, but adds a class function.
756
+
757
+ def c_singleton src, options = {}
758
+ options = {
759
+ :expand_types => true,
760
+ :singleton => true,
761
+ }.merge options
762
+ self.generate src, options
763
+ end
764
+
765
+ ##
766
+ # Adds a raw C function to the source. This version does not
767
+ # perform any type conversion and must conform to the ruby/C
768
+ # coding conventions. The Ruby method name can be overridden
769
+ # by providing method_name.
770
+
771
+ def c_raw src, options = {}
772
+ self.generate src, options
773
+ end
774
+
775
+ ##
776
+ # Same as +c_raw+, but adds a class function.
777
+
778
+ def c_raw_singleton src, options = {}
779
+ options = {
780
+ :singleton => true,
781
+ }.merge options
782
+ self.generate src, options
783
+ end
784
+
785
+ end # class Inline::C
786
+ end # module Inline
787
+
788
+ class Module
789
+
790
+ ##
791
+ # options is a hash that allows you to pass extra data to your
792
+ # builder. The only key that is guaranteed to exist is :testing.
793
+
794
+ attr_reader :options
795
+
796
+ ##
797
+ # Extends the Module class to have an inline method. The default
798
+ # language/builder used is C, but can be specified with the +lang+
799
+ # parameter.
800
+
801
+ def inline(lang = :C, options={})
802
+ Inline.register self
803
+
804
+ case options
805
+ when TrueClass, FalseClass then
806
+ warn "WAR\NING: 2nd argument to inline is now a hash, changing to {:testing=>#{options}}" unless options
807
+ options = { :testing => options }
808
+ when Hash
809
+ options[:testing] ||= false
810
+ else
811
+ raise ArgumentError, "BLAH"
812
+ end
813
+
814
+ builder_class = begin
815
+ Inline.const_get(lang)
816
+ rescue NameError
817
+ require "inline/#{lang}"
818
+ Inline.const_get(lang)
819
+ end
820
+
821
+ @options = options
822
+ builder = builder_class.new self
823
+
824
+ yield builder
825
+
826
+ unless options[:testing] then
827
+ unless builder.load_cache then
828
+ builder.build
829
+ builder.load
830
+ end
831
+ end
832
+ end
833
+ end
834
+
835
+ class File
836
+
837
+ ##
838
+ # Equivalent to +File::open+ with an associated block, but moves
839
+ # any existing file with the same name to the side first.
840
+
841
+ def self.write_with_backup(path) # returns true if file already existed
842
+
843
+ # move previous version to the side if it exists
844
+ renamed = false
845
+ if test ?f, path then
846
+ renamed = true
847
+ File.rename path, path + ".old"
848
+ end
849
+
850
+ File.open(path, "w") do |io|
851
+ yield(io)
852
+ end
853
+
854
+ return renamed
855
+ end
856
+ end # class File
857
+
858
+ class Dir
859
+
860
+ ##
861
+ # +assert_secure+ checks that if a +path+ exists it has minimally
862
+ # writable permissions. If not, it prints an error and exits. It
863
+ # only works on +POSIX+ systems. Patches for other systems are
864
+ # welcome.
865
+
866
+ def self.assert_secure(path)
867
+ mode = File.stat(path).mode
868
+ unless ((mode % 01000) & 0022) == 0 then
869
+ if $TESTING then
870
+ raise SecurityError, "Directory #{path} is insecure"
871
+ else
872
+ abort "#{path} is insecure (#{'%o' % mode}). It may not be group or world writable. Exiting."
873
+ end
874
+ end
875
+ rescue Errno::ENOENT
876
+ # If it ain't there, it's certainly secure
877
+ end
878
+ end