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 390
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

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 428
428:     def accessor(method, type, member = method)
429:       reader method, type, member
430:       writer method, type, member
431:     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 638
638:     def add_compile_flags(*flags)
639:       @flags.push(*flags)
640:     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 653
653:     def add_static name, init, type = "VALUE"
654:       prefix      "static #{type} #{name};"
655:       add_to_init "#{name} = #{init};"
656:     end
add_to_init(*src) click to toggle source

Adds custom content to the end of the init function.

     # File lib/inline.rb, line 661
661:     def add_to_init(*src)
662:       @init_extra.push(*src)
663:     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 668
668:     def add_type_converter(type, r2c, c2r)
669:       warn "WAR\NING: overridding #{type} on #{caller[0]}" if @type_map.has_key? type
670:       @type_map[type] = [r2c, c2r]
671:     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 676
676:     def alias_type_converter(existing_type, alias_type)
677:       warn "WAR\NING: overridding #{type} on #{caller[0]}" if
678:         @type_map.has_key? alias_type
679: 
680:       @type_map[alias_type] = @type_map[existing_type]
681:     end
build() click to toggle source

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

     # File lib/inline.rb, line 519
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 = %(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['CCDLFLAGS'],
568:                   Config::CONFIG['CFLAGS'],
569:                   '-I', hdrdir,
570:                   config_hdrdir,
571:                   '-I', Config::CONFIG['includedir'],
572:                   "-L#{Config::CONFIG['libdir']}",
573:                   '-o', so_name.inspect,
574:                   File.expand_path(src_name).inspect,
575:                   libs,
576:                   crap_for_windoze ].join(' ')
577: 
578:           # TODO: remove after osx 10.5.2
579:           cmd += ' -flat_namespace -undefined suppress' if
580:             RUBY_PLATFORM =~ /darwin9\.[01]/
581:           cmd += " 2> #{DEV_NULL}" if $TESTING and not $DEBUG
582: 
583:           warn "Building #{so_name} with '#{cmd}'" if $DEBUG
584:           result = `#{cmd}`
585:           warn "Output:\n#{result}" if $DEBUG
586:           if $? != 0 then
587:             bad_src_name = src_name + ".bad"
588:             File.rename src_name, bad_src_name
589:             raise CompilationError, "error executing #{cmd.inspect}: #{$?}\nRenamed #{src_name} to #{bad_src_name}"
590:           end
591: 
592:           # NOTE: manifest embedding is only required when using VC8 ruby
593:           # build or compiler.
594:           # Errors from this point should be ignored if Config::CONFIG['arch']
595:           # (RUBY_PLATFORM) matches 'i386-mswin32_80'
596:           if WINDOZE and RUBY_PLATFORM =~ /_80$/ then
597:             Dir.chdir Inline.directory do
598:               cmd = "mt /manifest lib.so.manifest /outputresource:so.dll;#2"
599:               warn "Embedding manifest with '#{cmd}'" if $DEBUG
600:               result = `#{cmd}`
601:               warn "Output:\n#{result}" if $DEBUG
602:               if $? != 0 then
603:                 raise CompilationError, "error executing #{cmd}: #{$?}"
604:               end
605:             end
606:           end
607: 
608:           warn "Built successfully" if $DEBUG
609:         end
610: 
611:       else
612:         warn "#{so_name} is up to date" if $DEBUG
613:       end # unless (file is out of date)
614:     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 746
746:     def c src, options = {}
747:       options = {
748:         :expand_types => true,
749:       }.merge options
750:       self.generate src, options
751:     end
c2ruby(type) click to toggle source

Converts C type type to a ruby type

     # File lib/inline.rb, line 488
488:     def c2ruby(type)
489:       raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
490:       @type_map[type].last
491:     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 770
770:     def c_raw src, options = {}
771:       self.generate src, options
772:     end
c_raw_singleton(src, options = {}) click to toggle source

Same as c_raw, but adds a class function.

     # File lib/inline.rb, line 777
777:     def c_raw_singleton src, options = {}
778:       options = {
779:         :singleton => true,
780:       }.merge options
781:       self.generate src, options
782:     end
c_singleton(src, options = {}) click to toggle source

Same as c, but adds a class function.

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

Returns extra compilation flags for windoze platforms. Ugh.

     # File lib/inline.rb, line 619
619:     def crap_for_windoze
620:       # gawd windoze land sucks
621:       case RUBY_PLATFORM
622:       when /mswin32/ then
623:         " -link /LIBPATH:\"#{Config::CONFIG['libdir']}\" /DEFAULTLIB:\"#{Config::CONFIG['LIBRUBY']}\" /INCREMENTAL:no /EXPORT:Init_#{module_name}"
624:       when /mingw32/ then
625:         " -Wl,--enable-auto-import -L#{Config::CONFIG['libdir']} -lmsvcrt-ruby18"
626:       when /i386-cygwin/ then
627:         ' -L/usr/local/lib -lruby.dll'
628:       else
629:         ''
630:       end
631:     end
generate(src, options={}) click to toggle source
     # File lib/inline.rb, line 238
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 = 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.*?\{)/ 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
generate_ext() click to toggle source

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

     # File lib/inline.rb, line 305
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
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 721
721:     def include(header)
722:       @inc << "#include #{header}"
723:     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 729
729:     def include_ruby_last
730:       @include_ruby_first = false
731:     end
load() click to toggle source

Loads the generated code back into ruby

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

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

     # File lib/inline.rb, line 496
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 dd, dir and $VERBOSE
502:           return true
503:         end
504:       rescue LoadError
505:       end
506:       return false
507:     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 710
710:     def map_c_const(names_and_types)
711:       names_and_types.each do |name, typ|
712:         typ, ruby_name = Array === typ ? typ : [typ, name]
713:         self.add_to_init "    rb_define_const(c, #{ruby_name.to_s.inspect}, #{c2ruby(typ.to_s)}(#{name}));"
714:       end
715:     end
map_ruby_const(*names) click to toggle source

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

     # File lib/inline.rb, line 693
693:     def map_ruby_const(*names)
694:       names.each do |name|
695:         self.prefix "static VALUE #{name};"
696:         self.add_to_init "    #{name} = rb_const_get(c, rb_intern(#{name.to_s.inspect}));"
697:       end
698:     end
module_name() click to toggle source
     # File lib/inline.rb, line 363
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
parse_signature(src, raw=false) click to toggle source
     # File lib/inline.rb, line 195
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
prefix(code) click to toggle source

Adds any amount of text/code to the source

     # File lib/inline.rb, line 736
736:     def prefix(code)
737:       @src << code
738:     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 439
439:     def reader(method, type, member = method)
440:       raise "struct name not set for reader #{method} #{type}" unless
441:         @struct_name
442: 
443:       c VALUE #{method}() {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  return #{c2ruby type}(pointer->#{member});}
444:     end
remove_type_converter(type) click to toggle source

Unregisters C type-casts for type.

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

Converts ruby type type to a C type

     # File lib/inline.rb, line 480
480:     def ruby2c(type)
481:       raise ArgumentError, "Unknown type #{type.inspect}" unless @type_map.has_key? type
482:       @type_map[type].first
483:     end
so_name() click to toggle source
     # File lib/inline.rb, line 373
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
strip_comments(src) click to toggle source
     # File lib/inline.rb, line 186
186:     def strip_comments(src)
187:       # strip c-comments
188:       src = src.gsub(%\s*/\*.*?\*/%, '')
189:       # strip cpp-comments
190:       src = src.gsub(%^\s*//.*?\n%, '')
191:       src = src.gsub(%[ \t]*//[^\n]*%, '')
192:       src
193:     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 460
460:     def writer(method, type, member = method)
461:       raise "struct name not set for writer #{method} #{type}" unless
462:         @struct_name
463: 
464:       c VALUE #{method}_equals(VALUE value) {  #{@struct_name} *pointer;  Data_Get_Struct(self, #{@struct_name}, pointer);  pointer->#{member} = #{ruby2c type}(value);  return value;}
465:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.