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.
@@ -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.2
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-03-05 00:00:00.000000000 Z
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: &18320080 !ruby/object:Gem::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: *18320080
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.16
64
+ rubygems_version: 1.8.23
58
65
  signing_key:
59
66
  specification_version: 3
60
67
  summary: A generator for Ruby FFI bindings