In Files

Parent

Included Modules

Class Index [+]

Quicksearch

Inline::C

Inline::C is the default builder used and the only one provided by Inline. It can be used as a template to write builders for other languages. It understands type-conversions for the basic types and can be extended as needed using #, # and #.

Constants

MAGIC_ARITY_THRESHOLD
MAGIC_ARITY
TYPE_MAP

Default C to ruby and ruby to C type map

Attributes

rb_file[R]
mod[R]
mod[W]
src[RW]
sig[RW]
flags[RW]
libs[RW]
init_extra[RW]
struct_name[RW]

Sets the name of the C struct for generating accessors. Used with #, #, #.

Public Class Methods

new(mod) click to toggle source
     # File lib/inline.rb, line 395
395:     def initialize(mod)
396:       raise ArgumentError, "Class/Module arg is required" unless Module === mod
397:       # new (but not on some 1.8s) -> inline -> real_caller|eval
398:       stack = caller
399:       meth = stack.shift until meth =~ /in .(inline|test_|setup)/ or stack.empty?
400:       raise "Couldn't discover caller" if stack.empty?
401:       real_caller = stack.first
402:       real_caller = stack[3] if real_caller =~ /\(eval\)/
403:       real_caller =~ /(.*):(\d+)/
404:       real_caller = $1
405:       @rb_file = File.expand_path real_caller
406: 
407:       @mod = mod
408:       @src = []
409:       @inc = []
410:       @sig = {}
411:       @flags = []
412:       @libs = []
413:       @init_extra = []
414:       @include_ruby_first = true
415:       @inherited_methods = {}
416:       @struct_name = nil
417: 
418:       @type_map = TYPE_MAP.dup
419:     end

Public Instance Methods

accessor(method, type, member = method) click to toggle source

Adds a # and # for a C struct member wrapped via Data_Wrap_Struct. method is the ruby name to give the accessor, type is the C type. Unless the C member name is overridden with member, the method name is used as the struct member.

  builder.struct_name = 'MyStruct'
  builder.accessor :title,        'char *'
  builder.accessor :stream_index, 'int',   :index

The latter accesses MyStruct->index via the stream_index method.

     # File lib/inline.rb, line 433
433:     def accessor(method, type, member = method)
434:       reader method, type, member
435:       writer method, type, member
436:     end
add_compile_flags(*flags) click to toggle source

Adds compiler options to the compiler command line. No preprocessing is done, so you must have all your dashes and everything.

     # File lib/inline.rb, line 649
649:     def add_compile_flags(*flags)
650:       @flags.push(*flags)
651:     end
add_static(name, init, type = "VALUE") click to toggle source

Create a static variable and initialize it to a value.

     # File lib/inline.rb, line 664
664:     def add_static name, init, type = "VALUE"
665:       prefix      "static #{type} #{name};"
666:       add_to_init "#{name} = #{init};"
667:     end
add_to_init(*src) click to toggle source

Adds custom content to the end of the init function.

     # File lib/inline.rb, line 672
672:     def add_to_init(*src)
673:       @init_extra.push(*src)
674:     end
add_type_converter(type, r2c, c2r) click to toggle source

Registers C type-casts r2c and c2r for type.

     # File lib/inline.rb, line 679
679:     def add_type_converter(type, r2c, c2r)
680:       warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
681:       @type_map[type] = [r2c, c2r]
682:     end
alias_type_converter(existing_type, alias_type) click to toggle source

Registers C type alias_type as an alias of existing_type

     # File lib/inline.rb, line 687
687:     def alias_type_converter(existing_type, alias_type)
688:       warn "WAR\NING: overridding #{type} on #{caller[0]}" if
689:         @type_map.has_key? alias_type
690: 
691:       @type_map[alias_type] = @type_map[existing_type]
692:     end
build() click to toggle source

Builds the source file, if needed, and attempts to compile it.

     # File lib/inline.rb, line 524
524:     def build
525:       so_name = self.so_name
526:       so_exists = File.file? so_name
527:       unless so_exists and File.mtime(rb_file) < File.mtime(so_name) then
528: 
529:         unless File.directory? Inline.directory then
530:           warn "NOTE: creating #{Inline.directory} for RubyInline" if $DEBUG
531:           FileUtils.mkdir_p Inline.directory, :mode => 0700
532:         end
533: 
534:         src_name = "#{Inline.directory}/#{module_name}.c"
535:         old_src_name = "#{src_name}.old"
536:         should_compare = File.write_with_backup(src_name) do |io|
537:           io.puts generate_ext
538:         end
539: 
540:         # recompile only if the files are different
541:         recompile = true
542:         if so_exists and should_compare and
543:             FileUtils.compare_file(old_src_name, src_name) then
544:           recompile = false
545: 
546:           # Updates the timestamps on all the generated/compiled files.
547:           # Prevents us from entering this conditional unless the source
548:           # file changes again.
549:           t = Time.now
550:           File.utime(t, t, src_name, old_src_name, so_name)
551:         end
552: 
553:         if recompile then
554: 
555:           hdrdir = %(srcdir archdir rubyhdrdir).map { |name|
556:             RbConfig::CONFIG[name]
557:           }.find { |dir|
558:             dir and File.exist? File.join(dir, "/ruby.h")
559:           } or abort "ERROR: Can't find header dir for ruby. Exiting..."
560: 
561:           flags = @flags.join(' ')
562:           libs  = @libs.join(' ')
563: 
564:           config_hdrdir = if RUBY_VERSION > '1.9' then
565:                             "-I #{File.join hdrdir, RbConfig::CONFIG['arch']}"
566:                           else
567:                             nil
568:                           end
569: 
570:           cmd = [ RbConfig::CONFIG['LDSHARED'],
571:                   flags,
572:                   RbConfig::CONFIG['DLDFLAGS'],
573:                   RbConfig::CONFIG['CCDLFLAGS'],
574:                   RbConfig::CONFIG['CFLAGS'],
575:                   RbConfig::CONFIG['LDFLAGS'],
576:                   '-I', hdrdir,
577:                   config_hdrdir,
578:                   '-I', RbConfig::CONFIG['includedir'],
579:                   "-L#{RbConfig::CONFIG['libdir']}",
580:                   '-o', so_name.inspect,
581:                   File.expand_path(src_name).inspect,
582:                   libs,
583:                   crap_for_windoze ].join(' ')
584: 
585:          # strip off some makefile macros for mingw 1.9
586:          cmd = cmd.gsub(/\$\(.*\)/, '') if RUBY_PLATFORM =~ /mingw/
587:         
588:           # TODO: remove after osx 10.5.2
589:           cmd += ' -flat_namespace -undefined suppress' if
590:             RUBY_PLATFORM =~ /darwin9\.[01]/
591:           cmd += " 2> #{DEV_NULL}" if $TESTING and not $DEBUG
592: 
593:           warn "Building #{so_name} with '#{cmd}'" if $DEBUG
594:           result = `#{cmd}`
595:           warn "Output:\n#{result}" if $DEBUG
596:           if $? != 0 then
597:             bad_src_name = src_name + ".bad"
598:             File.rename src_name, bad_src_name
599:             raise CompilationError, "error executing #{cmd.inspect}: #{$?}\nRenamed #{src_name} to #{bad_src_name}"
600:           end
601: 
602:           # NOTE: manifest embedding is only required when using VC8 ruby
603:           # build or compiler.
604:           # Errors from this point should be ignored if RbConfig::CONFIG['arch']
605:           # (RUBY_PLATFORM) matches 'i386-mswin32_80'
606:           if WINDOZE and RUBY_PLATFORM =~ /_80$/ then
607:             Dir.chdir Inline.directory do
608:               cmd = "mt /manifest lib.so.manifest /outputresource:so.dll;#2"
609:               warn "Embedding manifest with '#{cmd}'" if $DEBUG
610:               result = `#{cmd}`
611:               warn "Output:\n#{result}" if $DEBUG
612:               if $? != 0 then
613:                 raise CompilationError, "error executing #{cmd}: #{$?}"
614:               end
615:             end
616:           end
617: 
618:           warn "Built successfully" if $DEBUG
619:         end
620: 
621:       else
622:         warn "#{so_name} is up to date" if $DEBUG
623:       end # unless (file is out of date)
624:     end
c(src, options = {}) click to toggle source

Adds a C function to the source, including performing automatic type conversion to arguments and the return value. The Ruby method name can be overridden by providing method_name. Unknown type conversions can be extended by using add_type_converter.

     # File lib/inline.rb, line 757
757:     def c src, options = {}
758:       options = {
759:         :expand_types => true,
760:       }.merge options
761:       self.generate src, options
762:     end
c2ruby(type) click to toggle source

Converts C type type to a ruby type

     # File lib/inline.rb, line 493
493:     def c2ruby(type)
494:       raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
495:       @type_map[type].last
496:     end
c_raw(src, options = {}) click to toggle source

Adds a raw C function to the source. This version does not perform any type conversion and must conform to the ruby/C coding conventions. The Ruby method name can be overridden by providing method_name.

     # File lib/inline.rb, line 781
781:     def c_raw src, options = {}
782:       self.generate src, options
783:     end
c_raw_singleton(src, options = {}) click to toggle source

Same as c_raw, but adds a class function.

     # File lib/inline.rb, line 788
788:     def c_raw_singleton src, options = {}
789:       options = {
790:         :singleton => true,
791:       }.merge options
792:       self.generate src, options
793:     end
c_singleton(src, options = {}) click to toggle source

Same as c, but adds a class function.

     # File lib/inline.rb, line 767
767:     def c_singleton src, options = {}
768:       options = {
769:         :expand_types => true,
770:         :singleton    => true,
771:       }.merge options
772:       self.generate src, options
773:     end
crap_for_windoze() click to toggle source

Returns extra compilation flags for windoze platforms. Ugh.

     # File lib/inline.rb, line 629
629:     def crap_for_windoze
630:       # gawd windoze land sucks
631:       case RUBY_PLATFORM
632:       when /mswin32/ then
633:         " -link /LIBPATH:\"#{RbConfig::CONFIG['libdir']}\" /DEFAULTLIB:\"#{RbConfig::CONFIG['LIBRUBY']}\" /INCREMENTAL:no /EXPORT:Init_#{module_name}"
634:       when /mingw32/ then
635:         c = RbConfig::CONFIG
636:         " -Wl,--enable-auto-import -L#{c['libdir']} -l#{c['RUBY_SO_NAME']}"
637:       when /i386-cygwin/ then
638:         ' -L/usr/local/lib -lruby.dll'
639:       else
640:         ''
641:       end
642:     end
generate(src, options={}) click to toggle source
     # File lib/inline.rb, line 241
241:     def generate(src, options={})
242:       options = {:expand_types=>options} unless Hash === options
243: 
244:       expand_types = options[:expand_types]
245:       singleton = options[:singleton]
246:       result = self.strip_comments(src)
247: 
248:       signature = parse_signature(src, !expand_types)
249:       function_name = signature['name']
250:       method_name = options[:method_name]
251:       method_name ||= test_to_normal function_name
252:       return_type = signature['return']
253:       arity = options[:arity] || signature['arity']
254: 
255:       raise ArgumentError, "too many arguments" if arity > MAGIC_ARITY_THRESHOLD
256: 
257:       if expand_types then
258:         prefix = "static VALUE #{function_name}("
259:         if arity <= MAGIC_ARITY then
260:           prefix += "int argc, VALUE *argv, VALUE self"
261:         else
262:           prefix += "VALUE self"
263:           prefix += signature['args'].map { |arg, type| ", VALUE _#{arg}"}.join
264:         end
265:         prefix += ") {\n"
266:         prefix += signature['args'].map { |arg, type|
267:           "  #{type} #{arg} = #{ruby2c(type)}(_#{arg});\n"
268:         }.join
269: 
270:         # replace the function signature (hopefully) with new sig (prefix)
271:         result.sub!(/[^;\/\"\>]+#{function_name}\s*\([^\{]+\{/, "\n" + prefix)
272:         result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
273:         unless return_type == "void" then
274:           raise SyntaxError, "Couldn't find return statement for #{function_name}" unless
275:             result =~ /return/
276:           result.gsub!(/return\s+([^\;\}]+)/) do
277:             "return #{c2ruby(return_type)}(#{$1})"
278:           end
279:         else
280:           result.sub!(/\s*\}\s*\Z/, "\nreturn Qnil;\n}")
281:         end
282:       else
283:         prefix = "static #{return_type} #{function_name}("
284:         result.sub!(/[^;\/\"\>]+#{function_name}\s*\(/, prefix)
285:         result.sub!(/\A\n/, '') # strip off the \n in front in case we added it
286:       end
287: 
288:       delta = if result =~ /\A(static.*?\{)/ then
289:                 $1.split(/\n/).size
290:               else
291:                 msg = "WAR\NING: Can't find signature in #{result.inspect}\n"
292:                 warn msg unless $TESTING
293:                 0
294:               end
295: 
296:       file, line = caller[1].split(/:/)
297:       result = "# line #{line.to_i + delta} \"#{file}\"\n" + result unless
298:         $DEBUG and not $TESTING
299: 
300:       @src << result
301:       @sig[function_name] = [arity,singleton,method_name]
302: 
303:       return result if $TESTING
304:     end
generate_ext() click to toggle source

Builds a complete C extension suitable for writing to a file and compiling.

     # File lib/inline.rb, line 310
310:     def generate_ext
311:       ext = []
312: 
313:       if @include_ruby_first
314:         @inc.unshift "#include \"ruby.h\""
315:       else
316:         @inc.push "#include \"ruby.h\""
317:       end
318: 
319:       ext << @inc
320:       ext << nil
321:       ext << @src.join("\n\n")
322:       ext << nil
323:       ext << nil
324:       ext << "#ifdef __cplusplus"
325:       ext << "extern \"C\" {"
326:       ext << "#endif"
327:       ext << "  __declspec(dllexport)" if WINDOZE
328:       ext << "  void Init_#{module_name}() {"
329:       ext << "    VALUE c = rb_cObject;"
330: 
331:       # TODO: use rb_class2path
332:       # ext << "    VALUE c = rb_path2class(#{@mod.name.inspect});"
333:       ext << @mod.name.split("::").map { |n|
334:         "    c = rb_const_get(c, rb_intern(\"#{n}\"));"
335:       }.join("\n")
336: 
337:       ext << nil
338: 
339:       @sig.keys.sort.each do |name|
340:         method = ''
341:         arity, singleton, method_name = @sig[name]
342:         if singleton then
343:           if method_name == 'allocate' then
344:             raise "#{@mod}::allocate must have an arity of zero" if arity > 0
345:             ext << "    rb_define_alloc_func(c, (VALUE(*)(VALUE))#{name});"
346:             next
347:           end
348:           method << "    rb_define_singleton_method(c, \"#{method_name}\", "
349:         else
350:           method << "    rb_define_method(c, \"#{method_name}\", "
351:         end
352:         method << "(VALUE(*)(ANYARGS))#{name}, #{arity});"
353:         ext << method
354:       end
355: 
356:       ext << @init_extra.join("\n") unless @init_extra.empty?
357: 
358:       ext << nil
359:       ext << "  }"
360:       ext << "#ifdef __cplusplus"
361:       ext << "}"
362:       ext << "#endif"
363:       ext << nil
364: 
365:       ext.join "\n"
366:     end
include(header) click to toggle source

Adds an include to the top of the file. Don’t forget to use quotes or angle brackets.

     # File lib/inline.rb, line 732
732:     def include(header)
733:       @inc << "#include #{header}"
734:     end
include_ruby_last() click to toggle source

Specifies that the the ruby.h header should be included after custom header(s) instead of before them.

     # File lib/inline.rb, line 740
740:     def include_ruby_last
741:       @include_ruby_first = false
742:     end
load() click to toggle source

Loads the generated code back into ruby

     # File lib/inline.rb, line 517
517:     def load
518:       require "#{so_name}" or raise LoadError, "require on #{so_name} failed"
519:     end
load_cache() click to toggle source

Attempts to load pre-generated code returning true if it succeeds.

     # File lib/inline.rb, line 501
501:     def load_cache
502:       begin
503:         file = File.join("inline", File.basename(so_name))
504:         if require file then
505:           dir = Inline.directory
506:           warn "WAR\NING: #{dir} exists but is not being used" if test dd, dir and $VERBOSE
507:           return true
508:         end
509:       rescue LoadError
510:       end
511:       return false
512:     end
map_c_const(names_and_types) click to toggle source

Maps a C constant to ruby. names_and_types is a hash that maps the name of the constant to its C type.

  builder.map_c_const :C_NAME => :int

If you wish to give the constant a different ruby name:

  builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
     # File lib/inline.rb, line 721
721:     def map_c_const(names_and_types)
722:       names_and_types.each do |name, typ|
723:         typ, ruby_name = Array === typ ? typ : [typ, name]
724:         self.add_to_init "    rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
725:       end
726:     end
map_ruby_const(*names) click to toggle source

Maps a ruby constant to C (with the same name)

     # File lib/inline.rb, line 704
704:     def map_ruby_const(*names)
705:       names.each do |name|
706:         self.prefix "static VALUE #{name};"
707:         self.add_to_init "    #{name} = rb_const_get(c, rb_intern(#{name.to_s.inspect}));"
708:       end
709:     end
module_name() click to toggle source
     # File lib/inline.rb, line 368
368:     def module_name
369:       unless defined? @module_name then
370:         module_name = @mod.name.gsub('::','__')
371:         md5 = Digest::MD5.new
372:         @sig.keys.sort_by { |x| x.to_s }.each { |m| md5 << m.to_s }
373:         @module_name = "Inline_#{module_name}_#{md5}"
374:       end
375:       @module_name
376:     end
parse_signature(src, raw=false) click to toggle source
     # File lib/inline.rb, line 198
198:     def parse_signature(src, raw=false)
199: 
200:       sig = self.strip_comments(src)
201:       # strip preprocessor directives
202:       sig.gsub!(/^\s*\#.*(\\\n.*)*/, '')
203:       # strip {}s
204:       sig.gsub!(/\{[^\}]*\}/, '{ }')
205:       # clean and collapse whitespace
206:       sig.gsub!(/\s+/, ' ')
207: 
208:       unless defined? @types then
209:         @types = 'void|' + @type_map.keys.map{|x| Regexp.escape(x)}.join('|')
210:       end
211: 
212:       if /(#{@types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then
213:         return_type, function_name, arg_string = $1, $2, $3
214:         args = []
215:         arg_string.split(',').each do |arg|
216: 
217:           # helps normalize into 'char * varname' form
218:           arg = arg.gsub(/\s*\*\s*/, ' * ').strip
219: 
220:           if /(((#{@types})\s*\*?)+)\s+(\w+)\s*$/ =~ arg then
221:             args.push([$4, $1])
222:           elsif arg != "void" then
223:             warn "WAR\NING: '#{arg}' not understood"
224:           end
225:         end
226: 
227:         arity = args.size
228:         arity = MAGIC_ARITY if raw
229: 
230:         return {
231:           'return' => return_type,
232:           'name'   => function_name,
233:           'args'   => args,
234:           'arity'  => arity
235:         }
236:       end
237: 
238:       raise SyntaxError, "Can't parse signature: #{sig}"
239:     end
prefix(code) click to toggle source

Adds any amount of text/code to the source

     # File lib/inline.rb, line 747
747:     def prefix(code)
748:       @src << code
749:     end
reader(method, type, member = method) click to toggle source

Adds a reader for a C struct member wrapped via Data_Wrap_Struct. method is the ruby name to give the reader, type is the C type. Unless the C member name is overridden with member, the method name is used as the struct member. See # for an example.

     # File lib/inline.rb, line 444
444:     def reader(method, type, member = method)
445:       raise "struct name not set for reader #{method} #{type}" unless
446:         @struct_name
447: 
448:       c VALUE #{method}() {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  return #{c2ruby type}(pointer->#{member});}
449:     end
remove_type_converter(type) click to toggle source

Unregisters C type-casts for type.

     # File lib/inline.rb, line 697
697:     def remove_type_converter(type)
698:       @type_map.delete type
699:     end
ruby2c(type) click to toggle source

Converts ruby type type to a C type

     # File lib/inline.rb, line 485
485:     def ruby2c(type)
486:       raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
487:       @type_map[type].first
488:     end
so_name() click to toggle source
     # File lib/inline.rb, line 378
378:     def so_name
379:       unless defined? @so_name then
380:         @so_name = "#{Inline.directory}/#{module_name}.#{RbConfig::CONFIG["DLEXT"]}"
381:       end
382:       @so_name
383:     end
strip_comments(src) click to toggle source
     # File lib/inline.rb, line 189
189:     def strip_comments(src)
190:       # strip c-comments
191:       src = src.gsub(%\s*/\*.*?\*/%, '')
192:       # strip cpp-comments
193:       src = src.gsub(%^\s*//.*?\n%, '')
194:       src = src.gsub(%[ \t]*//[^\n]*%, '')
195:       src
196:     end
writer(method, type, member = method) click to toggle source

Adds a writer for a C struct member wrapped via Data_Get_Struct. method is the ruby name to give the writer, type is the C type. Unless the C member name is overridden with member, the method name is used as the struct member. See # for an example.

     # File lib/inline.rb, line 465
465:     def writer(method, type, member = method)
466:       raise "struct name not set for writer #{method} #{type}" unless
467:         @struct_name
468: 
469:       c VALUE #{method}_equals(VALUE value) {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  pointer->#{member} = #{ruby2c type}(value);  return value;}
470:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.