ffi_gen 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -4
- data/lib/ffi_gen.rb +254 -378
- data/lib/ffi_gen/clang.rb +40 -38
- data/lib/ffi_gen/java_output.rb +242 -0
- data/lib/ffi_gen/ruby_output.rb +251 -0
- metadata +12 -5
@@ -0,0 +1,251 @@
|
|
1
|
+
class FFIGen
|
2
|
+
def generate_rb
|
3
|
+
writer = Writer.new " ", "# "
|
4
|
+
writer.puts "# Generated by ffi_gen. Please do not change this file by hand.", "", "require 'ffi'", "", "module #{@module_name}"
|
5
|
+
writer.indent do
|
6
|
+
writer.puts "extend FFI::Library"
|
7
|
+
writer.puts "ffi_lib_flags #{@ffi_lib_flags.map(&:inspect).join(', ')}" if @ffi_lib_flags
|
8
|
+
writer.puts "ffi_lib '#{@ffi_lib}'", ""
|
9
|
+
writer.puts "def self.attach_function(name, *_)", " begin; super; rescue FFI::NotFoundError => e", " (class << self; self; end).class_eval { define_method(name) { |*_| raise e } }", " end", "end", ""
|
10
|
+
declarations.values.compact.uniq.each do |declaration|
|
11
|
+
declaration.write_ruby writer
|
12
|
+
end
|
13
|
+
end
|
14
|
+
writer.puts "end"
|
15
|
+
writer.output
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_ruby_type(full_type)
|
19
|
+
canonical_type = Clang.get_canonical_type full_type
|
20
|
+
data_array = case canonical_type[:kind]
|
21
|
+
when :void then [":void", "nil"]
|
22
|
+
when :bool then [":bool", "Boolean"]
|
23
|
+
when :u_char then [":uchar", "Integer"]
|
24
|
+
when :u_short then [":ushort", "Integer"]
|
25
|
+
when :u_int then [":uint", "Integer"]
|
26
|
+
when :u_long then [":ulong", "Integer"]
|
27
|
+
when :u_long_long then [":ulong_long", "Integer"]
|
28
|
+
when :char_s, :s_char then [":char", "Integer"]
|
29
|
+
when :short then [":short", "Integer"]
|
30
|
+
when :int then [":int", "Integer"]
|
31
|
+
when :long then [":long", "Integer"]
|
32
|
+
when :long_long then [":long_long", "Integer"]
|
33
|
+
when :float then [":float", "Float"]
|
34
|
+
when :double then [":double", "Float"]
|
35
|
+
when :pointer
|
36
|
+
pointee_type = Clang.get_pointee_type canonical_type
|
37
|
+
result = nil
|
38
|
+
case pointee_type[:kind]
|
39
|
+
when :char_s
|
40
|
+
result = [":string", "String"]
|
41
|
+
when :record
|
42
|
+
pointee_declaration = @declarations[Clang.get_cursor_type(Clang.get_type_declaration(pointee_type))]
|
43
|
+
result = [pointee_declaration.ruby_name, pointee_declaration.ruby_name] if pointee_declaration and pointee_declaration.written
|
44
|
+
when :function_proto
|
45
|
+
declaration = @declarations[full_type]
|
46
|
+
result = [":#{declaration.ruby_name}", "Proc(_callback_#{declaration.ruby_name}_)"] if declaration
|
47
|
+
end
|
48
|
+
|
49
|
+
if result.nil?
|
50
|
+
pointer_depth = 0
|
51
|
+
pointer_target_name = ""
|
52
|
+
current_type = full_type
|
53
|
+
loop do
|
54
|
+
declaration = Clang.get_type_declaration current_type
|
55
|
+
pointer_target_name = Name.new self, Clang.get_cursor_spelling(declaration).to_s_and_dispose
|
56
|
+
break if not pointer_target_name.empty?
|
57
|
+
|
58
|
+
case current_type[:kind]
|
59
|
+
when :pointer
|
60
|
+
pointer_depth += 1
|
61
|
+
current_type = Clang.get_pointee_type current_type
|
62
|
+
when :unexposed
|
63
|
+
break
|
64
|
+
else
|
65
|
+
pointer_target_name = Name.new self, Clang.get_type_kind_spelling(current_type[:kind]).to_s_and_dispose
|
66
|
+
break
|
67
|
+
end
|
68
|
+
end
|
69
|
+
result = [":pointer", "FFI::Pointer(#{'*' * pointer_depth}#{pointer_target_name.to_ruby_classname})", pointer_target_name]
|
70
|
+
end
|
71
|
+
|
72
|
+
result
|
73
|
+
when :record
|
74
|
+
declaration = @declarations[canonical_type]
|
75
|
+
declaration ? ["#{declaration.ruby_name}.by_value", declaration.ruby_name] : [":char", "unknown"] # TODO
|
76
|
+
when :enum
|
77
|
+
declaration = @declarations[canonical_type]
|
78
|
+
declaration ? [":#{declaration.ruby_name}", "Symbol from _enum_#{declaration.ruby_name}_", declaration.name] : [":char", "unknown"] # TODO
|
79
|
+
when :constant_array
|
80
|
+
element_type_data = to_ruby_type Clang.get_array_element_type(canonical_type)
|
81
|
+
size = Clang.get_array_size canonical_type
|
82
|
+
["[#{element_type_data[:ffi_type]}, #{size}]", "Array<#{element_type_data[:description]}>"]
|
83
|
+
when :unexposed
|
84
|
+
[":char", "unexposed"]
|
85
|
+
else
|
86
|
+
raise NotImplementedError, "No translation for values of type #{canonical_type[:kind]}"
|
87
|
+
end
|
88
|
+
|
89
|
+
{ ffi_type: data_array[0], description: data_array[1], parameter_name: (data_array[2] || Name.new(self, data_array[1])).to_ruby_downcase }
|
90
|
+
end
|
91
|
+
|
92
|
+
class Name
|
93
|
+
RUBY_KEYWORDS = %w{alias and begin break case class def defined do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield BEGIN END}
|
94
|
+
|
95
|
+
def to_ruby_downcase
|
96
|
+
format :downcase, :underscores, RUBY_KEYWORDS
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_ruby_classname
|
100
|
+
format :camelcase, RUBY_KEYWORDS
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_ruby_constant
|
104
|
+
format :upcase, :underscores, RUBY_KEYWORDS
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Enum
|
109
|
+
def write_ruby(writer)
|
110
|
+
shorten_names
|
111
|
+
|
112
|
+
@constants.each do |constant|
|
113
|
+
constant[:symbol] = ":#{constant[:name].to_ruby_downcase}"
|
114
|
+
end
|
115
|
+
|
116
|
+
writer.comment do
|
117
|
+
writer.write_description @comment
|
118
|
+
writer.puts "", "<em>This entry is only for documentation and no real method. The FFI::Enum can be accessed via #enum_type(:#{ruby_name}).</em>"
|
119
|
+
writer.puts "", "=== Options:"
|
120
|
+
@constants.each do |constant|
|
121
|
+
writer.puts "#{constant[:symbol]} ::"
|
122
|
+
writer.write_description constant[:comment], false, " ", " "
|
123
|
+
end
|
124
|
+
writer.puts "", "@method _enum_#{ruby_name}_", "@return [Symbol]", "@scope class"
|
125
|
+
end
|
126
|
+
|
127
|
+
writer.puts "enum :#{ruby_name}, ["
|
128
|
+
writer.indent do
|
129
|
+
writer.write_array @constants, "," do |constant|
|
130
|
+
"#{constant[:symbol]}, #{constant[:value]}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
writer.puts "]", ""
|
134
|
+
end
|
135
|
+
|
136
|
+
def ruby_name
|
137
|
+
@ruby_name ||= @name.to_ruby_downcase
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class StructOrUnion
|
142
|
+
def write_ruby(writer)
|
143
|
+
@fields.each do |field|
|
144
|
+
field[:symbol] = ":#{field[:name].to_ruby_downcase}"
|
145
|
+
field[:type_data] = @generator.to_ruby_type field[:type]
|
146
|
+
end
|
147
|
+
|
148
|
+
writer.comment do
|
149
|
+
writer.write_description @comment
|
150
|
+
unless @fields.empty?
|
151
|
+
writer.puts "", "= Fields:"
|
152
|
+
@fields.each do |field|
|
153
|
+
writer.puts "#{field[:symbol]} ::"
|
154
|
+
writer.write_description field[:comment], false, " (#{field[:type_data][:description]}) ", " "
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
@fields << { symbol: ":dummy", type_data: { ffi_type: ":char" } } if @fields.empty?
|
160
|
+
|
161
|
+
unless @oo_functions.empty?
|
162
|
+
writer.puts "module #{ruby_name}Wrappers"
|
163
|
+
writer.indent do
|
164
|
+
@oo_functions.each_with_index do |(name, function, return_type_declaration), index|
|
165
|
+
parameter_names = function.parameters[1..-1].map { |parameter| !parameter[:name].empty? ? parameter[:name].to_ruby_downcase : "arg#{function.parameters.index(parameter)}" }
|
166
|
+
writer.puts "" unless index == 0
|
167
|
+
writer.puts "def #{name.to_ruby_downcase}(#{parameter_names.join(', ')})"
|
168
|
+
writer.indent do
|
169
|
+
cast = return_type_declaration ? "#{return_type_declaration.ruby_name}.new " : ""
|
170
|
+
writer.puts "#{cast}#{@generator.module_name}.#{function.ruby_name}(#{(["self"] + parameter_names).join(', ')})"
|
171
|
+
end
|
172
|
+
writer.puts "end"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
writer.puts "end", ""
|
176
|
+
end
|
177
|
+
|
178
|
+
writer.puts "class #{ruby_name} < #{@is_union ? 'FFI::Union' : 'FFI::Struct'}"
|
179
|
+
writer.indent do
|
180
|
+
writer.puts "include #{ruby_name}Wrappers" unless @oo_functions.empty?
|
181
|
+
writer.write_array @fields, ",", "layout ", " " do |field|
|
182
|
+
"#{field[:symbol]}, #{field[:type_data][:ffi_type]}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
writer.puts "end", ""
|
186
|
+
|
187
|
+
@written = true
|
188
|
+
end
|
189
|
+
|
190
|
+
def ruby_name
|
191
|
+
@ruby_name ||= @name.to_ruby_classname
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class FunctionOrCallback
|
196
|
+
def write_ruby(writer)
|
197
|
+
@parameters.each do |parameter|
|
198
|
+
parameter[:type_data] = @generator.to_ruby_type parameter[:type]
|
199
|
+
parameter[:ruby_name] = !parameter[:name].empty? ? parameter[:name].to_ruby_downcase : parameter[:type_data][:parameter_name]
|
200
|
+
parameter[:description] = []
|
201
|
+
end
|
202
|
+
return_type_data = @generator.to_ruby_type @return_type
|
203
|
+
|
204
|
+
function_description = []
|
205
|
+
return_value_description = []
|
206
|
+
current_description = function_description
|
207
|
+
@comment.split("\n").map do |line|
|
208
|
+
line = writer.prepare_comment_line line
|
209
|
+
if line.gsub!(/\\param (.*?) /, '')
|
210
|
+
parameter = @parameters.find { |p| p[:name].raw == $1 }
|
211
|
+
if parameter
|
212
|
+
current_description = parameter[:description]
|
213
|
+
else
|
214
|
+
current_description << "#{$1}: "
|
215
|
+
end
|
216
|
+
end
|
217
|
+
current_description = return_value_description if line.gsub! '\\returns ', ''
|
218
|
+
current_description << line
|
219
|
+
end
|
220
|
+
|
221
|
+
writer.puts "@blocking = true" if @blocking
|
222
|
+
writer.comment do
|
223
|
+
writer.write_description function_description
|
224
|
+
writer.puts "", "<em>This entry is only for documentation and no real method.</em>" if @is_callback
|
225
|
+
writer.puts "", "@method #{@is_callback ? "_callback_#{ruby_name}_" : ruby_name}(#{@parameters.map{ |parameter| parameter[:ruby_name] }.join(', ')})"
|
226
|
+
@parameters.each do |parameter|
|
227
|
+
writer.write_description parameter[:description], false, "@param [#{parameter[:type_data][:description]}] #{parameter[:ruby_name]} ", " "
|
228
|
+
end
|
229
|
+
writer.write_description return_value_description, false, "@return [#{return_type_data[:description]}] ", " "
|
230
|
+
writer.puts "@scope class"
|
231
|
+
end
|
232
|
+
|
233
|
+
ffi_signature = "[#{@parameters.map{ |parameter| parameter[:type_data][:ffi_type] }.join(', ')}], #{return_type_data[:ffi_type]}"
|
234
|
+
if @is_callback
|
235
|
+
writer.puts "callback :#{ruby_name}, #{ffi_signature}", ""
|
236
|
+
else
|
237
|
+
writer.puts "attach_function :#{ruby_name}, :#{@name.raw}, #{ffi_signature}", ""
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def ruby_name
|
242
|
+
@ruby_name ||= @name.to_ruby_downcase
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
class Constant
|
247
|
+
def write_ruby(writer)
|
248
|
+
writer.puts "#{@name.to_ruby_constant} = #{@value}", ""
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi_gen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,12 @@ dependencies:
|
|
21
21
|
version: 1.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
25
30
|
description: A generator for Ruby FFI bindings, directly from header files via LLVM's
|
26
31
|
Clang compiler
|
27
32
|
email: mail@richard-musiol.de
|
@@ -30,6 +35,8 @@ extensions: []
|
|
30
35
|
extra_rdoc_files: []
|
31
36
|
files:
|
32
37
|
- lib/ffi_gen.rb
|
38
|
+
- lib/ffi_gen/java_output.rb
|
39
|
+
- lib/ffi_gen/ruby_output.rb
|
33
40
|
- lib/ffi_gen/clang.rb
|
34
41
|
- LICENSE
|
35
42
|
- README.md
|
@@ -54,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
61
|
version: '0'
|
55
62
|
requirements: []
|
56
63
|
rubyforge_project:
|
57
|
-
rubygems_version: 1.8.
|
64
|
+
rubygems_version: 1.8.23
|
58
65
|
signing_key:
|
59
66
|
specification_version: 3
|
60
67
|
summary: A generator for Ruby FFI bindings
|