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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE +25 -0
  4. data/README.md +68 -0
  5. data/Rakefile +8 -0
  6. data/bin/ruby-bindgen +133 -0
  7. data/docs/architecture.md +238 -0
  8. data/docs/c/c_bindings.md +65 -0
  9. data/docs/c/constants.md +21 -0
  10. data/docs/c/customizing.md +19 -0
  11. data/docs/c/filtering.md +24 -0
  12. data/docs/c/getting_started.md +56 -0
  13. data/docs/c/library_loading.md +53 -0
  14. data/docs/c/output.md +96 -0
  15. data/docs/c/types.md +61 -0
  16. data/docs/c/version_guards.md +105 -0
  17. data/docs/cmake/cmake_bindings.md +19 -0
  18. data/docs/cmake/filtering.md +26 -0
  19. data/docs/cmake/getting_started.md +52 -0
  20. data/docs/cmake/output.md +110 -0
  21. data/docs/configuration.md +351 -0
  22. data/docs/contributing.md +68 -0
  23. data/docs/cpp/buffers.md +24 -0
  24. data/docs/cpp/classes.md +139 -0
  25. data/docs/cpp/cpp_bindings.md +29 -0
  26. data/docs/cpp/customizing.md +124 -0
  27. data/docs/cpp/enums.md +42 -0
  28. data/docs/cpp/filtering.md +35 -0
  29. data/docs/cpp/getting_started.md +80 -0
  30. data/docs/cpp/iterators.md +94 -0
  31. data/docs/cpp/operators.md +170 -0
  32. data/docs/cpp/output.md +125 -0
  33. data/docs/cpp/templates.md +114 -0
  34. data/docs/examples.md +133 -0
  35. data/docs/index.md +133 -0
  36. data/docs/prior_art.md +37 -0
  37. data/docs/troubleshooting.md +243 -0
  38. data/docs/type_spelling.md +200 -0
  39. data/docs/updating_bindings.md +55 -0
  40. data/docs/version_guards.md +69 -0
  41. data/lib/ruby-bindgen/config.rb +63 -0
  42. data/lib/ruby-bindgen/generators/cmake/cmake.rb +171 -0
  43. data/lib/ruby-bindgen/generators/cmake/directory.erb +29 -0
  44. data/lib/ruby-bindgen/generators/cmake/guard.rb +55 -0
  45. data/lib/ruby-bindgen/generators/cmake/presets.erb +232 -0
  46. data/lib/ruby-bindgen/generators/cmake/project.erb +89 -0
  47. data/lib/ruby-bindgen/generators/ffi/callback.erb +1 -0
  48. data/lib/ruby-bindgen/generators/ffi/constant.erb +1 -0
  49. data/lib/ruby-bindgen/generators/ffi/enum_constant_decl.erb +1 -0
  50. data/lib/ruby-bindgen/generators/ffi/enum_decl.erb +4 -0
  51. data/lib/ruby-bindgen/generators/ffi/enum_decl_anonymous.erb +4 -0
  52. data/lib/ruby-bindgen/generators/ffi/ffi.rb +687 -0
  53. data/lib/ruby-bindgen/generators/ffi/field_decl.erb +1 -0
  54. data/lib/ruby-bindgen/generators/ffi/function.erb +5 -0
  55. data/lib/ruby-bindgen/generators/ffi/library.erb +39 -0
  56. data/lib/ruby-bindgen/generators/ffi/project.erb +18 -0
  57. data/lib/ruby-bindgen/generators/ffi/struct.erb +6 -0
  58. data/lib/ruby-bindgen/generators/ffi/translation_unit.erb +9 -0
  59. data/lib/ruby-bindgen/generators/ffi/typedef_decl.erb +1 -0
  60. data/lib/ruby-bindgen/generators/ffi/union.erb +6 -0
  61. data/lib/ruby-bindgen/generators/ffi/variable.erb +1 -0
  62. data/lib/ruby-bindgen/generators/ffi/version.erb +9 -0
  63. data/lib/ruby-bindgen/generators/ffi/version_method.erb +5 -0
  64. data/lib/ruby-bindgen/generators/generator.rb +52 -0
  65. data/lib/ruby-bindgen/generators/rice/auto_generated_base_class.erb +5 -0
  66. data/lib/ruby-bindgen/generators/rice/class.erb +31 -0
  67. data/lib/ruby-bindgen/generators/rice/class_template.erb +9 -0
  68. data/lib/ruby-bindgen/generators/rice/class_template_specialization.erb +10 -0
  69. data/lib/ruby-bindgen/generators/rice/constant.erb +9 -0
  70. data/lib/ruby-bindgen/generators/rice/constructor.erb +1 -0
  71. data/lib/ruby-bindgen/generators/rice/conversion_function.erb +4 -0
  72. data/lib/ruby-bindgen/generators/rice/cxx_iterator_method.erb +1 -0
  73. data/lib/ruby-bindgen/generators/rice/cxx_method.erb +6 -0
  74. data/lib/ruby-bindgen/generators/rice/enum_constant_decl.erb +7 -0
  75. data/lib/ruby-bindgen/generators/rice/enum_decl.erb +6 -0
  76. data/lib/ruby-bindgen/generators/rice/field_decl.erb +8 -0
  77. data/lib/ruby-bindgen/generators/rice/function.erb +6 -0
  78. data/lib/ruby-bindgen/generators/rice/function_pointer.rb +68 -0
  79. data/lib/ruby-bindgen/generators/rice/incomplete_class.erb +3 -0
  80. data/lib/ruby-bindgen/generators/rice/iterator_alias.erb +1 -0
  81. data/lib/ruby-bindgen/generators/rice/iterator_collector.rb +159 -0
  82. data/lib/ruby-bindgen/generators/rice/namespace.erb +5 -0
  83. data/lib/ruby-bindgen/generators/rice/non_member_operator_binary.erb +4 -0
  84. data/lib/ruby-bindgen/generators/rice/non_member_operator_inspect.erb +6 -0
  85. data/lib/ruby-bindgen/generators/rice/non_member_operator_unary.erb +4 -0
  86. data/lib/ruby-bindgen/generators/rice/operator[].erb +4 -0
  87. data/lib/ruby-bindgen/generators/rice/project.cpp.erb +18 -0
  88. data/lib/ruby-bindgen/generators/rice/project.hpp.erb +13 -0
  89. data/lib/ruby-bindgen/generators/rice/reference_qualifier.rb +495 -0
  90. data/lib/ruby-bindgen/generators/rice/rice.rb +1724 -0
  91. data/lib/ruby-bindgen/generators/rice/rice_include.hpp.erb +7 -0
  92. data/lib/ruby-bindgen/generators/rice/signature_builder.rb +230 -0
  93. data/lib/ruby-bindgen/generators/rice/template_resolver.rb +585 -0
  94. data/lib/ruby-bindgen/generators/rice/translation_unit.cpp.erb +40 -0
  95. data/lib/ruby-bindgen/generators/rice/translation_unit.hpp.erb +7 -0
  96. data/lib/ruby-bindgen/generators/rice/translation_unit.ipp.erb +3 -0
  97. data/lib/ruby-bindgen/generators/rice/type_index.rb +117 -0
  98. data/lib/ruby-bindgen/generators/rice/type_speller.rb +509 -0
  99. data/lib/ruby-bindgen/generators/rice/union.erb +5 -0
  100. data/lib/ruby-bindgen/generators/rice/variable.erb +5 -0
  101. data/lib/ruby-bindgen/inputter.rb +54 -0
  102. data/lib/ruby-bindgen/name_mapper.rb +65 -0
  103. data/lib/ruby-bindgen/namer.rb +138 -0
  104. data/lib/ruby-bindgen/outputter.rb +40 -0
  105. data/lib/ruby-bindgen/parser.rb +82 -0
  106. data/lib/ruby-bindgen/refinements/cursor.rb +57 -0
  107. data/lib/ruby-bindgen/refinements/string.rb +41 -0
  108. data/lib/ruby-bindgen/symbol_candidates.rb +282 -0
  109. data/lib/ruby-bindgen/symbol_entry.rb +21 -0
  110. data/lib/ruby-bindgen/symbols.rb +107 -0
  111. data/lib/ruby-bindgen/type_pointer_formatter.rb +35 -0
  112. data/lib/ruby-bindgen/version.rb +3 -0
  113. data/lib/ruby-bindgen.rb +19 -0
  114. data/ruby-bindgen.gemspec +52 -0
  115. 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
@@ -0,0 +1,3 @@
1
+ module RubyBindgen
2
+ VERSION = "1.0.0"
3
+ end
@@ -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