opsb-RubyInline 3.8.6

Sign up to get free protection for your applications and to get access to all the features.
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