ruby-bindgen 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +25 -0
- data/README.md +68 -0
- data/Rakefile +8 -0
- data/bin/ruby-bindgen +133 -0
- data/docs/architecture.md +238 -0
- data/docs/c/c_bindings.md +65 -0
- data/docs/c/constants.md +21 -0
- data/docs/c/customizing.md +19 -0
- data/docs/c/filtering.md +24 -0
- data/docs/c/getting_started.md +56 -0
- data/docs/c/library_loading.md +53 -0
- data/docs/c/output.md +96 -0
- data/docs/c/types.md +61 -0
- data/docs/c/version_guards.md +105 -0
- data/docs/cmake/cmake_bindings.md +19 -0
- data/docs/cmake/filtering.md +26 -0
- data/docs/cmake/getting_started.md +52 -0
- data/docs/cmake/output.md +110 -0
- data/docs/configuration.md +351 -0
- data/docs/contributing.md +68 -0
- data/docs/cpp/buffers.md +24 -0
- data/docs/cpp/classes.md +139 -0
- data/docs/cpp/cpp_bindings.md +29 -0
- data/docs/cpp/customizing.md +124 -0
- data/docs/cpp/enums.md +42 -0
- data/docs/cpp/filtering.md +35 -0
- data/docs/cpp/getting_started.md +80 -0
- data/docs/cpp/iterators.md +94 -0
- data/docs/cpp/operators.md +170 -0
- data/docs/cpp/output.md +125 -0
- data/docs/cpp/templates.md +114 -0
- data/docs/examples.md +133 -0
- data/docs/index.md +133 -0
- data/docs/prior_art.md +37 -0
- data/docs/troubleshooting.md +243 -0
- data/docs/type_spelling.md +200 -0
- data/docs/updating_bindings.md +55 -0
- data/docs/version_guards.md +69 -0
- data/lib/ruby-bindgen/config.rb +63 -0
- data/lib/ruby-bindgen/generators/cmake/cmake.rb +171 -0
- data/lib/ruby-bindgen/generators/cmake/directory.erb +29 -0
- data/lib/ruby-bindgen/generators/cmake/guard.rb +55 -0
- data/lib/ruby-bindgen/generators/cmake/presets.erb +232 -0
- data/lib/ruby-bindgen/generators/cmake/project.erb +89 -0
- data/lib/ruby-bindgen/generators/ffi/callback.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/constant.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_constant_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl_anonymous.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/ffi.rb +687 -0
- data/lib/ruby-bindgen/generators/ffi/field_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/function.erb +5 -0
- data/lib/ruby-bindgen/generators/ffi/library.erb +39 -0
- data/lib/ruby-bindgen/generators/ffi/project.erb +18 -0
- data/lib/ruby-bindgen/generators/ffi/struct.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/translation_unit.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/typedef_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/union.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/variable.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/version.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/version_method.erb +5 -0
- data/lib/ruby-bindgen/generators/generator.rb +52 -0
- data/lib/ruby-bindgen/generators/rice/auto_generated_base_class.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/class.erb +31 -0
- data/lib/ruby-bindgen/generators/rice/class_template.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/class_template_specialization.erb +10 -0
- data/lib/ruby-bindgen/generators/rice/constant.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/constructor.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/conversion_function.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/cxx_iterator_method.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/cxx_method.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/enum_constant_decl.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/enum_decl.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/field_decl.erb +8 -0
- data/lib/ruby-bindgen/generators/rice/function.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/function_pointer.rb +68 -0
- data/lib/ruby-bindgen/generators/rice/incomplete_class.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/iterator_alias.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/iterator_collector.rb +159 -0
- data/lib/ruby-bindgen/generators/rice/namespace.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_binary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_inspect.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_unary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/operator[].erb +4 -0
- data/lib/ruby-bindgen/generators/rice/project.cpp.erb +18 -0
- data/lib/ruby-bindgen/generators/rice/project.hpp.erb +13 -0
- data/lib/ruby-bindgen/generators/rice/reference_qualifier.rb +495 -0
- data/lib/ruby-bindgen/generators/rice/rice.rb +1724 -0
- data/lib/ruby-bindgen/generators/rice/rice_include.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/signature_builder.rb +230 -0
- data/lib/ruby-bindgen/generators/rice/template_resolver.rb +585 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.cpp.erb +40 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.ipp.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/type_index.rb +117 -0
- data/lib/ruby-bindgen/generators/rice/type_speller.rb +509 -0
- data/lib/ruby-bindgen/generators/rice/union.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/variable.erb +5 -0
- data/lib/ruby-bindgen/inputter.rb +54 -0
- data/lib/ruby-bindgen/name_mapper.rb +65 -0
- data/lib/ruby-bindgen/namer.rb +138 -0
- data/lib/ruby-bindgen/outputter.rb +40 -0
- data/lib/ruby-bindgen/parser.rb +82 -0
- data/lib/ruby-bindgen/refinements/cursor.rb +57 -0
- data/lib/ruby-bindgen/refinements/string.rb +41 -0
- data/lib/ruby-bindgen/symbol_candidates.rb +282 -0
- data/lib/ruby-bindgen/symbol_entry.rb +21 -0
- data/lib/ruby-bindgen/symbols.rb +107 -0
- data/lib/ruby-bindgen/type_pointer_formatter.rb +35 -0
- data/lib/ruby-bindgen/version.rb +3 -0
- data/lib/ruby-bindgen.rb +19 -0
- data/ruby-bindgen.gemspec +52 -0
- metadata +260 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module RubyBindgen
|
|
4
|
+
# Generates the ordered list of name strings a cursor could plausibly be
|
|
5
|
+
# referenced as in a YAML symbols entry. Pure name enumeration — owns no
|
|
6
|
+
# tables and makes no policy decisions. Consumers (Symbols, Namer) feed
|
|
7
|
+
# the result through their own lookup tables.
|
|
8
|
+
#
|
|
9
|
+
# The list spans:
|
|
10
|
+
# * libclang's qualified_name (with anonymous scopes stripped)
|
|
11
|
+
# * parent.type.spelling-based qualified name (collapses inline namespaces)
|
|
12
|
+
# * display_name forms with template arguments
|
|
13
|
+
# * a "specialized" display rebuilt from libclang template-arg APIs (so
|
|
14
|
+
# Outer::takeValue<7>() matches even when display_name is takeValue<>())
|
|
15
|
+
# * parameter-list forms (clang preferred / fully-qualified / canonical)
|
|
16
|
+
# for callable cursors, against every qualified-name form above
|
|
17
|
+
# * cursor.spelling as the bare-name fallback (last so it does not
|
|
18
|
+
# shadow a more-specific user rule)
|
|
19
|
+
class SymbolCandidates
|
|
20
|
+
include Enumerable
|
|
21
|
+
|
|
22
|
+
# Canonicalize whitespace in a signature so user-supplied spellings match
|
|
23
|
+
# libclang's. Used by table consumers to keep keys and lookups aligned.
|
|
24
|
+
def self.normalize_signature(str)
|
|
25
|
+
str
|
|
26
|
+
.gsub(/\s+/, ' ')
|
|
27
|
+
.gsub(/\s*\*/, '*')
|
|
28
|
+
.gsub(/\s*&/, '&')
|
|
29
|
+
.strip
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def initialize(cursor)
|
|
33
|
+
@cursor = cursor
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Yield each candidate string in priority order (most-specific first).
|
|
37
|
+
# Consumers stop on the first match, so qualified user rules win over
|
|
38
|
+
# bare-name fallbacks like the built-in operator mappings.
|
|
39
|
+
def each
|
|
40
|
+
return enum_for(:each) unless block_given?
|
|
41
|
+
|
|
42
|
+
seen = Set.new
|
|
43
|
+
qualified_names = qualified_name_forms
|
|
44
|
+
|
|
45
|
+
qualified_names.each do |name|
|
|
46
|
+
yield name if seen.add?(name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Template display candidates: display_name carries template args, e.g.
|
|
50
|
+
# "DataType<hfloat>" or "saturate_cast<hfloat>(uchar)". Also rebuild a
|
|
51
|
+
# specialized display from libclang template-arg APIs to recover names
|
|
52
|
+
# that display_name leaves abbreviated, e.g. takeValue<>() -> takeValue<7>().
|
|
53
|
+
display = @cursor.display_name
|
|
54
|
+
if display != @cursor.spelling
|
|
55
|
+
qualified_names.each do |qn|
|
|
56
|
+
yield display if seen.add?(display)
|
|
57
|
+
qualified_display = sub_last(qn, @cursor.spelling, display)
|
|
58
|
+
yield qualified_display if seen.add?(qualified_display)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
specialized_display = specialized_template_display_name(display)
|
|
62
|
+
if specialized_display && specialized_display != display
|
|
63
|
+
qualified_names.each do |qn|
|
|
64
|
+
yield specialized_display if seen.add?(specialized_display)
|
|
65
|
+
fq_qualified_display = sub_last(qn, @cursor.spelling, specialized_display)
|
|
66
|
+
yield fq_qualified_display if seen.add?(fq_qualified_display)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Parameter-list forms for callable cursors. Each list is built three
|
|
72
|
+
# ways because libclang spells the same parameter type differently in
|
|
73
|
+
# different contexts:
|
|
74
|
+
# - arg_type.spelling: clang's preferred form, often the typedef
|
|
75
|
+
# - fully_qualified_name: fully namespace-qualified
|
|
76
|
+
# - canonical.spelling: post-typedef canonical type
|
|
77
|
+
# Users may write any of these forms in their YAML; emit them all.
|
|
78
|
+
if @cursor.type.is_a?(FFI::Clang::Types::Function)
|
|
79
|
+
parameter_lists = []
|
|
80
|
+
parameter_lists << (0...@cursor.type.args_size).map { |i| @cursor.type.arg_type(i).spelling }.join(", ")
|
|
81
|
+
parameter_lists << (0...@cursor.type.args_size).map { |i| @cursor.type.arg_type(i).fully_qualified_name(@cursor.printing_policy) }.join(", ")
|
|
82
|
+
parameter_lists << (0...@cursor.type.args_size).map { |i| @cursor.type.arg_type(i).canonical.spelling }.join(", ")
|
|
83
|
+
|
|
84
|
+
parameter_lists.uniq.each do |param_types|
|
|
85
|
+
bare = "#{@cursor.spelling}(#{param_types})"
|
|
86
|
+
yield bare if seen.add?(bare)
|
|
87
|
+
qualified_names.each do |qn|
|
|
88
|
+
candidate = "#{qn}(#{param_types})"
|
|
89
|
+
yield candidate if seen.add?(candidate)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
yield @cursor.spelling if seen.add?(@cursor.spelling)
|
|
95
|
+
|
|
96
|
+
$stderr.puts "Candidates for #{@cursor.spelling}: #{seen.to_a.inspect}" if ENV['BINDGEN_DEBUG_SYMBOLS']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# Enumerate qualified-name forms a cursor can be referred to by.
|
|
102
|
+
#
|
|
103
|
+
# Returns at minimum the libclang qualified_name (with anonymous scope
|
|
104
|
+
# segments dropped so enum constants match Outer::Value rather than
|
|
105
|
+
# Outer::(unnamed enum at ...)::Value).
|
|
106
|
+
#
|
|
107
|
+
# For class members, also includes a qualified name built from the
|
|
108
|
+
# parent's type spelling. Type spellings collapse inline namespaces, so
|
|
109
|
+
# cv::dnn::dnn4_v20241223::Layer::init also gets a cv::dnn::Layer::init
|
|
110
|
+
# candidate that matches YAML entries omitting the inline namespace.
|
|
111
|
+
def qualified_name_forms
|
|
112
|
+
forms = [normalized_qualified_name].compact.uniq
|
|
113
|
+
|
|
114
|
+
parent = @cursor.semantic_parent
|
|
115
|
+
if parent && [:cursor_class_decl, :cursor_struct, :cursor_class_template].include?(parent.kind)
|
|
116
|
+
type_qualified = "#{parent.type.spelling}::#{@cursor.spelling}"
|
|
117
|
+
forms << type_qualified unless forms.include?(type_qualified)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
forms
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Use libclang's semantic qualified name directly, but drop anonymous
|
|
124
|
+
# scope segments so enum constants still match entries like Outer::Value
|
|
125
|
+
# instead of Outer::(unnamed enum at ... )::Value.
|
|
126
|
+
def normalized_qualified_name
|
|
127
|
+
qualified_name = @cursor.qualified_name
|
|
128
|
+
return @cursor.spelling if qualified_name.nil? || qualified_name.empty?
|
|
129
|
+
|
|
130
|
+
qualified_name
|
|
131
|
+
.split('::')
|
|
132
|
+
.reject { |segment| segment.start_with?('(') }
|
|
133
|
+
.join('::')
|
|
134
|
+
rescue ArgumentError
|
|
135
|
+
@cursor.spelling
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Rebuild a template specialization display name using cursor template
|
|
139
|
+
# arguments. Type args are fully qualified semantically, while written
|
|
140
|
+
# display text is preserved for integral or other non-type args that
|
|
141
|
+
# libclang does not expose as names.
|
|
142
|
+
#
|
|
143
|
+
# Examples:
|
|
144
|
+
# `Holder<Tag, 7>` becomes `Holder<Outer::Tag, 7>`
|
|
145
|
+
# `takeValue<>()` becomes `takeValue<7>()`
|
|
146
|
+
def specialized_template_display_name(display)
|
|
147
|
+
args = template_argument_display_values(display)
|
|
148
|
+
return nil if args.nil? || args.empty? || args.any?(&:nil?)
|
|
149
|
+
|
|
150
|
+
sub_template_args(display, "<#{args.join(', ')}>")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def template_argument_display_values(display)
|
|
154
|
+
n = @cursor.num_template_arguments
|
|
155
|
+
return nil unless n > 0
|
|
156
|
+
|
|
157
|
+
written_args = template_argument_texts(display)
|
|
158
|
+
(0...n).map do |index|
|
|
159
|
+
case @cursor.template_argument_kind(index)
|
|
160
|
+
when :template_argument_type
|
|
161
|
+
@cursor.template_argument_type(index).fully_qualified_name(@cursor.printing_policy)
|
|
162
|
+
when :template_argument_integral
|
|
163
|
+
written_args[index] || @cursor.template_argument_value(index).to_s
|
|
164
|
+
else
|
|
165
|
+
written_args[index]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def template_argument_texts(display)
|
|
171
|
+
start = display.index('<')
|
|
172
|
+
return [] unless start
|
|
173
|
+
|
|
174
|
+
depth = 0
|
|
175
|
+
args_start = start + 1
|
|
176
|
+
args_end = nil
|
|
177
|
+
|
|
178
|
+
(start...display.length).each do |index|
|
|
179
|
+
depth += 1 if display[index] == '<'
|
|
180
|
+
depth -= 1 if display[index] == '>'
|
|
181
|
+
if depth.zero?
|
|
182
|
+
args_end = index
|
|
183
|
+
break
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
return [] unless args_end
|
|
187
|
+
|
|
188
|
+
args_text = display[args_start...args_end]
|
|
189
|
+
return [] if args_text.nil? || args_text.empty?
|
|
190
|
+
|
|
191
|
+
split_template_arguments(args_text)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Split a template-argument list, respecting nested <>, (), [], {} and
|
|
195
|
+
# quoted literals. The naive `split(',')` would mangle nested types like
|
|
196
|
+
# `std::pair<int, std::vector<int, std::allocator<int>>>` and string
|
|
197
|
+
# literals containing commas.
|
|
198
|
+
def split_template_arguments(args_text)
|
|
199
|
+
result = []
|
|
200
|
+
current = String.new
|
|
201
|
+
angle_depth = 0
|
|
202
|
+
paren_depth = 0
|
|
203
|
+
bracket_depth = 0
|
|
204
|
+
brace_depth = 0
|
|
205
|
+
quote = nil
|
|
206
|
+
escaped = false
|
|
207
|
+
|
|
208
|
+
args_text.each_char do |char|
|
|
209
|
+
current << char
|
|
210
|
+
|
|
211
|
+
if quote
|
|
212
|
+
if escaped
|
|
213
|
+
escaped = false
|
|
214
|
+
elsif char == '\\'
|
|
215
|
+
escaped = true
|
|
216
|
+
elsif char == quote
|
|
217
|
+
quote = nil
|
|
218
|
+
end
|
|
219
|
+
next
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
case char
|
|
223
|
+
when '"', "'"
|
|
224
|
+
quote = char
|
|
225
|
+
when '<'
|
|
226
|
+
angle_depth += 1
|
|
227
|
+
when '>'
|
|
228
|
+
angle_depth -= 1 if angle_depth > 0
|
|
229
|
+
when '('
|
|
230
|
+
paren_depth += 1
|
|
231
|
+
when ')'
|
|
232
|
+
paren_depth -= 1 if paren_depth > 0
|
|
233
|
+
when '['
|
|
234
|
+
bracket_depth += 1
|
|
235
|
+
when ']'
|
|
236
|
+
bracket_depth -= 1 if bracket_depth > 0
|
|
237
|
+
when '{'
|
|
238
|
+
brace_depth += 1
|
|
239
|
+
when '}'
|
|
240
|
+
brace_depth -= 1 if brace_depth > 0
|
|
241
|
+
when ','
|
|
242
|
+
if angle_depth.zero? && paren_depth.zero? && bracket_depth.zero? && brace_depth.zero?
|
|
243
|
+
current.chop!
|
|
244
|
+
piece = current.strip
|
|
245
|
+
result << piece unless piece.empty?
|
|
246
|
+
current = String.new
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
piece = current.strip
|
|
252
|
+
result << piece unless piece.empty?
|
|
253
|
+
result
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Replace the first balanced <...> group in str with replacement.
|
|
257
|
+
# Handles nested angle brackets like DataType<Vec<float, 3>>.
|
|
258
|
+
def sub_template_args(str, replacement)
|
|
259
|
+
start = str.index('<')
|
|
260
|
+
return str unless start
|
|
261
|
+
|
|
262
|
+
depth = 0
|
|
263
|
+
(start...str.length).each do |i|
|
|
264
|
+
depth += 1 if str[i] == '<'
|
|
265
|
+
depth -= 1 if str[i] == '>'
|
|
266
|
+
if depth == 0
|
|
267
|
+
return str[0...start] + replacement + str[(i + 1)..]
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
str
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Replace the last occurrence of `target` in `str` with `replacement`.
|
|
274
|
+
# Used when building qualified candidates for constructors, where the
|
|
275
|
+
# spelling (e.g. "DataType") appears both as namespace and method name.
|
|
276
|
+
def sub_last(str, target, replacement)
|
|
277
|
+
i = str.rindex(target)
|
|
278
|
+
return str unless i
|
|
279
|
+
str[0...i] + replacement + str[(i + target.length)..]
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
class SymbolEntry
|
|
3
|
+
attr_reader :version, :signature
|
|
4
|
+
|
|
5
|
+
def initialize(skip: false, version: nil, signature: nil)
|
|
6
|
+
@skip = skip
|
|
7
|
+
@version = version
|
|
8
|
+
@signature = signature
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def skip?
|
|
12
|
+
@skip
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def merge(skip: false, version: nil, signature: nil)
|
|
16
|
+
@skip = true if skip
|
|
17
|
+
@version = version if version
|
|
18
|
+
@signature = signature if signature
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
# Skip / version-guard / FFI-override decisions for cursors, looked up by
|
|
3
|
+
# name strings supplied in the YAML symbols config.
|
|
4
|
+
#
|
|
5
|
+
# Owns the storage (an exact-match hash plus a list of regex entries) and
|
|
6
|
+
# the policy queries (skip?, version, override). Delegates name
|
|
7
|
+
# enumeration to SymbolCandidates so the matching logic is shared with
|
|
8
|
+
# Namer / NameMapper.
|
|
9
|
+
class Symbols
|
|
10
|
+
def initialize(config = {})
|
|
11
|
+
@exact = {}
|
|
12
|
+
@regex = []
|
|
13
|
+
|
|
14
|
+
(config[:skip] || []).each do |name|
|
|
15
|
+
add_entry(name, skip: true)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
(config[:versions] || {}).each do |version, names|
|
|
19
|
+
names.each do |name|
|
|
20
|
+
add_entry(name, version: version)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
(config[:overrides] || {}).each do |name, signature|
|
|
25
|
+
add_entry(name.to_s, signature: signature)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Look up a cursor by trying each of its candidate names.
|
|
30
|
+
# Returns a SymbolEntry or nil.
|
|
31
|
+
def lookup_cursor(cursor)
|
|
32
|
+
symbol_candidates = SymbolCandidates.new(cursor)
|
|
33
|
+
lookup(symbol_candidates)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Look up a list of pre-built candidate names.
|
|
37
|
+
# Returns a SymbolEntry or nil.
|
|
38
|
+
def lookup(candidates)
|
|
39
|
+
candidates.each do |name|
|
|
40
|
+
result = @exact[SymbolCandidates.normalize_signature(name)]
|
|
41
|
+
return result if result
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@regex.each do |pattern, entry|
|
|
45
|
+
candidates.each do |name|
|
|
46
|
+
return entry if pattern.match?(SymbolCandidates.normalize_signature(name))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Check if a cursor should be skipped based on symbols config.
|
|
53
|
+
def skip?(cursor)
|
|
54
|
+
entry = lookup_cursor(cursor)
|
|
55
|
+
entry&.skip? || false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check if a type spelling matches any skip symbol using word boundaries.
|
|
59
|
+
# Used as a fallback for dependent/unexposed types where no declaration
|
|
60
|
+
# is available.
|
|
61
|
+
def skip_spelling?(spelling)
|
|
62
|
+
@exact.each do |key, entry|
|
|
63
|
+
next unless entry.skip?
|
|
64
|
+
simple_name = key.split('::').last
|
|
65
|
+
return true if spelling.match?(/\b#{Regexp.escape(simple_name)}\b/)
|
|
66
|
+
end
|
|
67
|
+
@regex.each do |pattern, entry|
|
|
68
|
+
next unless entry.skip?
|
|
69
|
+
return true if pattern.match?(spelling)
|
|
70
|
+
end
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns the version guard value for a cursor, or nil if not version-guarded.
|
|
75
|
+
def version(cursor)
|
|
76
|
+
entry = lookup_cursor(cursor)
|
|
77
|
+
entry&.version
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns the override signature string for a cursor, or nil if not overridden.
|
|
81
|
+
def override(cursor)
|
|
82
|
+
entry = lookup_cursor(cursor)
|
|
83
|
+
entry&.signature
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def has_versions?
|
|
87
|
+
@exact.any? { |_, entry| entry.version } || @regex.any? { |_, entry| entry.version }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def add_entry(name, skip: false, version: nil, signature: nil)
|
|
93
|
+
return if name.nil?
|
|
94
|
+
if name.start_with?('/') && name.end_with?('/') && name.length > 2
|
|
95
|
+
@regex << [Regexp.new(name[1..-2]), SymbolEntry.new(skip: skip, version: version, signature: signature)]
|
|
96
|
+
else
|
|
97
|
+
key = SymbolCandidates.normalize_signature(name)
|
|
98
|
+
existing = @exact[key]
|
|
99
|
+
if existing
|
|
100
|
+
existing.merge(skip: skip, version: version, signature: signature)
|
|
101
|
+
else
|
|
102
|
+
@exact[key] = SymbolEntry.new(skip: skip, version: version, signature: signature)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
# Shared pointer-type formatting for both the fully-qualified-name shim and
|
|
3
|
+
# the Rice type speller. The caller supplies how non-pointer child types
|
|
4
|
+
# should be spelled.
|
|
5
|
+
module TypePointerFormatter
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def pointer_spelling(type)
|
|
9
|
+
pointee = type.pointee
|
|
10
|
+
|
|
11
|
+
if function_pointer_pointee?(pointee)
|
|
12
|
+
ptr_const = type.const_qualified? ? " const" : ""
|
|
13
|
+
result_type = yield(pointee.result_type)
|
|
14
|
+
arg_types = pointee.arg_types.map { |arg_type| yield(arg_type) }.join(", ")
|
|
15
|
+
return "#{result_type} (*#{ptr_const})(#{arg_types})"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
parts = []
|
|
19
|
+
current = type
|
|
20
|
+
while current.kind == :type_pointer
|
|
21
|
+
inner = current.pointee
|
|
22
|
+
break if function_pointer_pointee?(inner)
|
|
23
|
+
|
|
24
|
+
parts << (current.const_qualified? ? "*const" : "*")
|
|
25
|
+
current = inner
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
"#{yield(current)} #{parts.reverse.join}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def function_pointer_pointee?(type)
|
|
32
|
+
type.kind == :type_function_proto || type.kind == :type_function_no_proto
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/ruby-bindgen.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'ruby-bindgen/config'
|
|
2
|
+
require 'ruby-bindgen/refinements/string'
|
|
3
|
+
|
|
4
|
+
require 'ruby-bindgen/inputter'
|
|
5
|
+
require 'ruby-bindgen/outputter'
|
|
6
|
+
|
|
7
|
+
require 'ruby-bindgen/parser'
|
|
8
|
+
require 'ruby-bindgen/name_mapper'
|
|
9
|
+
require 'ruby-bindgen/namer'
|
|
10
|
+
require 'ruby-bindgen/symbol_entry'
|
|
11
|
+
require 'ruby-bindgen/symbol_candidates'
|
|
12
|
+
require 'ruby-bindgen/symbols'
|
|
13
|
+
|
|
14
|
+
require 'ruby-bindgen/generators/generator'
|
|
15
|
+
require 'ruby-bindgen/generators/cmake/cmake'
|
|
16
|
+
require 'ruby-bindgen/generators/ffi/ffi'
|
|
17
|
+
require 'ruby-bindgen/generators/rice/rice'
|
|
18
|
+
|
|
19
|
+
require 'ruby-bindgen/version'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative "lib/ruby-bindgen/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "ruby-bindgen"
|
|
7
|
+
spec.version = RubyBindgen::VERSION
|
|
8
|
+
spec.homepage = "https://github.com/ruby-rice/ruby-bindgen/"
|
|
9
|
+
spec.summary = "C and C++ binding generator for Ruby"
|
|
10
|
+
spec.description = <<~DESC
|
|
11
|
+
ruby-bindgen reads C and C++ headers with libclang and emits Ruby bindings.
|
|
12
|
+
It supports three output formats: Rice C++ source for high-fidelity C++ wrappers,
|
|
13
|
+
raw FFI for plain C libraries, and CMake build files to compile the generated
|
|
14
|
+
extensions. Bindings are driven from a YAML configuration that controls header
|
|
15
|
+
matching, symbol filtering, name mapping, and version guards. Battle-tested
|
|
16
|
+
against OpenCV (thousands of classes) and PROJ.
|
|
17
|
+
DESC
|
|
18
|
+
spec.license = 'BSD-2-Clause'
|
|
19
|
+
|
|
20
|
+
spec.metadata = {
|
|
21
|
+
"bug_tracker_uri" => "https://github.com/ruby-rice/ruby-bindgen/issues",
|
|
22
|
+
"changelog_uri" => "https://github.com/ruby-rice/ruby-bindgen/blob/main/CHANGELOG.md",
|
|
23
|
+
"documentation_uri" => "https://ruby-rice.github.io/ruby-bindgen/",
|
|
24
|
+
"source_code_uri" => "https://github.com/ruby-rice/ruby-bindgen",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
spec.author = "Charlie Savage"
|
|
28
|
+
spec.platform = Gem::Platform::RUBY
|
|
29
|
+
spec.require_path = "lib"
|
|
30
|
+
spec.bindir = "bin"
|
|
31
|
+
spec.executables = ["ruby-bindgen"]
|
|
32
|
+
spec.files = Dir['CHANGELOG.md',
|
|
33
|
+
'LICENSE',
|
|
34
|
+
'Rakefile',
|
|
35
|
+
'README.md',
|
|
36
|
+
'ruby-bindgen.gemspec',
|
|
37
|
+
'bin/ruby-bindgen',
|
|
38
|
+
'docs/**/*',
|
|
39
|
+
'lib/**/*',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
spec.required_ruby_version = '>= 3.2.0'
|
|
43
|
+
|
|
44
|
+
spec.add_dependency 'ffi', '>= 1.16'
|
|
45
|
+
spec.add_dependency 'ffi-clang', '>= 0.16.0'
|
|
46
|
+
|
|
47
|
+
spec.add_development_dependency 'minitest'
|
|
48
|
+
spec.add_development_dependency 'minitest-reporters'
|
|
49
|
+
spec.add_development_dependency 'rake'
|
|
50
|
+
spec.add_development_dependency 'simplecov'
|
|
51
|
+
spec.add_development_dependency 'simplecov-cobertura'
|
|
52
|
+
end
|