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