ffi-compiler 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +3 -3
- data/ffi-compiler.gemspec +1 -1
- data/lib/ffi-compiler/compile_task.rb +216 -0
- data/lib/ffi-compiler/export_task.rb +49 -0
- data/lib/ffi-compiler/exporter.rb +4 -0
- data/lib/ffi-compiler/fake_ffi/ffi-compiler/loader.rb +8 -0
- data/lib/ffi-compiler/fake_ffi/ffi.rb +170 -0
- data/lib/ffi-compiler/loader.rb +3 -2
- data/lib/ffi-compiler/platform.rb +29 -0
- data/lib/ffi-compiler/task.rb +5 -157
- metadata +8 -2
data/README.md
CHANGED
@@ -33,13 +33,13 @@ Example
|
|
33
33
|
long
|
34
34
|
example(void)
|
35
35
|
{
|
36
|
-
|
36
|
+
return 0xdeadbeef;
|
37
37
|
}
|
38
38
|
|
39
39
|
###### ext/Rakefile
|
40
|
-
require 'ffi-compiler/
|
40
|
+
require 'ffi-compiler/compile_task'
|
41
41
|
|
42
|
-
FFI::Compiler::
|
42
|
+
FFI::Compiler::CompileTask.new('example') do |c|
|
43
43
|
c.have_header?('stdio.h', '/usr/local/include')
|
44
44
|
c.have_func?('puts')
|
45
45
|
c.have_library?('z')
|
data/ffi-compiler.gemspec
CHANGED
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'ffi'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'rbconfig'
|
7
|
+
require_relative 'platform'
|
8
|
+
|
9
|
+
module FFI
|
10
|
+
module Compiler
|
11
|
+
DEFAULT_CFLAGS = %w(-fexceptions -O -fno-omit-frame-pointer -fno-strict-aliasing)
|
12
|
+
DEFAULT_LDFLAGS = %w(-fexceptions)
|
13
|
+
|
14
|
+
class CompileTask < Rake::TaskLib
|
15
|
+
attr_reader :cflags, :cxxflags, :ldflags, :libs, :platform
|
16
|
+
|
17
|
+
def initialize(name)
|
18
|
+
@name = File.basename(name)
|
19
|
+
@ext_dir = File.dirname(name)
|
20
|
+
@defines = []
|
21
|
+
@include_paths = []
|
22
|
+
@library_paths = []
|
23
|
+
@libraries = []
|
24
|
+
@headers = []
|
25
|
+
@functions = []
|
26
|
+
@cflags = DEFAULT_CFLAGS.dup
|
27
|
+
@cxxflags = DEFAULT_CFLAGS.dup
|
28
|
+
@ldflags = DEFAULT_LDFLAGS.dup
|
29
|
+
@libs = []
|
30
|
+
@platform = Platform.system
|
31
|
+
@exports = []
|
32
|
+
|
33
|
+
yield self if block_given?
|
34
|
+
define_task!
|
35
|
+
end
|
36
|
+
|
37
|
+
def have_func?(func)
|
38
|
+
main = <<-C_FILE
|
39
|
+
extern void #{func}();
|
40
|
+
int main(int argc, char **argv) { #{func}(); return 0; }
|
41
|
+
C_FILE
|
42
|
+
|
43
|
+
if try_compile(main)
|
44
|
+
@functions << func
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
false
|
48
|
+
end
|
49
|
+
|
50
|
+
def have_header?(header, *paths)
|
51
|
+
try_header(header, @include_paths) || try_header(header, paths)
|
52
|
+
end
|
53
|
+
|
54
|
+
def have_library?(libname, *paths)
|
55
|
+
try_library(libname, paths: @library_paths) || try_library(libname, paths: paths)
|
56
|
+
end
|
57
|
+
|
58
|
+
def have_library(lib, func = nil, headers = nil, &b)
|
59
|
+
try_library(lib, function: func, headers: headers, paths: @library_paths)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_library(lib, func, *paths)
|
63
|
+
try_library(lib, function: func, paths: @library_paths) || try_library(libname, function: func, paths: paths)
|
64
|
+
end
|
65
|
+
|
66
|
+
def export(rb_file)
|
67
|
+
@exports << { :rb_file => rb_file, :header => File.join(@ext_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def define_task!
|
72
|
+
pic_flags = %w(-fPIC)
|
73
|
+
so_flags = []
|
74
|
+
|
75
|
+
if @platform.mac?
|
76
|
+
pic_flags = []
|
77
|
+
so_flags << '-bundle'
|
78
|
+
|
79
|
+
elsif @platform.name =~ /linux/
|
80
|
+
so_flags << "-shared -Wl,-soname,#{lib_name}"
|
81
|
+
|
82
|
+
else
|
83
|
+
so_flags << '-shared'
|
84
|
+
end
|
85
|
+
so_flags = so_flags.join(' ')
|
86
|
+
|
87
|
+
out_dir = "#{@platform.arch}-#{@platform.os}"
|
88
|
+
if @ext_dir != '.'
|
89
|
+
out_dir = File.join(@ext_dir, out_dir)
|
90
|
+
end
|
91
|
+
|
92
|
+
directory(out_dir)
|
93
|
+
CLOBBER.include(out_dir)
|
94
|
+
|
95
|
+
lib_name = File.join(out_dir, Platform.system.map_library_name(@name))
|
96
|
+
|
97
|
+
iflags = @include_paths.uniq.map { |p| "-I#{p}" }
|
98
|
+
defines = @functions.uniq.map { |f| "-DHAVE_#{f.upcase}=1" }
|
99
|
+
defines << @headers.uniq.map { |h| "-DHAVE_#{h.upcase.sub(/\./, '_')}=1" }
|
100
|
+
|
101
|
+
cflags = (@cflags + pic_flags + iflags + defines).join(' ')
|
102
|
+
cxxflags = (@cxxflags + @cflags + pic_flags + iflags + defines).join(' ')
|
103
|
+
ld_flags = (@library_paths.map { |path| "-L#{path}" } + @ldflags).join(' ')
|
104
|
+
libs = (@libraries.map { |l| "-l#{l}" } + @libs).join(' ')
|
105
|
+
|
106
|
+
src_files = FileList["#{@ext_dir}/**/*.{c,cpp}"]
|
107
|
+
obj_files = src_files.ext('.o').map { |f| File.join(out_dir, f.sub(/^#{@ext_dir}\//, '')) }
|
108
|
+
ld = src_files.detect { |f| f =~ /\.cpp$/ } ? cxx : cc
|
109
|
+
|
110
|
+
src_files.each do |src|
|
111
|
+
obj_file = File.join(out_dir, src.sub(/\.(c|cpp)$/, '.o').sub(/^#{@ext_dir}\//, ''))
|
112
|
+
if src =~ /\.c$/
|
113
|
+
file obj_file => [ src, File.dirname(obj_file) ] do |t|
|
114
|
+
sh "#{cc} #{cflags} -o #{t.name} -c #{t.prerequisites[0]}"
|
115
|
+
end
|
116
|
+
|
117
|
+
else
|
118
|
+
file obj_file => [ src, File.dirname(obj_file) ] do |t|
|
119
|
+
sh "#{cxx} #{cxxflags} -o #{t.name} -c #{t.prerequisites[0]}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
CLEAN.include(obj_file)
|
124
|
+
end
|
125
|
+
|
126
|
+
desc "Build dynamic library"
|
127
|
+
file lib_name => obj_files do |t|
|
128
|
+
sh "#{ld} #{so_flags} -o #{t.name} #{t.prerequisites.join(' ')} #{ld_flags} #{libs}"
|
129
|
+
end
|
130
|
+
CLEAN.include(lib_name)
|
131
|
+
|
132
|
+
@exports.each do |e|
|
133
|
+
desc "Export #{e[:rb_file]}"
|
134
|
+
file e[:header] => [ e[:rb_file] ] do |t|
|
135
|
+
ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
|
136
|
+
end
|
137
|
+
|
138
|
+
obj_files.each { |o| file o => [ e[:header] ] }
|
139
|
+
CLEAN.include(e[:header])
|
140
|
+
|
141
|
+
desc "Export API headers"
|
142
|
+
task :api_headers => [ e[:header] ]
|
143
|
+
end
|
144
|
+
|
145
|
+
task :default => [ lib_name ]
|
146
|
+
task :package => [ :api_headers ]
|
147
|
+
end
|
148
|
+
|
149
|
+
def try_header(header, paths)
|
150
|
+
main = <<-C_FILE
|
151
|
+
#include <#{header}>
|
152
|
+
int main(int argc, char **argv) { return 0; }
|
153
|
+
C_FILE
|
154
|
+
|
155
|
+
if paths.empty? && try_compile(main)
|
156
|
+
@headers << header
|
157
|
+
return true
|
158
|
+
end
|
159
|
+
|
160
|
+
paths.each do |path|
|
161
|
+
if try_compile(main, "-I#{path}")
|
162
|
+
@include_paths << path
|
163
|
+
@headers << header
|
164
|
+
return true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def try_library(libname, options = {})
|
172
|
+
func = options[:function] || 'main'
|
173
|
+
paths = options[:paths] || ''
|
174
|
+
main = <<-C_FILE
|
175
|
+
#{(options[:headers] || []).map {|h| "#include <#{h}>"}.join('\n')}
|
176
|
+
extern int #{func}();
|
177
|
+
int main() { return #{func}(); }
|
178
|
+
C_FILE
|
179
|
+
|
180
|
+
if paths.empty? && try_compile(main)
|
181
|
+
@libraries << libname
|
182
|
+
return true
|
183
|
+
end
|
184
|
+
|
185
|
+
paths.each do |path|
|
186
|
+
if try_compile(main, "-L#{path}", "-l#{libname}")
|
187
|
+
@library_paths << path
|
188
|
+
@libraries << libname
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def try_compile(src, *opts)
|
194
|
+
Dir.mktmpdir do |dir|
|
195
|
+
path = File.join(dir, 'ffi-test.c')
|
196
|
+
File.open(path, 'w') do |f|
|
197
|
+
f << src
|
198
|
+
end
|
199
|
+
begin
|
200
|
+
return system "#{cc} #{opts.join(' ')} -o #{File.join(dir, 'ffi-test')} #{path} >& /dev/null"
|
201
|
+
rescue
|
202
|
+
return false
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def cc
|
208
|
+
@cc ||= (ENV['CC'] || RbConfig::CONFIG['CC'] || 'cc')
|
209
|
+
end
|
210
|
+
|
211
|
+
def cxx
|
212
|
+
@cxx ||= (ENV['CXX'] || RbConfig::CONFIG['CXX'] || 'c++')
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
require 'rake/clean'
|
4
|
+
|
5
|
+
module FFI
|
6
|
+
module Compiler
|
7
|
+
class ExportTask < Rake::TaskLib
|
8
|
+
|
9
|
+
def initialize(rb_dir, out_dir, options = {})
|
10
|
+
@rb_dir = rb_dir
|
11
|
+
@out_dir = out_dir
|
12
|
+
@gem_spec = options[:gem_spec]
|
13
|
+
@exports = []
|
14
|
+
|
15
|
+
if block_given?
|
16
|
+
yield self
|
17
|
+
define_tasks!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def export(rb_file)
|
22
|
+
@exports << { :rb_file => File.join(@rb_dir, rb_file), :header => File.join(@out_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def export_all
|
26
|
+
Dir["#@rb_dir/**/*rb"].each do |rb_file|
|
27
|
+
@exports << { :rb_file => rb_file, :header => File.join(@out_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def define_tasks!
|
33
|
+
@exports.each do |e|
|
34
|
+
file e[:header] => [ e[:rb_file] ] do |t|
|
35
|
+
ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
|
36
|
+
end
|
37
|
+
CLEAN.include(e[:header])
|
38
|
+
|
39
|
+
desc "Export API headers"
|
40
|
+
task :api_headers => [ e[:header] ]
|
41
|
+
@gem_spec.files << e[:header] unless @gem_spec.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
task :gem => [ :api_headers ] unless @gem_spec.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module FFI
|
2
|
+
|
3
|
+
def self.exporter=(exporter)
|
4
|
+
@@exporter = exporter
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.exporter
|
8
|
+
@@exporter
|
9
|
+
end
|
10
|
+
|
11
|
+
class Type
|
12
|
+
attr_reader :name
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class StructByReference < Type
|
19
|
+
def initialize(struct_class)
|
20
|
+
super("struct #{struct_class.to_s.gsub('::', '_')} *")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class StructByValue < Type
|
25
|
+
def initialize(struct_class)
|
26
|
+
super("struct #{struct_class.to_s.gsub('::', '_')}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
PrimitiveTypes = {
|
31
|
+
:char => 'char',
|
32
|
+
:uchar => 'unsigned char',
|
33
|
+
:short => 'short',
|
34
|
+
:ushort => 'unsigned short',
|
35
|
+
:int => 'int',
|
36
|
+
:uint => 'unsigned int',
|
37
|
+
:long => 'long',
|
38
|
+
:ulong => 'unsigned long',
|
39
|
+
:float => 'float',
|
40
|
+
:double => 'double',
|
41
|
+
:pointer => 'void *',
|
42
|
+
:string => 'const char *',
|
43
|
+
}
|
44
|
+
|
45
|
+
TypeMap = {}
|
46
|
+
def self.find_type(type)
|
47
|
+
return type if type.is_a?(Type)
|
48
|
+
|
49
|
+
t = TypeMap[type]
|
50
|
+
return t unless t.nil?
|
51
|
+
|
52
|
+
if PrimitiveTypes.has_key?(type)
|
53
|
+
return TypeMap[type] = Type.new(PrimitiveTypes[type])
|
54
|
+
end
|
55
|
+
raise TypeError.new("cannot resolve type #{type}")
|
56
|
+
end
|
57
|
+
|
58
|
+
class Exporter
|
59
|
+
attr_reader :mod, :functions
|
60
|
+
|
61
|
+
def initialize(mod)
|
62
|
+
@mod = mod
|
63
|
+
@functions = []
|
64
|
+
@structs = []
|
65
|
+
end
|
66
|
+
|
67
|
+
def attach(mname, fname, result_type, param_types)
|
68
|
+
@functions << { mname: mname, fname: fname, result_type: result_type, params: param_types.dup }
|
69
|
+
end
|
70
|
+
|
71
|
+
def struct(name, fields)
|
72
|
+
@structs << { name: name, fields: fields.dup }
|
73
|
+
end
|
74
|
+
|
75
|
+
def dump(out_file)
|
76
|
+
File.open(out_file, 'w') do |f|
|
77
|
+
guard = File.basename(out_file).upcase.gsub('.', '_').gsub('/', '_')
|
78
|
+
f.puts <<-HEADER
|
79
|
+
#ifndef #{guard}
|
80
|
+
#define #{guard} 1
|
81
|
+
|
82
|
+
#ifndef RBFFI_EXPORT
|
83
|
+
# ifdef __cplusplus
|
84
|
+
# define RBFFI_EXPORT extern "C"
|
85
|
+
# else
|
86
|
+
# define RBFFI_EXPORT
|
87
|
+
# endif
|
88
|
+
#endif
|
89
|
+
|
90
|
+
HEADER
|
91
|
+
|
92
|
+
@structs.each do |s|
|
93
|
+
f.puts "struct #{s[:name].gsub('::', '_')} {"
|
94
|
+
s[:fields].each do |field|
|
95
|
+
f.puts "#{' ' * 4}#{field[:type].name} #{field[:name].to_s};"
|
96
|
+
end
|
97
|
+
f.puts '};'
|
98
|
+
f.puts
|
99
|
+
end
|
100
|
+
@functions.each do |fn|
|
101
|
+
param_string = fn[:params].empty? ? 'void' : fn[:params].map(&:name).join(', ')
|
102
|
+
f.puts "RBFFI_EXPORT #{fn[:result_type].name} #{fn[:fname]}(#{param_string});"
|
103
|
+
end
|
104
|
+
f.puts <<-EPILOG
|
105
|
+
|
106
|
+
#endif /* #{guard} */
|
107
|
+
EPILOG
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
module Library
|
114
|
+
def self.extended(mod)
|
115
|
+
FFI.exporter = Exporter.new(mod)
|
116
|
+
end
|
117
|
+
|
118
|
+
def attach_function(*args)
|
119
|
+
FFI.exporter.attach(args[0], args[0], find_type(args[2]), args[1].map { |t| find_type(t) })
|
120
|
+
end
|
121
|
+
|
122
|
+
def ffi_lib(*args)
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
TypeMap = {}
|
127
|
+
def find_type(type)
|
128
|
+
t = TypeMap[type]
|
129
|
+
return t unless t.nil?
|
130
|
+
|
131
|
+
if type.is_a?(Class) && type < Struct
|
132
|
+
return TypeMap[type] = StructByReference.new(type)
|
133
|
+
end
|
134
|
+
|
135
|
+
TypeMap[type] = FFI.find_type(type)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Struct
|
140
|
+
def self.layout(*args)
|
141
|
+
fields = []
|
142
|
+
i = 0
|
143
|
+
while i < args.length
|
144
|
+
fields << { name: args[i], type: find_type(args[i+1]) }
|
145
|
+
i += 2
|
146
|
+
end
|
147
|
+
FFI.exporter.struct(self.to_s, fields)
|
148
|
+
end
|
149
|
+
|
150
|
+
TypeMap = {}
|
151
|
+
def self.find_type(type)
|
152
|
+
t = TypeMap[type]
|
153
|
+
return t unless t.nil?
|
154
|
+
|
155
|
+
if type.is_a?(Class) && type < Struct
|
156
|
+
return TypeMap[type] = StructByValue.new(type)
|
157
|
+
end
|
158
|
+
|
159
|
+
TypeMap[type] = FFI.find_type(type)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.by_value
|
163
|
+
StructByValue.new(self)
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.by_ref
|
167
|
+
StructByReference.new(self)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/ffi-compiler/loader.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'ffi'
|
3
|
+
require_relative 'platform'
|
3
4
|
|
4
5
|
module FFI
|
5
6
|
module Compiler
|
6
7
|
module Loader
|
7
8
|
def self.find(name, start_path = nil)
|
8
|
-
library =
|
9
|
+
library = Platform.system.map_library_name(name)
|
9
10
|
root = false
|
10
11
|
Pathname.new(start_path || File.dirname(caller[0].split(/:/)[0])).ascend do |path|
|
11
|
-
Dir.glob("#{path}
|
12
|
+
Dir.glob("#{path}/**/{#{FFI::Platform::ARCH}-#{FFI::Platform::OS}/#{library},#{library}}") do |f|
|
12
13
|
return f
|
13
14
|
end
|
14
15
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module FFI::Compiler
|
2
|
+
class Platform
|
3
|
+
LIBSUFFIX = FFI::Platform.mac? ? 'bundle' : FFI::Platform::LIBSUFFIX
|
4
|
+
|
5
|
+
def self.system
|
6
|
+
@@system ||= Platform.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def map_library_name(name)
|
10
|
+
"#{FFI::Platform::LIBPREFIX}#{name}.#{LIBSUFFIX}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def arch
|
14
|
+
FFI::Platform::ARCH
|
15
|
+
end
|
16
|
+
|
17
|
+
def os
|
18
|
+
FFI::Platform::OS
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
FFI::Platform.name
|
23
|
+
end
|
24
|
+
|
25
|
+
def mac?
|
26
|
+
FFI::Platform.mac?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ffi-compiler/task.rb
CHANGED
@@ -1,167 +1,15 @@
|
|
1
|
+
require 'rake'
|
1
2
|
require 'rake/tasklib'
|
2
3
|
require 'rake/clean'
|
3
4
|
require 'ffi'
|
4
5
|
require 'tmpdir'
|
5
6
|
require 'rbconfig'
|
7
|
+
require_relative 'compile_task'
|
6
8
|
|
7
9
|
module FFI
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
class Task < Rake::TaskLib
|
13
|
-
attr_reader :cflags, :cxxflags, :ldflags, :libs
|
14
|
-
|
15
|
-
def initialize(name)
|
16
|
-
@name = File.basename(name)
|
17
|
-
@defines = []
|
18
|
-
@include_paths = []
|
19
|
-
@library_paths = []
|
20
|
-
@libraries = []
|
21
|
-
@headers = []
|
22
|
-
@functions = []
|
23
|
-
@cflags = DEFAULT_CFLAGS.dup
|
24
|
-
@cxxflags = DEFAULT_CFLAGS.dup
|
25
|
-
@ldflags = DEFAULT_LDFLAGS.dup
|
26
|
-
@libs = []
|
27
|
-
|
28
|
-
yield self if block_given?
|
29
|
-
define_task!
|
30
|
-
end
|
31
|
-
|
32
|
-
def have_func?(func)
|
33
|
-
main = <<-C_FILE
|
34
|
-
extern void #{func}();
|
35
|
-
int main(int argc, char **argv) { #{func}(); return 0; }
|
36
|
-
C_FILE
|
37
|
-
|
38
|
-
if try_compile(main)
|
39
|
-
@functions << func
|
40
|
-
return true
|
41
|
-
end
|
42
|
-
false
|
43
|
-
end
|
44
|
-
|
45
|
-
def have_header?(header, *paths)
|
46
|
-
try_header(header, @include_paths) || try_header(header, paths)
|
47
|
-
end
|
48
|
-
|
49
|
-
def have_library?(libname, *paths)
|
50
|
-
try_library(libname, @library_paths) || try_library(libname, paths)
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
def define_task!
|
55
|
-
pic_flags = %w(-fPIC)
|
56
|
-
so_flags = []
|
57
|
-
|
58
|
-
if FFI::Platform.mac?
|
59
|
-
pic_flags = []
|
60
|
-
so_flags << '-dynamiclib'
|
61
|
-
|
62
|
-
elsif FFI::Platform.name =~ /linux/
|
63
|
-
so_flags << "-shared -Wl,-soname,#{lib_name}"
|
64
|
-
|
65
|
-
else
|
66
|
-
so_flags << '-shared'
|
67
|
-
end
|
68
|
-
so_flags = so_flags.join(' ')
|
69
|
-
|
70
|
-
lib_name = FFI.map_library_name(@name)
|
71
|
-
|
72
|
-
iflags = @include_paths.uniq.map { |p| "-I#{p}" }
|
73
|
-
defines = @functions.uniq.map { |f| "-DHAVE_#{f.upcase}=1" }
|
74
|
-
defines << @headers.uniq.map { |h| "-DHAVE_#{h.upcase.sub(/\./, '_')}=1" }
|
75
|
-
|
76
|
-
cflags = (@cflags + pic_flags + iflags + defines).join(' ')
|
77
|
-
cxxflags = (@cxxflags + @cflags + pic_flags + iflags + defines).join(' ')
|
78
|
-
ld_flags = (@library_paths.map { |path| "-L#{path}" } + @ldflags).join(' ')
|
79
|
-
libs = (@libraries.map { |l| "-l#{l}" } + @libs).join(' ')
|
80
|
-
|
81
|
-
src_files = FileList['*.{c,cpp}']
|
82
|
-
obj_files = src_files.ext '.o'
|
83
|
-
ld = src_files.detect { |f| f =~ /\.cpp$/ } ? cxx : cc
|
84
|
-
|
85
|
-
CLEAN.include(obj_files)
|
86
|
-
|
87
|
-
rule '.o' => '.c' do |t|
|
88
|
-
sh "#{cc} #{cflags} -o #{t.name} -c #{t.source}"
|
89
|
-
end
|
90
|
-
|
91
|
-
rule '.o' => '.cpp' do |t|
|
92
|
-
sh "#{cxx} #{cxxflags} -o #{t.name} -c #{t.source}"
|
93
|
-
end
|
94
|
-
|
95
|
-
desc "Build dynamic library"
|
96
|
-
file lib_name => obj_files do |t|
|
97
|
-
sh "#{ld} #{so_flags} -o #{t.name} #{t.prerequisites.join(' ')} #{ld_flags} #{libs}"
|
98
|
-
end
|
99
|
-
CLEAN.include(lib_name)
|
100
|
-
|
101
|
-
task :default => [ lib_name ]
|
102
|
-
end
|
103
|
-
|
104
|
-
def try_header(header, paths)
|
105
|
-
main = <<-C_FILE
|
106
|
-
#include <#{header}>
|
107
|
-
int main(int argc, char **argv) { return 0; }
|
108
|
-
C_FILE
|
109
|
-
|
110
|
-
if paths.empty? && try_compile(main)
|
111
|
-
@headers << header
|
112
|
-
return true
|
113
|
-
end
|
114
|
-
|
115
|
-
paths.each do |path|
|
116
|
-
if try_compile(main, "-I#{path}")
|
117
|
-
@include_paths << path
|
118
|
-
@headers << header
|
119
|
-
return true
|
120
|
-
end
|
121
|
-
end
|
122
|
-
false
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
def try_library(libname, paths)
|
127
|
-
main = <<-C_FILE
|
128
|
-
int main(int argc, char **argv) { return 0; }
|
129
|
-
C_FILE
|
130
|
-
|
131
|
-
if paths.empty? && try_compile(main)
|
132
|
-
@libraries << libname
|
133
|
-
return true
|
134
|
-
end
|
135
|
-
|
136
|
-
paths.each do |path|
|
137
|
-
if try_compile(main, "-L#{path}", "-l#{libname}")
|
138
|
-
@library_paths << path
|
139
|
-
@libraries << libname
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def try_compile(src, *opts)
|
145
|
-
Dir.mktmpdir do |dir|
|
146
|
-
path = File.join(dir, 'ffi-test.c')
|
147
|
-
File.open(path, 'w') do |f|
|
148
|
-
f << src
|
149
|
-
end
|
150
|
-
begin
|
151
|
-
return system "#{cc} #{opts.join(' ')} -o #{File.join(dir, 'ffi-test')} #{path} >& /dev/null"
|
152
|
-
rescue
|
153
|
-
return false
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def cc
|
159
|
-
@cc ||= (ENV['CC'] || RbConfig::CONFIG['CC'] || 'cc')
|
160
|
-
end
|
161
|
-
|
162
|
-
def cxx
|
163
|
-
@cxx ||= (ENV['CXX'] || RbConfig::CONFIG['CXX'] || 'c++')
|
164
|
-
end
|
10
|
+
module Compiler
|
11
|
+
class Task < CompileTask
|
12
|
+
warn "#{self} is deprecated"
|
165
13
|
end
|
166
14
|
end
|
167
15
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -69,7 +69,13 @@ files:
|
|
69
69
|
- README.md
|
70
70
|
- Rakefile
|
71
71
|
- LICENSE
|
72
|
+
- lib/ffi-compiler/compile_task.rb
|
73
|
+
- lib/ffi-compiler/export_task.rb
|
74
|
+
- lib/ffi-compiler/exporter.rb
|
75
|
+
- lib/ffi-compiler/fake_ffi/ffi-compiler/loader.rb
|
76
|
+
- lib/ffi-compiler/fake_ffi/ffi.rb
|
72
77
|
- lib/ffi-compiler/loader.rb
|
78
|
+
- lib/ffi-compiler/platform.rb
|
73
79
|
- lib/ffi-compiler/task.rb
|
74
80
|
homepage: http://wiki.github.com/ffi/ffi
|
75
81
|
licenses:
|