ffi_gen 0.8 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +77 -0
- data/lib/ffi_gen.rb +195 -108
- data/lib/ffi_gen/clang.rb +351 -349
- metadata +6 -4
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Richard Musiol
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
ffi_gen - A Generator for Ruby FFI bindings
|
2
|
+
===========================================
|
3
|
+
|
4
|
+
*Author:* Richard Musiol
|
5
|
+
*Contributors:* Jeremy Voorhis (thanks for the initial idea)
|
6
|
+
*License:* MIT (see LICENSE)
|
7
|
+
|
8
|
+
|
9
|
+
Features
|
10
|
+
--------
|
11
|
+
* Generation of FFI methods, structures, enums and callbacks
|
12
|
+
* Generation of YARD documentation comments
|
13
|
+
* Tested with headers of the following libraries:
|
14
|
+
* Clang
|
15
|
+
* LLVM
|
16
|
+
* OpenGL
|
17
|
+
|
18
|
+
|
19
|
+
Requirements
|
20
|
+
------------
|
21
|
+
|
22
|
+
* Ruby 1.9
|
23
|
+
* Clang 3.0 ([Download](http://llvm.org/releases/download.html#3.0), use the binaries or configure with ``--enable-shared``)
|
24
|
+
|
25
|
+
*These requirements are only for running the generator. The generated files are Ruby 1.8 compatible and do not need Clang.*
|
26
|
+
|
27
|
+
|
28
|
+
Example
|
29
|
+
-------
|
30
|
+
Use the following interface in a script or Rake task:
|
31
|
+
|
32
|
+
require "ffi_gen"
|
33
|
+
|
34
|
+
FFIGen.generate(
|
35
|
+
ruby_module: "Clang",
|
36
|
+
ffi_lib: "clang",
|
37
|
+
headers: ["clang-c/Index.h"],
|
38
|
+
cflags: `llvm-config --cflags`.split(" "),
|
39
|
+
prefixes: ["clang_", "CX"],
|
40
|
+
blacklist: ["clang_getExpansionLocation"],
|
41
|
+
output: "clang.rb"
|
42
|
+
)
|
43
|
+
|
44
|
+
Output: [clang.rb](https://github.com/neelance/ffi_gen/blob/master/lib/ffi_gen/clang.rb)
|
45
|
+
|
46
|
+
|
47
|
+
Hints
|
48
|
+
-----
|
49
|
+
|
50
|
+
You may need to set additional include directories:
|
51
|
+
|
52
|
+
export CPATH=/usr/lib/gcc/x86_64-linux-gnu/4.6.1/include
|
53
|
+
|
54
|
+
Your GCC include paths can be seen with:
|
55
|
+
|
56
|
+
`gcc -print-prog-name=cc1` -v
|
57
|
+
|
58
|
+
|
59
|
+
Projects using ffi_gen
|
60
|
+
----------------------
|
61
|
+
|
62
|
+
* https://github.com/jvoorhis/ruby-llvm
|
63
|
+
|
64
|
+
|
65
|
+
Roadmap
|
66
|
+
-------
|
67
|
+
|
68
|
+
* Support for more libraries:
|
69
|
+
* Cairo
|
70
|
+
* (Write me if you have a whish)
|
71
|
+
* Automatic generation of object oriented wrappers
|
72
|
+
* Polish YARD documentation comments some more
|
73
|
+
|
74
|
+
|
75
|
+
Feedback
|
76
|
+
--------
|
77
|
+
Please use GitHub's issue tracker for problems or suggestions. Pull requests are welcome, too.
|
data/lib/ffi_gen.rb
CHANGED
@@ -24,6 +24,8 @@ class Clang::String
|
|
24
24
|
end
|
25
25
|
|
26
26
|
class FFIGen
|
27
|
+
RUBY_KEYWORDS = %w{alias allocate and begin break case class def defined do else elsif end ensure false for if in initialize module next nil not or redo rescue retry return self super then true undef unless until when while yield}
|
28
|
+
|
27
29
|
class Enum
|
28
30
|
attr_reader :constants
|
29
31
|
|
@@ -34,55 +36,63 @@ class FFIGen
|
|
34
36
|
@constants = []
|
35
37
|
end
|
36
38
|
|
37
|
-
def
|
39
|
+
def write(writer)
|
38
40
|
prefix_length = 0
|
39
41
|
suffix_length = 0
|
40
42
|
|
41
43
|
unless @constants.size < 2
|
42
|
-
search_pattern = @constants.all? { |constant| constant[
|
43
|
-
first_name = @constants.first[
|
44
|
+
search_pattern = @constants.all? { |constant| constant[:name].include? "_" } ? /(?<=_)/ : /[A-Z]/
|
45
|
+
first_name = @constants.first[:name]
|
44
46
|
|
45
47
|
loop do
|
46
48
|
position = first_name.index(search_pattern, prefix_length + 1) or break
|
47
49
|
prefix = first_name[0...position]
|
48
|
-
break if not @constants.all? { |constant| constant[
|
50
|
+
break if not @constants.all? { |constant| constant[:name].start_with? prefix }
|
49
51
|
prefix_length = position
|
50
52
|
end
|
51
53
|
|
52
54
|
loop do
|
53
55
|
position = first_name.rindex(search_pattern, first_name.size - suffix_length - 1) or break
|
54
56
|
prefix = first_name[position..-1]
|
55
|
-
break if not @constants.all? { |constant| constant[
|
57
|
+
break if not @constants.all? { |constant| constant[:name].end_with? prefix }
|
56
58
|
suffix_length = first_name.size - position
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
symbol_descriptions = []
|
63
|
-
@constants.map do |(constant_name, constant_value, constant_comment)|
|
64
|
-
symbol = ":#{@generator.to_ruby_lowercase constant_name[prefix_length..(-1 - suffix_length)]}"
|
65
|
-
symbols << symbol
|
66
|
-
definitions << " #{symbol}#{constant_value ? ", #{constant_value}" : ""}"
|
67
|
-
symbol_descriptions << " # #{symbol} ::\n # #{@generator.create_description_comment(constant_comment, ' # ', true)}\n"
|
62
|
+
@constants.each do |constant|
|
63
|
+
constant[:symbol] = ":#{@generator.to_ruby_lowercase constant[:name][prefix_length..(-1 - suffix_length)]}"
|
68
64
|
end
|
69
65
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
66
|
+
writer.comment do
|
67
|
+
writer.write_description @comment
|
68
|
+
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>"
|
69
|
+
writer.puts "", "=== Options:"
|
70
|
+
@constants.each do |constant|
|
71
|
+
writer.puts "#{constant[:symbol]} ::"
|
72
|
+
writer.write_description constant[:comment], false, " ", " "
|
73
|
+
end
|
74
|
+
writer.puts "", "@method _enum_#{ruby_name}_", "@return [Symbol]", "@scope class"
|
75
|
+
end
|
76
|
+
|
77
|
+
writer.puts "enum :#{ruby_name}, ["
|
78
|
+
writer.indent do
|
79
|
+
writer.write_array @constants, "," do |constant|
|
80
|
+
"#{constant[:symbol]}#{constant[:value] ? ", #{constant[:value]}" : ''}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
writer.puts "]", ""
|
84
|
+
end
|
85
|
+
|
86
|
+
def ruby_name
|
87
|
+
@ruby_name ||= @generator.to_ruby_lowercase @name
|
78
88
|
end
|
79
89
|
|
80
90
|
def type_name(short)
|
81
|
-
short ? @name : "Symbol from #{
|
91
|
+
short ? @name : "Symbol from _enum_#{ruby_name}_"
|
82
92
|
end
|
83
93
|
|
84
94
|
def reference
|
85
|
-
":#{
|
95
|
+
":#{ruby_name}"
|
86
96
|
end
|
87
97
|
end
|
88
98
|
|
@@ -96,31 +106,41 @@ class FFIGen
|
|
96
106
|
@fields = []
|
97
107
|
end
|
98
108
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
@fields.each do |(field_name, field_type, field_comment)|
|
103
|
-
symbol = ":#{@generator.to_ruby_lowercase field_name}"
|
104
|
-
field_definitions << "#{symbol}, #{@generator.to_ffi_type field_type}"
|
105
|
-
field_descriptions << " # #{symbol} ::\n # (#{@generator.to_type_name field_type}) #{@generator.create_description_comment(field_comment, ' # ', true)}\n"
|
109
|
+
def write(writer)
|
110
|
+
@fields.each do |field|
|
111
|
+
field[:symbol] = ":#{@generator.to_ruby_lowercase field[:name]}"
|
106
112
|
end
|
107
113
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
writer.comment do
|
115
|
+
writer.write_description @comment
|
116
|
+
unless @fields.empty?
|
117
|
+
writer.puts "", "= Fields:"
|
118
|
+
@fields.each do |field|
|
119
|
+
writer.puts "#{field[:symbol]} ::"
|
120
|
+
writer.write_description field[:comment], false, " (#{@generator.to_type_name field[:type]}) ", " "
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
writer.puts "class #{ruby_name} < FFI::Struct"
|
126
|
+
writer.indent do
|
127
|
+
writer.write_array @fields, ",", "layout ", " " do |field|
|
128
|
+
"#{field[:symbol]}, #{@generator.to_ffi_type field[:type]}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
writer.puts "end", ""
|
132
|
+
end
|
133
|
+
|
134
|
+
def ruby_name
|
135
|
+
@ruby_name ||= @generator.to_ruby_camelcase @name
|
116
136
|
end
|
117
137
|
|
118
138
|
def type_name(short)
|
119
|
-
|
139
|
+
ruby_name
|
120
140
|
end
|
121
141
|
|
122
142
|
def reference
|
123
|
-
"#{
|
143
|
+
"#{ruby_name}.by_value"
|
124
144
|
end
|
125
145
|
end
|
126
146
|
|
@@ -136,25 +156,22 @@ class FFIGen
|
|
136
156
|
@comment = comment
|
137
157
|
end
|
138
158
|
|
139
|
-
def
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
[ruby_param_name, ruby_param_type, []]
|
159
|
+
def write(writer)
|
160
|
+
@parameters.each do |parameter|
|
161
|
+
parameter[:ruby_type] = @generator.to_type_name parameter[:type]
|
162
|
+
parameter[:ruby_name] = @generator.to_ruby_lowercase(parameter[:name].empty? ? @generator.to_type_name(parameter[:type], true) : parameter[:name])
|
163
|
+
parameter[:description] = []
|
145
164
|
end
|
146
165
|
|
147
|
-
ffi_signature = "[#{@parameters.map{ |(name, type)| @generator.to_ffi_type type }.join(', ')}], #{@generator.to_ffi_type @return_type}"
|
148
|
-
|
149
166
|
function_description = []
|
150
167
|
return_value_description = []
|
151
168
|
current_description = function_description
|
152
169
|
@comment.split("\n").map do |line|
|
153
|
-
line =
|
170
|
+
line = writer.prepare_comment_line line
|
154
171
|
if line.gsub! /\\param (.*?) /, ''
|
155
|
-
|
156
|
-
if
|
157
|
-
current_description =
|
172
|
+
parameter = @parameters.find { |parameter| parameter[:name] == $1 }
|
173
|
+
if parameter
|
174
|
+
current_description = parameter[:description]
|
158
175
|
else
|
159
176
|
current_description << "#{$1}: "
|
160
177
|
end
|
@@ -163,33 +180,102 @@ class FFIGen
|
|
163
180
|
current_description << line
|
164
181
|
end
|
165
182
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
str << " # @param [#{type}] #{name} #{@generator.create_description_comment(description, ' # ', true)}\n"
|
183
|
+
writer.comment do
|
184
|
+
writer.write_description function_description
|
185
|
+
writer.puts "", "<em>This entry is only for documentation and no real method.</em>" if @is_callback
|
186
|
+
writer.puts "", "@method #{@is_callback ? "_callback_#{ruby_name}_" : ruby_name}(#{@parameters.map{ |parameter| parameter[:ruby_name] }.join(', ')})"
|
187
|
+
@parameters.each do |parameter|
|
188
|
+
writer.write_description parameter[:description], false, "@param [#{parameter[:ruby_type]}] #{parameter[:ruby_name]} ", " "
|
189
|
+
end
|
190
|
+
writer.write_description return_value_description, false, "@return [#{@generator.to_type_name @return_type}] ", " "
|
191
|
+
writer.puts "@scope class"
|
176
192
|
end
|
177
|
-
|
178
|
-
|
193
|
+
|
194
|
+
ffi_signature = "[#{@parameters.map{ |parameter| @generator.to_ffi_type parameter[:type] }.join(', ')}], #{@generator.to_ffi_type @return_type}"
|
179
195
|
if @is_callback
|
180
|
-
|
196
|
+
writer.puts "callback :#{ruby_name}, #{ffi_signature}", ""
|
181
197
|
else
|
182
|
-
|
198
|
+
writer.puts "attach_function :#{ruby_name}, :#{@name}, #{ffi_signature}", ""
|
183
199
|
end
|
184
|
-
|
200
|
+
end
|
201
|
+
|
202
|
+
def ruby_name
|
203
|
+
@ruby_name ||= @generator.to_ruby_lowercase @name, true
|
185
204
|
end
|
186
205
|
|
187
206
|
def type_name(short)
|
188
|
-
"Proc(#{
|
207
|
+
"Proc(_callback_#{ruby_name}_)"
|
189
208
|
end
|
190
209
|
|
191
210
|
def reference
|
192
|
-
":#{
|
211
|
+
":#{ruby_name}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
class Constant
|
216
|
+
def initialize(generator, name, value)
|
217
|
+
@generator = generator
|
218
|
+
@name = name
|
219
|
+
@value = value
|
220
|
+
end
|
221
|
+
|
222
|
+
def write(writer)
|
223
|
+
writer.puts "#{@generator.to_ruby_lowercase(@name, true).upcase} = #{@value}", ""
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class Writer
|
228
|
+
attr_reader :output
|
229
|
+
|
230
|
+
def initialize
|
231
|
+
@indentation = ""
|
232
|
+
@output = ""
|
233
|
+
end
|
234
|
+
|
235
|
+
def indent(prefix = " ")
|
236
|
+
previous_indentation = @indentation
|
237
|
+
@indentation += prefix
|
238
|
+
yield
|
239
|
+
@indentation = previous_indentation
|
240
|
+
end
|
241
|
+
|
242
|
+
def comment(&block)
|
243
|
+
indent "# ", &block
|
244
|
+
end
|
245
|
+
|
246
|
+
def puts(*lines)
|
247
|
+
lines.each do |line|
|
248
|
+
@output << "#{@indentation}#{line}\n"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def write_array(array, separator = "", first_line_prefix = "", other_lines_prefix = "")
|
253
|
+
array.each_with_index do |entry, index|
|
254
|
+
entry = yield entry if block_given?
|
255
|
+
puts "#{index == 0 ? first_line_prefix : other_lines_prefix}#{entry}#{index < array.size - 1 ? separator : ''}"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def prepare_comment_line(line)
|
260
|
+
line = line.dup
|
261
|
+
line.sub! /\ ?\*+\/\s*$/, ''
|
262
|
+
line.sub! /^\s*\/?\*+ ?/, ''
|
263
|
+
line.gsub! /\\(brief|determine) /, ''
|
264
|
+
line.gsub! '[', '('
|
265
|
+
line.gsub! ']', ')'
|
266
|
+
line
|
267
|
+
end
|
268
|
+
|
269
|
+
def write_description(description, not_documented_message = true, first_line_prefix = "", other_lines_prefix = "")
|
270
|
+
if description.is_a? String
|
271
|
+
description = description.split("\n").map { |line| prepare_comment_line(line) }
|
272
|
+
end
|
273
|
+
|
274
|
+
description.shift while not description.empty? and description.first.strip.empty?
|
275
|
+
description.pop while not description.empty? and description.last.strip.empty?
|
276
|
+
description << (not_documented_message ? "(Not documented)" : "") if description.empty?
|
277
|
+
|
278
|
+
write_array description, "", first_line_prefix, other_lines_prefix
|
193
279
|
end
|
194
280
|
end
|
195
281
|
|
@@ -204,6 +290,9 @@ class FFIGen
|
|
204
290
|
@blacklist = options.fetch :blacklist, []
|
205
291
|
@output = options.fetch :output, $stdout
|
206
292
|
|
293
|
+
blacklist = @blacklist
|
294
|
+
@blacklist = lambda { |name| blacklist.include? name } if @blacklist.is_a? Array
|
295
|
+
|
207
296
|
@translation_unit = nil
|
208
297
|
@declarations = nil
|
209
298
|
end
|
@@ -221,7 +310,7 @@ class FFIGen
|
|
221
310
|
args_ptr.write_array_of_pointer pointers
|
222
311
|
|
223
312
|
index = Clang.create_index 0, 0
|
224
|
-
@translation_unit = Clang.parse_translation_unit index, File.join(File.dirname(__FILE__), "ffi_gen/empty.h"), args_ptr, args.size, nil, 0,
|
313
|
+
@translation_unit = Clang.parse_translation_unit index, File.join(File.dirname(__FILE__), "ffi_gen/empty.h"), args_ptr, args.size, nil, 0, Clang.enum_type(:translation_unit_flags)[:detailed_preprocessing_record]
|
225
314
|
|
226
315
|
Clang.get_num_diagnostics(@translation_unit).times do |i|
|
227
316
|
diag = Clang.get_diagnostic @translation_unit, i
|
@@ -257,7 +346,7 @@ class FFIGen
|
|
257
346
|
next if not header_files.include? file
|
258
347
|
|
259
348
|
name = Clang.get_cursor_spelling(declaration).to_s_and_dispose
|
260
|
-
next if blacklist
|
349
|
+
next if @blacklist[name]
|
261
350
|
|
262
351
|
comment = extract_comment translation_unit, comment_range
|
263
352
|
|
@@ -274,7 +363,7 @@ class FFIGen
|
|
274
363
|
next if function_child[:kind] != :parm_decl
|
275
364
|
param_name = Clang.get_cursor_spelling(function_child).to_s_and_dispose
|
276
365
|
param_type = Clang.get_cursor_type function_child
|
277
|
-
function.parameters <<
|
366
|
+
function.parameters << { name: param_name, type: param_type}
|
278
367
|
end
|
279
368
|
|
280
369
|
when :typedef_decl
|
@@ -290,10 +379,25 @@ class FFIGen
|
|
290
379
|
typedef_children[1..-1].each do |param_decl|
|
291
380
|
param_name = Clang.get_cursor_spelling(param_decl).to_s_and_dispose
|
292
381
|
param_type = Clang.get_cursor_type param_decl
|
293
|
-
callback.parameters <<
|
382
|
+
callback.parameters << { name: param_name, type: param_type }
|
294
383
|
end
|
295
384
|
end
|
385
|
+
|
386
|
+
when :macro_definition
|
387
|
+
tokens_ptr_ptr = FFI::MemoryPointer.new :pointer
|
388
|
+
num_tokens_ptr = FFI::MemoryPointer.new :uint
|
389
|
+
Clang.tokenize translation_unit, extent, tokens_ptr_ptr, num_tokens_ptr
|
390
|
+
num_tokens = num_tokens_ptr.read_uint
|
391
|
+
tokens_ptr = FFI::Pointer.new Clang::Token, tokens_ptr_ptr.read_pointer
|
296
392
|
|
393
|
+
if num_tokens == 3
|
394
|
+
token = Clang::Token.new tokens_ptr[1]
|
395
|
+
if Clang.get_token_kind(token) == :literal
|
396
|
+
value = Clang.get_token_spelling(translation_unit, token).to_s_and_dispose
|
397
|
+
@declarations[name] = Constant.new self, name, value
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
297
401
|
end
|
298
402
|
end
|
299
403
|
|
@@ -301,12 +405,20 @@ class FFIGen
|
|
301
405
|
end
|
302
406
|
|
303
407
|
def generate
|
304
|
-
|
408
|
+
writer = Writer.new
|
409
|
+
writer.puts "# Generated by ffi_gen. Please do not change this file by hand.", "", "require 'ffi'", "", "module #{@ruby_module}"
|
410
|
+
writer.indent do
|
411
|
+
writer.puts "extend FFI::Library", "ffi_lib '#{@ffi_lib}'", ""
|
412
|
+
declarations.each do |name, declaration|
|
413
|
+
declaration.write writer
|
414
|
+
end
|
415
|
+
end
|
416
|
+
writer.puts "end"
|
305
417
|
if @output.is_a? String
|
306
|
-
File.open(@output, "w") { |file| file.write
|
418
|
+
File.open(@output, "w") { |file| file.write writer.output }
|
307
419
|
puts "ffi_gen: #{@output}"
|
308
420
|
else
|
309
|
-
@output.write
|
421
|
+
@output.write writer.output
|
310
422
|
end
|
311
423
|
end
|
312
424
|
|
@@ -340,7 +452,7 @@ class FFIGen
|
|
340
452
|
constant_comment = extract_comment translation_unit, constant_comment_range
|
341
453
|
previous_constant_location = constant_location
|
342
454
|
|
343
|
-
enum.constants <<
|
455
|
+
enum.constants << { name: constant_name, value: constant_value, comment: constant_comment }
|
344
456
|
end
|
345
457
|
|
346
458
|
when :struct_decl
|
@@ -357,7 +469,7 @@ class FFIGen
|
|
357
469
|
field_comment = extract_comment translation_unit, field_comment_range
|
358
470
|
previous_field_location = field_location
|
359
471
|
|
360
|
-
struct.fields <<
|
472
|
+
struct.fields << { name: field_name, type: field_type, comment: field_comment }
|
361
473
|
end
|
362
474
|
end
|
363
475
|
end
|
@@ -389,6 +501,7 @@ class FFIGen
|
|
389
501
|
when :u_int then ":uint"
|
390
502
|
when :u_long then ":ulong"
|
391
503
|
when :u_long_long then ":ulong_long"
|
504
|
+
when :char_s, :s_char then ":char"
|
392
505
|
when :short then ":short"
|
393
506
|
when :int then ":int"
|
394
507
|
when :long then ":long"
|
@@ -416,7 +529,7 @@ class FFIGen
|
|
416
529
|
case canonical_type[:kind]
|
417
530
|
when :void then "nil"
|
418
531
|
when :bool then "Boolean"
|
419
|
-
when :u_char, :u_short, :u_int, :u_long, :u_long_long, :short, :int, :long, :long_long then "Integer"
|
532
|
+
when :u_char, :u_short, :u_int, :u_long, :u_long_long, :char_s, :s_char, :short, :int, :long, :long_long then "Integer"
|
420
533
|
when :float, :double then "Float"
|
421
534
|
when :pointer
|
422
535
|
pointee_type = Clang.get_pointee_type canonical_type
|
@@ -452,7 +565,7 @@ class FFIGen
|
|
452
565
|
end
|
453
566
|
end
|
454
567
|
|
455
|
-
def to_ruby_lowercase(str)
|
568
|
+
def to_ruby_lowercase(str, avoid_keywords = false)
|
456
569
|
str = str.dup
|
457
570
|
str.sub! /^(#{@prefixes.join('|')})/, '' # remove prefixes
|
458
571
|
str.gsub! /([A-Z][a-z])/, '_\1' # add underscores before word beginnings
|
@@ -460,6 +573,8 @@ class FFIGen
|
|
460
573
|
str.sub! /^_*/, '' # remove underscores at the beginning
|
461
574
|
str.gsub! /__+/, '_' # replace multiple underscores by only one
|
462
575
|
str.downcase!
|
576
|
+
str.sub! /^\d/, '_\1' # fix illegal beginnings
|
577
|
+
str = "_#{str}" if avoid_keywords and RUBY_KEYWORDS.include? str
|
463
578
|
str
|
464
579
|
end
|
465
580
|
|
@@ -469,34 +584,6 @@ class FFIGen
|
|
469
584
|
str
|
470
585
|
end
|
471
586
|
|
472
|
-
def prepare_comment_line(line)
|
473
|
-
line = line.dup
|
474
|
-
line.sub! /\ ?\*+\/\s*$/, ''
|
475
|
-
line.sub! /^\s*\/?\*+ ?/, ''
|
476
|
-
line.gsub! /\\(brief|determine) /, ''
|
477
|
-
line.gsub! '[', '('
|
478
|
-
line.gsub! ']', ')'
|
479
|
-
line
|
480
|
-
end
|
481
|
-
|
482
|
-
def create_description_comment(description, line_prefix, inline_mode = false)
|
483
|
-
if description.is_a? String
|
484
|
-
description = description.split("\n").map { |line| prepare_comment_line(line) }
|
485
|
-
end
|
486
|
-
|
487
|
-
description.shift while not description.empty? and description.first.strip.empty?
|
488
|
-
description.pop while not description.empty? and description.last.strip.empty?
|
489
|
-
description << "(Not documented)" if not inline_mode and description.empty?
|
490
|
-
|
491
|
-
str = ""
|
492
|
-
description.each_with_index do |line, index|
|
493
|
-
str << line_prefix if not inline_mode or index > 0
|
494
|
-
str << line
|
495
|
-
str << "\n" if not inline_mode or index < description.size - 1
|
496
|
-
end
|
497
|
-
str
|
498
|
-
end
|
499
|
-
|
500
587
|
def self.generate(options = {})
|
501
588
|
self.new(options).generate
|
502
589
|
end
|