lib/ffi-inliner/inliner.rb in ffi-inliner-0.2.2 vs lib/ffi-inliner/inliner.rb in ffi-inliner-0.2.3

- old
+ new

@@ -57,25 +57,30 @@ end module Compilers class Compiler attr_reader :progname - def self.check_and_create(fm = nil) - compiler = new(fm) + def self.check_and_create(fm = nil, libraries = nil) + compiler = new(fm, libraries) unless compiler.exists? raise "Can't find compiler #{compiler.class}" else compiler end end - def initialize(fm = nil) + def initialize(fm = nil, libraries = nil) @fm = fm + @libraries = libraries @progname = cmd.split.first end def compile raise "Compile error! See #{@fm.log_fn}" unless system(cmd) end + private + def libs + @libraries.inject("") { |str, lib| str << "-l#{lib} " } if @libraries + end end class GCC < Compiler def exists? IO.popen("#{@progname} 2>&1") { |f| f.gets } ? true : false @@ -86,55 +91,85 @@ else 'gcc -shared -fPIC' end end def cmd - "#{ldshared} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\"" + "#{ldshared} #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\"" end end + class GPlusPlus < GCC + def ldshared + if Config::CONFIG['target_os'] =~ /darwin/ + 'g++ -dynamic -bundle -fPIC' + else + 'g++ -shared -fPIC' + end + end + end + class TCC < Compiler def exists? IO.popen("#{@progname}") { |f| f.gets } ? true : false end def cmd - "tcc -shared -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\"" + if Config::CONFIG['target_os'] =~ /mswin|mingw/ + "tcc -rdynamic -shared #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\"" + else + "tcc -shared #{libs} -o \"#{@fm.so_fn}\" \"#{@fm.c_fn}\" 2>\"#{@fm.log_fn}\"" + end end end end class Builder - attr_reader :code + attr_reader :code, :compiler def initialize(mod, code = "", options = {}) make_pointer_types @mod = mod @code = code @sig = [parse_signature(@code)] unless @code.empty? - options = { :compiler => Compilers::GCC }.merge(options) - @compiler = options[:compiler] + options = { :use_compiler => Compilers::GCC }.merge(options) + @compiler = options[:use_compiler] end def map(type_map) @types.merge!(type_map) end + def include(fn, options = {}) + options[:quoted] ? @code << "#include \"#{fn}\"\n" : @code << "#include <#{fn}>\n" + end + + def library(*libraries) + (@libraries ||= []).concat(libraries) + end + def c(code) (@sig ||= []) << parse_signature(code) - @code << code + @code << (@compiler == Compilers::GPlusPlus ? "extern \"C\" {\n#{code}\n}" : code ) end def c_raw(code) @code << code end def use_compiler(compiler) @compiler = compiler end + def struct(ffi_struct) + @code << "typedef struct {" + ffi_struct.layout.fields.each do |field| + @code << "#{field} #{field.name};\n" + end + @code << "} #{ffi_struct.class.name}" + end + def build @fm = FilenameManager.new(@mod, @code) - @compiler = @compiler.check_and_create(@fm) + @compiler = @compiler.check_and_create(@fm, @libraries) unless @fm.cached? write_files(@code, @sig) @compiler.compile @mod.instance_eval generate_ffi(@sig) else @@ -171,18 +206,20 @@ end # Based on RubyInline code by Ryan Davis # Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software def parse_signature(code) + sig = strip_comments(code) + # strip preprocessor directives sig.gsub!(/^\s*\#.*(\\\n.*)*/, '') # strip {}s sig.gsub!(/\{[^\}]*\}/, '{ }') # clean and collapse whitespace sig.gsub!(/\s+/, ' ') - + # types = 'void|int|char|char\s\*|void\s\*' types = @types.keys.map{|x| Regexp.escape(x)}.join('|') sig = sig.gsub(/\s*\*\s*/, ' * ').strip if /(#{types})\s*(\w+)\s*\(([^)]*)\)/ =~ sig then @@ -213,18 +250,23 @@ raise SyntaxError, "Can't parse signature: #{sig}" end def generate_ffi(sig) + ffi_code = <<PREAMBLE extend FFI::Library ffi_lib '#{@fm.so_fn}' PREAMBLE - sig.each do |s| - args = s['args'].map { |arg| ":#{to_ffi_type(arg)}" }.join(',') - ffi_code << "attach_function '#{s['name']}', [#{args}], :#{to_ffi_type(s['return'])}\n" + + unless sig.nil? + sig.each do |s| + args = s['args'].map { |arg| ":#{to_ffi_type(arg)}" }.join(',') + ffi_code << "attach_function '#{s['name']}', [#{args}], :#{to_ffi_type(s['return'])}\n" + end end + ffi_code end def write_c(code) File.open(@fm.c_fn, 'w') { |f| f << code } end