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.