Object
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 #.
# 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
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
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
Adds linker flags to the link command line. No preprocessing is done, so you must have all your dashes and everything.
# File lib/inline.rb, line 657 657: def add_link_flags(*flags) 658: @libs.push(*flags) 659: end
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
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
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
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
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
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
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
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
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
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
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
# 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
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
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
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
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
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
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
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
# 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
# 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
Adds any amount of text/code to the source
# File lib/inline.rb, line 747 747: def prefix(code) 748: @src << code 749: end
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
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
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
# 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
# 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
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.
Generated with the Darkfish Rdoc Generator 1.1.6.