ffi-compiler 0.1.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ffi-compiler.gemspec +1 -1
- data/lib/ffi-compiler/compile_task.rb +35 -11
- data/lib/ffi-compiler/fake_ffi/ffi.rb +135 -23
- metadata +16 -16
- data/lib/ffi-compiler/ffi-compiler.iml +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d72c341f9ccb50429bfd8a3542d3c9481f7e4d2
|
4
|
+
data.tar.gz: 780d11e250a8e1637d287d8482c7eb319d170db8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4d9af481db298d53bba6d89b85f4f288043748ca736f84f751f1cb16ad829be4719fbf5faab58a14ebdbaa91a01d0cafbaa31104c1dbfaae6cd9093f371c1e5
|
7
|
+
data.tar.gz: f7d499575077854ec5913209f0ec8fb83e01028a90b93ba86c8b221bc9b31f8c135a3df6370b47cc0a5f7a611167c1ae4f0971c6cec7032e40d435ff2123395e
|
data/ffi-compiler.gemspec
CHANGED
@@ -13,10 +13,13 @@ module FFI
|
|
13
13
|
|
14
14
|
class CompileTask < Rake::TaskLib
|
15
15
|
attr_reader :cflags, :cxxflags, :ldflags, :libs, :platform
|
16
|
+
attr_accessor :name, :ext_dir, :source_dirs, :exclude
|
16
17
|
|
17
18
|
def initialize(name)
|
18
19
|
@name = File.basename(name)
|
19
20
|
@ext_dir = File.dirname(name)
|
21
|
+
@source_dirs = [@ext_dir]
|
22
|
+
@exclude = []
|
20
23
|
@defines = []
|
21
24
|
@include_paths = []
|
22
25
|
@library_paths = []
|
@@ -34,6 +37,14 @@ module FFI
|
|
34
37
|
define_task!
|
35
38
|
end
|
36
39
|
|
40
|
+
def add_include_path(path)
|
41
|
+
@include_paths << path
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_define(name, value=1)
|
45
|
+
@defines << "-D#{name}=#{value}"
|
46
|
+
end
|
47
|
+
|
37
48
|
def have_func?(func)
|
38
49
|
main = <<-C_FILE
|
39
50
|
extern void #{func}();
|
@@ -95,20 +106,28 @@ module FFI
|
|
95
106
|
lib_name = File.join(out_dir, Platform.system.map_library_name(@name))
|
96
107
|
|
97
108
|
iflags = @include_paths.uniq.map { |p| "-I#{p}" }
|
98
|
-
defines
|
99
|
-
defines << @headers.uniq.map { |h| "-DHAVE_#{h.upcase.sub(/\./, '_')}=1" }
|
109
|
+
@defines << @functions.uniq.map { |f| "-DHAVE_#{f.upcase}=1" }
|
110
|
+
@defines << @headers.uniq.map { |h| "-DHAVE_#{h.upcase.sub(/\./, '_')}=1" }
|
100
111
|
|
101
|
-
cflags = (@cflags + pic_flags + iflags + defines).join(' ')
|
102
|
-
cxxflags = (@cxxflags + @cflags + pic_flags + iflags + defines).join(' ')
|
112
|
+
cflags = (@cflags + pic_flags + iflags + @defines).join(' ')
|
113
|
+
cxxflags = (@cxxflags + @cflags + pic_flags + iflags + @defines).join(' ')
|
103
114
|
ld_flags = (@library_paths.map { |path| "-L#{path}" } + @ldflags).join(' ')
|
104
115
|
libs = (@libraries.map { |l| "-l#{l}" } + @libs).join(' ')
|
105
116
|
|
106
|
-
src_files =
|
107
|
-
obj_files =
|
108
|
-
|
109
|
-
|
117
|
+
src_files = []
|
118
|
+
obj_files = []
|
119
|
+
@source_dirs.each do |dir|
|
120
|
+
files = FileList["#{dir}/**/*.{c,cpp}"]
|
121
|
+
unless @exclude.empty?
|
122
|
+
files.delete_if { |f| f =~ Regexp.union(*@exclude) }
|
123
|
+
end
|
124
|
+
src_files += files
|
125
|
+
obj_files += files.ext('.o').map { |f| File.join(out_dir, f.sub(/^#{dir}\//, '')) }
|
126
|
+
end
|
127
|
+
|
128
|
+
index = 0
|
110
129
|
src_files.each do |src|
|
111
|
-
obj_file =
|
130
|
+
obj_file = obj_files[index]
|
112
131
|
if src =~ /\.c$/
|
113
132
|
file obj_file => [ src, File.dirname(obj_file) ] do |t|
|
114
133
|
sh "#{cc} #{cflags} -o #{t.name} -c #{t.prerequisites[0]}"
|
@@ -121,8 +140,11 @@ module FFI
|
|
121
140
|
end
|
122
141
|
|
123
142
|
CLEAN.include(obj_file)
|
143
|
+
index += 1
|
124
144
|
end
|
125
145
|
|
146
|
+
ld = src_files.detect { |f| f =~ /\.cpp$/ } ? cxx : cc
|
147
|
+
|
126
148
|
# create all the directories for the output files
|
127
149
|
obj_files.map { |f| File.dirname(f) }.sort.uniq.map { |d| directory d }
|
128
150
|
|
@@ -135,7 +157,7 @@ module FFI
|
|
135
157
|
@exports.each do |e|
|
136
158
|
desc "Export #{e[:rb_file]}"
|
137
159
|
file e[:header] => [ e[:rb_file] ] do |t|
|
138
|
-
ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
|
160
|
+
ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} -I#{File.dirname(t.prerequisites[0])} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
|
139
161
|
end
|
140
162
|
|
141
163
|
obj_files.each { |o| file o => [ e[:header] ] }
|
@@ -199,8 +221,10 @@ module FFI
|
|
199
221
|
File.open(path, 'w') do |f|
|
200
222
|
f << src
|
201
223
|
end
|
224
|
+
cflags = opts.join(' ')
|
225
|
+
output = File.join(dir, 'ffi-test')
|
202
226
|
begin
|
203
|
-
return system "#{cc} #{
|
227
|
+
return system "#{cc} #{cflags} -o #{output} -c #{path} > #{path}.log 2>&1"
|
204
228
|
rescue
|
205
229
|
return false
|
206
230
|
end
|
@@ -3,11 +3,11 @@ module FFI
|
|
3
3
|
def self.exporter=(exporter)
|
4
4
|
@@exporter = exporter
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def self.exporter
|
8
|
-
@@exporter
|
8
|
+
@@exporter ||= Exporter.new(nil)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
class Type
|
12
12
|
attr_reader :name
|
13
13
|
def initialize(name)
|
@@ -26,8 +26,28 @@ module FFI
|
|
26
26
|
super("struct #{struct_class.to_s.gsub('::', '_')}")
|
27
27
|
end
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
class CallbackInfo
|
31
|
+
attr_reader :return_type
|
32
|
+
attr_reader :arg_types
|
33
|
+
attr_reader :options
|
34
|
+
|
35
|
+
def initialize(return_type, arg_types = [], *other)
|
36
|
+
@return_type = return_type
|
37
|
+
@arg_types = arg_types
|
38
|
+
@options = options
|
39
|
+
end
|
40
|
+
|
41
|
+
def name(name)
|
42
|
+
params = @arg_types.empty? ? 'void' : @arg_types.map(&:name).join(', ')
|
43
|
+
"#{@return_type.name} (*#{name})(#{params})"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
30
47
|
PrimitiveTypes = {
|
48
|
+
:void => 'void',
|
49
|
+
:bool => 'bool',
|
50
|
+
:string => 'const char *',
|
31
51
|
:char => 'char',
|
32
52
|
:uchar => 'unsigned char',
|
33
53
|
:short => 'short',
|
@@ -36,15 +56,29 @@ module FFI
|
|
36
56
|
:uint => 'unsigned int',
|
37
57
|
:long => 'long',
|
38
58
|
:ulong => 'unsigned long',
|
59
|
+
:long_long => 'long long',
|
60
|
+
:ulong_long => 'unsigned long long',
|
39
61
|
:float => 'float',
|
40
62
|
:double => 'double',
|
63
|
+
:long_double => 'long double',
|
41
64
|
:pointer => 'void *',
|
42
|
-
:
|
65
|
+
:int8 => 'int8_t',
|
66
|
+
:uint8 => 'uint8_t',
|
67
|
+
:int16 => 'int16_t',
|
68
|
+
:uint16 => 'uint16_t',
|
69
|
+
:int32 => 'int32_t',
|
70
|
+
:uint32 => 'uint32_t',
|
71
|
+
:int64 => 'int64_t',
|
72
|
+
:uint64 => 'uint64_t',
|
73
|
+
:buffer_in => 'const in void *',
|
74
|
+
:buffer_out => 'out void *',
|
75
|
+
:buffer_inout => 'inout void *',
|
76
|
+
:varargs => '...'
|
43
77
|
}
|
44
|
-
|
78
|
+
|
45
79
|
TypeMap = {}
|
46
80
|
def self.find_type(type)
|
47
|
-
return type if type.is_a?(Type)
|
81
|
+
return type if type.is_a?(Type) or type.is_a?(CallbackInfo)
|
48
82
|
|
49
83
|
t = TypeMap[type]
|
50
84
|
return t unless t.nil?
|
@@ -55,12 +89,19 @@ module FFI
|
|
55
89
|
raise TypeError.new("cannot resolve type #{type}")
|
56
90
|
end
|
57
91
|
|
92
|
+
class Function
|
93
|
+
def initialize(*args)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
58
97
|
class Exporter
|
59
|
-
|
98
|
+
attr_accessor :mod
|
99
|
+
attr_reader :functions, :callbacks, :structs
|
60
100
|
|
61
101
|
def initialize(mod)
|
62
102
|
@mod = mod
|
63
103
|
@functions = []
|
104
|
+
@callbacks = {}
|
64
105
|
@structs = []
|
65
106
|
end
|
66
107
|
|
@@ -71,7 +112,11 @@ module FFI
|
|
71
112
|
def struct(name, fields)
|
72
113
|
@structs << { name: name, fields: fields.dup }
|
73
114
|
end
|
74
|
-
|
115
|
+
|
116
|
+
def callback(name, cb)
|
117
|
+
@callbacks[name] = cb
|
118
|
+
end
|
119
|
+
|
75
120
|
def dump(out_file)
|
76
121
|
File.open(out_file, 'w') do |f|
|
77
122
|
guard = File.basename(out_file).upcase.gsub('.', '_').gsub('/', '_')
|
@@ -88,11 +133,19 @@ module FFI
|
|
88
133
|
#endif
|
89
134
|
|
90
135
|
HEADER
|
91
|
-
|
136
|
+
|
137
|
+
@callbacks.each do |name, cb|
|
138
|
+
f.puts "typedef #{cb.name(name)};"
|
139
|
+
end
|
92
140
|
@structs.each do |s|
|
93
141
|
f.puts "struct #{s[:name].gsub('::', '_')} {"
|
94
142
|
s[:fields].each do |field|
|
95
|
-
|
143
|
+
if field[:type].is_a?(CallbackInfo)
|
144
|
+
type = field[:type].name(field[:name].to_s)
|
145
|
+
else
|
146
|
+
type = "#{field[:type].name} #{field[:name].to_s}"
|
147
|
+
end
|
148
|
+
f.puts "#{' ' * 4}#{type};"
|
96
149
|
end
|
97
150
|
f.puts '};'
|
98
151
|
f.puts
|
@@ -112,17 +165,31 @@ module FFI
|
|
112
165
|
|
113
166
|
module Library
|
114
167
|
def self.extended(mod)
|
115
|
-
FFI.exporter =
|
168
|
+
FFI.exporter.mod = mod
|
116
169
|
end
|
117
170
|
|
118
|
-
def attach_function(
|
119
|
-
|
171
|
+
def attach_function(name, func, args, returns = nil, options = nil)
|
172
|
+
mname, a2, a3, a4, a5 = name, func, args, returns, options
|
173
|
+
cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]
|
174
|
+
arg_types = arg_types.map { |e| find_type(e) }
|
175
|
+
FFI.exporter.attach(mname, cname, find_type(ret_type), arg_types)
|
120
176
|
end
|
121
177
|
|
122
178
|
def ffi_lib(*args)
|
123
179
|
|
124
180
|
end
|
125
181
|
|
182
|
+
def callback(*args)
|
183
|
+
name, params, ret = if args.length == 3
|
184
|
+
args
|
185
|
+
else
|
186
|
+
[ nil, args[0], args[1] ]
|
187
|
+
end
|
188
|
+
native_params = params.map { |e| find_type(e) }
|
189
|
+
cb = FFI::CallbackInfo.new(find_type(ret), native_params)
|
190
|
+
FFI.exporter.callback(name, cb) if name
|
191
|
+
end
|
192
|
+
|
126
193
|
TypeMap = {}
|
127
194
|
def find_type(type)
|
128
195
|
t = TypeMap[type]
|
@@ -138,15 +205,43 @@ module FFI
|
|
138
205
|
|
139
206
|
class Struct
|
140
207
|
def self.layout(*args)
|
208
|
+
return if args.size.zero?
|
141
209
|
fields = []
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
210
|
+
if args.first.kind_of?(Hash)
|
211
|
+
args.first.each do |name, type|
|
212
|
+
fields << { :name => name, :type => find_type(type), :offset => nil }
|
213
|
+
end
|
214
|
+
else
|
215
|
+
i = 0
|
216
|
+
while i < args.size
|
217
|
+
name, type, offset = args[i], args[i+1], nil
|
218
|
+
i += 2
|
219
|
+
if args[i].kind_of?(Integer)
|
220
|
+
offset = args[i]
|
221
|
+
i += 1
|
222
|
+
end
|
223
|
+
fields << { :name => name, :type => find_type(type), :offset => offset }
|
224
|
+
end
|
146
225
|
end
|
147
226
|
FFI.exporter.struct(self.to_s, fields)
|
148
227
|
end
|
149
228
|
|
229
|
+
def initialize
|
230
|
+
@data = {}
|
231
|
+
end
|
232
|
+
|
233
|
+
def [](name)
|
234
|
+
@data[name]
|
235
|
+
end
|
236
|
+
|
237
|
+
def []=(name, value)
|
238
|
+
@data[name] = value
|
239
|
+
end
|
240
|
+
|
241
|
+
def self.callback(params, ret)
|
242
|
+
FFI::CallbackInfo.new(find_type(ret), params.map { |e| find_type(e) })
|
243
|
+
end
|
244
|
+
|
150
245
|
TypeMap = {}
|
151
246
|
def self.find_type(type)
|
152
247
|
t = TypeMap[type]
|
@@ -158,13 +253,30 @@ module FFI
|
|
158
253
|
|
159
254
|
TypeMap[type] = FFI.find_type(type)
|
160
255
|
end
|
161
|
-
|
162
|
-
def self.
|
163
|
-
|
256
|
+
|
257
|
+
def self.in
|
258
|
+
ptr(:in)
|
164
259
|
end
|
165
|
-
|
166
|
-
def self.
|
260
|
+
|
261
|
+
def self.out
|
262
|
+
ptr(:out)
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.ptr(flags = :inout)
|
167
266
|
StructByReference.new(self)
|
168
267
|
end
|
268
|
+
|
269
|
+
def self.val
|
270
|
+
StructByValue.new(self)
|
271
|
+
end
|
272
|
+
|
273
|
+
def self.by_value
|
274
|
+
self.val
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.by_ref(flags = :inout)
|
278
|
+
self.ptr(flags)
|
279
|
+
end
|
280
|
+
|
169
281
|
end
|
170
282
|
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wayne Meissner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: ffi
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.0.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubygems-tasks
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: Ruby FFI library
|
@@ -72,16 +72,15 @@ executables: []
|
|
72
72
|
extensions: []
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
|
-
-
|
75
|
+
- LICENSE
|
76
76
|
- README.md
|
77
77
|
- Rakefile
|
78
|
-
-
|
78
|
+
- ffi-compiler.gemspec
|
79
79
|
- lib/ffi-compiler/compile_task.rb
|
80
80
|
- lib/ffi-compiler/export_task.rb
|
81
81
|
- lib/ffi-compiler/exporter.rb
|
82
82
|
- lib/ffi-compiler/fake_ffi/ffi-compiler/loader.rb
|
83
83
|
- lib/ffi-compiler/fake_ffi/ffi.rb
|
84
|
-
- lib/ffi-compiler/ffi-compiler.iml
|
85
84
|
- lib/ffi-compiler/loader.rb
|
86
85
|
- lib/ffi-compiler/platform.rb
|
87
86
|
- lib/ffi-compiler/task.rb
|
@@ -95,18 +94,19 @@ require_paths:
|
|
95
94
|
- lib
|
96
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
97
96
|
requirements:
|
98
|
-
- -
|
97
|
+
- - ">="
|
99
98
|
- !ruby/object:Gem::Version
|
100
99
|
version: '1.9'
|
101
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
101
|
requirements:
|
103
|
-
- -
|
102
|
+
- - ">="
|
104
103
|
- !ruby/object:Gem::Version
|
105
104
|
version: '0'
|
106
105
|
requirements: []
|
107
106
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
107
|
+
rubygems_version: 2.5.1
|
109
108
|
signing_key:
|
110
109
|
specification_version: 4
|
111
110
|
summary: Ruby FFI Rakefile generator
|
112
111
|
test_files: []
|
112
|
+
has_rdoc: false
|
@@ -1,14 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<module type="RUBY_MODULE" version="4">
|
3
|
-
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
4
|
-
<exclude-output />
|
5
|
-
<content url="file://$MODULE_DIR$">
|
6
|
-
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
7
|
-
</content>
|
8
|
-
<orderEntry type="jdk" jdkName="RVM: ruby-1.9.3-p392" jdkType="RUBY_SDK" />
|
9
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
10
|
-
<orderEntry type="library" scope="PROVIDED" name="ffi (v1.4.0, RVM: ruby-1.9.3-p392) [gem]" level="application" />
|
11
|
-
<orderEntry type="library" scope="PROVIDED" name="rake (v10.0.3, RVM: ruby-1.9.3-p392) [gem]" level="application" />
|
12
|
-
</component>
|
13
|
-
</module>
|
14
|
-
|