RubyInlineWithoutZenTest 3.12.2

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