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 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
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
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
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 646 646: def add_link_flags(*flags) 647: @libs.push(*flags) 648: end
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
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
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
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
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
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
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
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
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
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
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
# 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
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
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
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
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
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
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
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
# 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
# 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
Adds any amount of text/code to the source
# File lib/inline.rb, line 736 736: def prefix(code) 737: @src << code 738: 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 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
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
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
# 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
# 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
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.
Generated with the Darkfish Rdoc Generator 1.1.6.