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,53 @@
|
|
|
1
|
+
# Library Loading
|
|
2
|
+
|
|
3
|
+
FFI loads the target shared library at runtime. The `library_names`, `library_versions`, and `library_search_path` configuration options specify where to find the library.
|
|
4
|
+
|
|
5
|
+
## Library Names
|
|
6
|
+
|
|
7
|
+
`library_names` specifies the base names of the shared library. The loader turns each base name into one or more search names and passes them to `ffi_lib`:
|
|
8
|
+
|
|
9
|
+
| Platform | `library_names: ["proj"]` searches for |
|
|
10
|
+
|----------|-----------------------------------------------------|
|
|
11
|
+
| Linux | `libproj`, `libproj.so.{version}` |
|
|
12
|
+
| macOS | `libproj`, `libproj.{version}` |
|
|
13
|
+
| MinGW | `libproj`, `libproj-{version}` |
|
|
14
|
+
| MSVC | `libproj`, `proj_{version}` |
|
|
15
|
+
|
|
16
|
+
The unversioned `libproj` entry is always included as a fallback.
|
|
17
|
+
|
|
18
|
+
The platform's loader appends the conventional shared-library suffix at
|
|
19
|
+
load time (`.so` on Linux, `.dylib` on macOS, `.dll` on Windows), so a
|
|
20
|
+
macOS entry like `libproj.25` resolves to `libproj.25.dylib`.
|
|
21
|
+
|
|
22
|
+
## Library Versions
|
|
23
|
+
|
|
24
|
+
C shared libraries use version suffixes that vary by platform and change across releases. `library_versions` lets you list known version suffixes so FFI can find whichever version is installed.
|
|
25
|
+
|
|
26
|
+
For example, the [PROJ](https://proj.org/) coordinate transformation library has used these version suffixes across releases:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
library_names:
|
|
30
|
+
- proj
|
|
31
|
+
library_versions:
|
|
32
|
+
- "25" # PROJ 9.2
|
|
33
|
+
- "22" # PROJ 8.x
|
|
34
|
+
- "19" # PROJ 7.x
|
|
35
|
+
- "17" # PROJ 6.1, 6.2
|
|
36
|
+
- "15" # PROJ 6.0
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This generates search names like `libproj.so.25`, `libproj.so.22`, etc. on Linux, `libproj.25` on macOS, `libproj-25` on MinGW, and `proj_25` on MSVC. The loader sorts the version suffixes in descending order before emitting them, so newer versions are tried first.
|
|
40
|
+
|
|
41
|
+
If `library_versions` is omitted, only the unversioned name is searched. This works on most systems where the package manager creates an unversioned symlink (e.g., `libproj.so` → `libproj.so.25`).
|
|
42
|
+
|
|
43
|
+
## Library Search Path
|
|
44
|
+
|
|
45
|
+
If the library is installed outside standard operating system search paths, use `library_search_path`:
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
library_names:
|
|
49
|
+
- proj
|
|
50
|
+
library_search_path: PROJ_LIB_PATH
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This generates loader code that checks `ENV["PROJ_LIB_PATH"]` at runtime and prepends that directory to every generated search name before falling back to the default search list.
|
data/docs/c/output.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# FFI Output
|
|
2
|
+
|
|
3
|
+
`ruby-bindgen` generates a project file, an optional version file and one FFI binding file per header. The project file first loads the target library and then binding files.
|
|
4
|
+
|
|
5
|
+
``` mermaid
|
|
6
|
+
flowchart LR
|
|
7
|
+
subgraph Input
|
|
8
|
+
H1["mylib.h"]
|
|
9
|
+
H2["mylib_extra.h"]
|
|
10
|
+
CF["ffi-bindings.yaml"]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
H1 & H2 & CF --> RB["ruby-bindgen"]
|
|
14
|
+
|
|
15
|
+
subgraph "FFI Output"
|
|
16
|
+
P["mylib_ffi.rb"]
|
|
17
|
+
V["mylib_version.rb"]
|
|
18
|
+
C1["mylib.rb"]
|
|
19
|
+
C2["mylib_extra.rb"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
RB --> P & V & C1 & C2
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Given a project named `mylib` that matches `mylib.h` and `mylib_extra.h`:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
output/
|
|
29
|
+
mylib_ffi.rb # project file: require 'ffi', library preamble, require_relatives
|
|
30
|
+
mylib_version.rb # version file: stub for runtime version detection (only with version_check)
|
|
31
|
+
mylib.rb # content from mylib.h
|
|
32
|
+
mylib_extra.rb # content from mylib_extra.h (reopens module)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Project file (`mylib_ffi.rb`)
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# This file was generated by ruby-bindgen. Please do not edit by hand.
|
|
39
|
+
require 'ffi'
|
|
40
|
+
|
|
41
|
+
module Mylib
|
|
42
|
+
extend FFI::Library
|
|
43
|
+
|
|
44
|
+
def self.library_names
|
|
45
|
+
["mylib"]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.search_names
|
|
49
|
+
# Cross-platform library name generation
|
|
50
|
+
# Handles .so, .dylib, .dll variants
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
ffi_lib self.search_names
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
require_relative './mylib'
|
|
57
|
+
require_relative './mylib_extra'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Version file (`mylib_version.rb`)
|
|
61
|
+
|
|
62
|
+
When [`version_check`](../configuration.md#versions) is configured, a version file is generated with a stub method that you must implement to return the runtime library version. This file is preserved on subsequent runs so your implementation is not overwritten. See [Version Guards](version_guards.md) for details.
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
module Mylib
|
|
66
|
+
def self.mylib_version
|
|
67
|
+
# Return the runtime library version as an integer.
|
|
68
|
+
# Example: 90602 for version 9.6.2
|
|
69
|
+
raise NotImplementedError, "Implement mylib_version to return the runtime library version number"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Content file (`mylib.rb`)
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
module Mylib
|
|
78
|
+
# Structs
|
|
79
|
+
class MyStruct < FFI::Struct
|
|
80
|
+
layout :field1, :int,
|
|
81
|
+
:field2, :double
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Enums
|
|
85
|
+
MY_ENUM = enum(
|
|
86
|
+
:value_one, 0,
|
|
87
|
+
:value_two, 1
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Callbacks
|
|
91
|
+
callback :my_callback, [:int, :pointer], :void
|
|
92
|
+
|
|
93
|
+
# Functions
|
|
94
|
+
attach_function :my_function, :my_function, [:int, :string], :int
|
|
95
|
+
end
|
|
96
|
+
```
|
data/docs/c/types.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Type Mapping
|
|
2
|
+
|
|
3
|
+
## String and Pointer Types
|
|
4
|
+
|
|
5
|
+
C uses `char *` for both strings and raw memory buffers. `ruby-bindgen` uses const-qualification and context to pick an FFI type as shown in the following table:
|
|
6
|
+
|
|
7
|
+
| Context | `const char *` | `char *` |
|
|
8
|
+
|---------|---------------|----------|
|
|
9
|
+
| Function parameters | `:string` | `:pointer` |
|
|
10
|
+
| Function returns | `:string` | `:pointer` |
|
|
11
|
+
| Callback returns | `:pointer` | `:pointer` |
|
|
12
|
+
| Struct/union fields | `:string` | `:pointer` |
|
|
13
|
+
|
|
14
|
+
Since `const char *` is a read-only string, FFI can safely auto-convert it to a Ruby `String`. On the other hand, `char *` often indicates a caller-allocated buffer (e.g., `char *buf, size_t buf_size`), so it is safer to map it to `:pointer`. This allows callers to create the buffer with `FFI::MemoryPointer.new`. Callback returns always use `:pointer` regardless of const because FFI cannot manage the lifetime of callback-returned strings.
|
|
15
|
+
|
|
16
|
+
If a specific function needs a different type mapping, use [`symbols: overrides:`](../configuration.md#overrides-ffi-only) to replace the generated signature.
|
|
17
|
+
|
|
18
|
+
## Struct Pointer Types
|
|
19
|
+
|
|
20
|
+
When a function parameter is a pointer to a struct, `ruby-bindgen` generates `StructName.by_ref`. This is correct for the common case of passing a single struct by pointer:
|
|
21
|
+
|
|
22
|
+
```c
|
|
23
|
+
int proj_get_area_of_use(PJ *obj, double *west, ...);
|
|
24
|
+
// → attach_function :proj_get_area_of_use, ..., [:pointer, :pointer, ...], :int
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
However, `.by_ref` is **wrong** when the pointer is actually an array of structs. `ruby-bindgen` cannot distinguish these cases from the C signatures — both are `SomeStruct *`.
|
|
28
|
+
|
|
29
|
+
Below are two common patterns to help decide.
|
|
30
|
+
|
|
31
|
+
### Array parameters
|
|
32
|
+
Look for a count parameter that either precedes or follows the struct pointer:
|
|
33
|
+
|
|
34
|
+
```c
|
|
35
|
+
PJ *proj_create_conversion(PJ_CONTEXT *ctx, ..., int param_count,
|
|
36
|
+
const PJ_PARAM_DESCRIPTION *params);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Here `params` points to an array of `param_count` structs. The caller allocates the array with `FFI::MemoryPointer` and writes structs into it.
|
|
40
|
+
|
|
41
|
+
### Array returns
|
|
42
|
+
A function returns a pointer to a statically-allocated or heap-allocated array of structs:
|
|
43
|
+
|
|
44
|
+
```c
|
|
45
|
+
const PJ_OPERATIONS *proj_list_operations(void);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This returns a NULL-terminated array of `PJ_OPERATIONS` structs, not a single struct. The caller iterates the array by advancing the pointer.
|
|
49
|
+
|
|
50
|
+
Use [`symbols: overrides:`](../configuration.md#overrides-ffi-only) to fix these:
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
symbols:
|
|
54
|
+
overrides:
|
|
55
|
+
proj_create_conversion: "[:pointer, :string, :string, :string, :string, :string, :string, :int, :pointer], :pointer"
|
|
56
|
+
proj_list_operations: "[], :pointer"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Union Pointer Types
|
|
60
|
+
|
|
61
|
+
The same considerations apply to unions. When a function takes a pointer to a union, `ruby-bindgen` generates `UnionName.by_ref`. As with structs, this is wrong when the pointer is actually an array of unions — use [`symbols: overrides:`](../configuration.md#overrides-ffi-only) to fix these cases.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Version Detection
|
|
2
|
+
|
|
3
|
+
When [`symbols.versions`](../configuration.md#versions) has entries, `ruby-bindgen` generates version-guarded Ruby conditionals and a `{project}_version.rb` skeleton file. You must implement the version detection method in that file — typically by calling the library's own version API.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
This example is from [proj4rb](https://github.com/cfis/proj4rb), Ruby bindings for the [PROJ](https://proj.org/) coordinate transformation library. PROJ's API has grown significantly across versions — `proj_normalize_for_visualization` was added in 6.1.0, `proj_cleanup` in 6.2.0, and so forth.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
format: FFI
|
|
11
|
+
project: proj
|
|
12
|
+
module: Proj::Api
|
|
13
|
+
version_check: proj_version
|
|
14
|
+
|
|
15
|
+
library_names:
|
|
16
|
+
- proj
|
|
17
|
+
|
|
18
|
+
symbols:
|
|
19
|
+
skip:
|
|
20
|
+
- PJ_INFO # manually defined in version file
|
|
21
|
+
- proj_info # manually defined in version file
|
|
22
|
+
|
|
23
|
+
versions:
|
|
24
|
+
# 6.1.0
|
|
25
|
+
60100:
|
|
26
|
+
- proj_normalize_for_visualization
|
|
27
|
+
|
|
28
|
+
# 6.2.0
|
|
29
|
+
60200:
|
|
30
|
+
- proj_cleanup
|
|
31
|
+
- proj_as_projjson
|
|
32
|
+
- proj_create_crs_to_crs_from_pj
|
|
33
|
+
|
|
34
|
+
# 8.0.0
|
|
35
|
+
80000:
|
|
36
|
+
- proj_context_errno_string
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The version file calls `proj_info()` and uses `PJ_INFO` to compute the runtime version number. Since those symbols are manually defined in the version file, add them to `skip` so they aren't also generated in the content files.
|
|
40
|
+
|
|
41
|
+
## Generated Output
|
|
42
|
+
|
|
43
|
+
The generator produces three things:
|
|
44
|
+
|
|
45
|
+
**1. Version guards in content files** — version-specific symbols are wrapped in conditionals:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
if proj_version >= 60100
|
|
49
|
+
attach_function :proj_normalize_for_visualization, ...
|
|
50
|
+
end
|
|
51
|
+
if proj_version >= 60200
|
|
52
|
+
attach_function :proj_cleanup, :proj_cleanup, [], :void
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**2. Version require in the project file** (`proj_ffi.rb`):
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
require_relative 'proj_version'
|
|
60
|
+
require_relative './proj'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**3. Version skeleton file** (`proj_version.rb`) — generated once, then user-maintained:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
module Proj
|
|
67
|
+
module Api
|
|
68
|
+
def self.proj_version
|
|
69
|
+
# Return the runtime library version as an integer.
|
|
70
|
+
# Example: 90602 for version 9.6.2
|
|
71
|
+
raise NotImplementedError, "Implement proj_version to return the runtime library version number"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Implementing Version Detection
|
|
78
|
+
|
|
79
|
+
Replace the skeleton with your library's version API. PROJ provides `proj_info()` which returns a `PJ_INFO` struct with `major`, `minor`, and `patch` fields. Since the version file is loaded before the generated content files, define the struct and function here:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
module Proj
|
|
83
|
+
module Api
|
|
84
|
+
class PjInfo < FFI::Struct
|
|
85
|
+
layout :major, :int,
|
|
86
|
+
:minor, :int,
|
|
87
|
+
:patch, :int,
|
|
88
|
+
:release, :string,
|
|
89
|
+
:version, :string,
|
|
90
|
+
:searchpath, :string,
|
|
91
|
+
:paths, :pointer,
|
|
92
|
+
:path_count, :ulong
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
attach_function :proj_info, :proj_info, [], PjInfo.by_value
|
|
96
|
+
|
|
97
|
+
def self.proj_version
|
|
98
|
+
info = proj_info
|
|
99
|
+
info[:major] * 10000 + info[:minor] * 100 + info[:patch]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The version file is loaded before the content files, so `proj_version` is available when the guards execute. The skeleton is only generated if the file doesn't already exist — your implementation is preserved across re-runs.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# CMake Bindings
|
|
2
|
+
|
|
3
|
+
The `CMake` format generates `CMakeLists.txt` and `CMakePresets.json` files for building Rice C++ bindings.
|
|
4
|
+
|
|
5
|
+
Rice supports building extensions with either [extconf.rb](https://ruby-rice.github.io/4.x/packaging/extconf.rb/) or [CMake](https://ruby-rice.github.io/4.x/packaging/cmake/). While `extconf.rb` works for simple bindings, CMake is vastly superior for anything more complex — it provides better cross-platform support, dependency management, and build configuration.
|
|
6
|
+
|
|
7
|
+
**Important:** CMake generation must run after Rice generation because it scans the output directory for `*-rb.cpp` files. If no Rice output exists, the generated CMake source lists will be empty.
|
|
8
|
+
|
|
9
|
+
## Getting Started
|
|
10
|
+
|
|
11
|
+
See [Getting Started](getting_started.md) for a step-by-step guide.
|
|
12
|
+
|
|
13
|
+
## Output
|
|
14
|
+
|
|
15
|
+
See [Output](output.md) for details on the generated files.
|
|
16
|
+
|
|
17
|
+
## Filtering
|
|
18
|
+
|
|
19
|
+
See [Filtering](filtering.md) for how to exclude files from the generated CMake build and how to conditionally include generated sources and directories with `guards`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Filtering
|
|
2
|
+
|
|
3
|
+
## Skipping Files
|
|
4
|
+
|
|
5
|
+
The CMake config supports a `skip` option to exclude specific `*-rb.cpp` files from the generated `CMakeLists.txt`. However, in most cases it's better to add skip patterns to your Rice config instead — that way the problematic files are never generated, and CMake won't find them to include. The CMake `skip` is useful as a quick fix when you have stale generated files on disk that you don't want to recompile.
|
|
6
|
+
|
|
7
|
+
## Guarding Files And Directories
|
|
8
|
+
|
|
9
|
+
Use `guards` when generated paths should remain on disk but only be compiled when a CMake condition is true.
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
guards:
|
|
13
|
+
OpenCV_HAS_CUDA:
|
|
14
|
+
- opencv2/cuda*-rb.cpp
|
|
15
|
+
TARGET OpenCV::dnn:
|
|
16
|
+
- opencv2/dnn
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Guard keys are emitted as raw `if(...)` conditions. Guard values may be exact paths or globs and may match generated directories and `*-rb.cpp` files.
|
|
20
|
+
|
|
21
|
+
- Matching directories are emitted inside guarded `add_subdirectory(...)` blocks.
|
|
22
|
+
- Matching `*-rb.cpp` files are emitted inside guarded `target_sources(...)` blocks.
|
|
23
|
+
- A guard pattern that matches nothing emits a warning.
|
|
24
|
+
- If the same path matches multiple guards, generation fails with an error.
|
|
25
|
+
|
|
26
|
+
Use `skip` for permanent exclusion. Use `guards` for conditional inclusion.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Getting Started with CMake Bindings
|
|
2
|
+
|
|
3
|
+
This guide walks you through generating CMake build files for Rice C++ bindings.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
You must first generate Rice C++ bindings. See [Getting Started with Rice](../cpp/getting_started.md).
|
|
8
|
+
|
|
9
|
+
## 1. Create a configuration file
|
|
10
|
+
|
|
11
|
+
Create a file named `cmake-bindings.yaml`:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
project: my_extension
|
|
15
|
+
output: ./ext/generated
|
|
16
|
+
format: CMake
|
|
17
|
+
|
|
18
|
+
guards:
|
|
19
|
+
TARGET MyLib::gpu:
|
|
20
|
+
- gpu
|
|
21
|
+
- gpu/**/*-rb.cpp
|
|
22
|
+
|
|
23
|
+
include_dirs:
|
|
24
|
+
- "${CMAKE_CURRENT_SOURCE_DIR}/../include"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Key options:
|
|
28
|
+
|
|
29
|
+
- `project` — name used in the CMake `project()` command and build target. When omitted, only subdirectory `CMakeLists.txt` files are generated — useful when you manage the root project files yourself.
|
|
30
|
+
- `output` — directory containing the Rice `*-rb.cpp` files. `input` defaults to `output` for CMake.
|
|
31
|
+
- `guards` — raw CMake conditions mapped to generated path patterns. Use this when a generated subdirectory or `*-rb.cpp` file should only be compiled when a module or feature is available.
|
|
32
|
+
- `include_dirs` — include directories added via `target_include_directories`. These are CMake expressions written directly into the generated `CMakeLists.txt`.
|
|
33
|
+
|
|
34
|
+
See [Configuration](../configuration.md) for all options.
|
|
35
|
+
|
|
36
|
+
## 2. Generate CMake files
|
|
37
|
+
|
|
38
|
+
Run this **after** generating Rice bindings:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
ruby-bindgen cmake-bindings.yaml
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 3. Build
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd ./ext/generated
|
|
48
|
+
cmake --preset linux-debug # or macos-debug, msvc-debug, mingw-debug, etc.
|
|
49
|
+
cmake --build build/linux-debug
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
See [Output](output.md) for details on the available presets and generated files.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# CMake Output
|
|
2
|
+
|
|
3
|
+
The CMake format scans the output directory for `*-rb.cpp` files and generates:
|
|
4
|
+
|
|
5
|
+
``` mermaid
|
|
6
|
+
flowchart LR
|
|
7
|
+
subgraph Input
|
|
8
|
+
CF["cmake-bindings.yaml"]
|
|
9
|
+
S1["*-rb.cpp"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
CF & S1 --> RB["ruby-bindgen"]
|
|
13
|
+
|
|
14
|
+
subgraph "CMake Output"
|
|
15
|
+
C1["CMakeLists.txt"]
|
|
16
|
+
C2["CMakePresets.json"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
RB --> C1 & C2
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Project Files
|
|
23
|
+
|
|
24
|
+
When the `project` option is set, `ruby-bindgen` generates the root `CMakeLists.txt` and `CMakePresets.json`. When `project` is omitted, these files are **not** generated — only subdirectory `CMakeLists.txt` files are produced. This is useful when you want to create and manage the root project files yourself and only regenerate subdirectory files on subsequent runs.
|
|
25
|
+
|
|
26
|
+
## Top-level CMakeLists.txt
|
|
27
|
+
|
|
28
|
+
The top-level `CMakeLists.txt` is a complete project file that configures the entire build:
|
|
29
|
+
|
|
30
|
+
- C++17 standard requirement
|
|
31
|
+
- Rice fetched from GitHub via `FetchContent`
|
|
32
|
+
- Ruby detection via `find_package(Ruby)`
|
|
33
|
+
- Library target (SHARED on MSVC, MODULE elsewhere)
|
|
34
|
+
- Extension output configuration (correct suffix, visibility settings)
|
|
35
|
+
- Subdirectory includes and `*-rb.cpp` source file listing
|
|
36
|
+
|
|
37
|
+
For a well-documented example, see the [BitmapPlusPlus-ruby CMakeLists.txt](https://github.com/ruby-rice/BitmapPlusPlus-ruby/blob/main/ext/CMakeLists.txt). For details on how Rice uses CMake, see the Rice [CMake](https://ruby-rice.github.io/4.x/packaging/cmake/) documentation.
|
|
38
|
+
|
|
39
|
+
## CMakePresets.json
|
|
40
|
+
|
|
41
|
+
The top-level directory also gets a `CMakePresets.json` with build presets for all major platforms. For an example, see the [BitmapPlusPlus-ruby CMakePresets.json](https://github.com/ruby-rice/BitmapPlusPlus-ruby/blob/main/ext/CMakePresets.json). For details, see the Rice [CMakePresets.json](https://ruby-rice.github.io/4.x/packaging/cmake/#cmakepresetsjson) documentation.
|
|
42
|
+
|
|
43
|
+
| Preset | Platform | Compiler |
|
|
44
|
+
|--------|----------|----------|
|
|
45
|
+
| `linux-debug` / `linux-release` | Linux | GCC/Clang |
|
|
46
|
+
| `macos-debug` / `macos-release` | macOS | Clang |
|
|
47
|
+
| `msvc-debug` / `msvc-release` | Windows | MSVC |
|
|
48
|
+
| `mingw-debug` / `mingw-release` | Windows | MinGW GCC |
|
|
49
|
+
| `clang-windows-debug` / `clang-windows-release` | Windows | clang-cl |
|
|
50
|
+
|
|
51
|
+
All presets use [Ninja](https://ninja-build.org/) as the build generator and include appropriate compiler flags for each platform (visibility settings, debug info, optimization levels).
|
|
52
|
+
|
|
53
|
+
## Subdirectories
|
|
54
|
+
|
|
55
|
+
Each subdirectory containing `*-rb.cpp` files gets a minimal `CMakeLists.txt` that lists its source files and any nested subdirectories:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
ext/generated/
|
|
59
|
+
CMakeLists.txt # root project (requires project option)
|
|
60
|
+
CMakePresets.json # build presets
|
|
61
|
+
core/
|
|
62
|
+
CMakeLists.txt # add_subdirectory("hal") + target_sources(...)
|
|
63
|
+
matrix-rb.cpp
|
|
64
|
+
image-rb.cpp
|
|
65
|
+
hal/
|
|
66
|
+
CMakeLists.txt # target_sources(...)
|
|
67
|
+
interface-rb.cpp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```cmake
|
|
71
|
+
# Subdirectories
|
|
72
|
+
add_subdirectory("hal")
|
|
73
|
+
|
|
74
|
+
# Sources
|
|
75
|
+
target_sources(${CMAKE_PROJECT_NAME} PUBLIC
|
|
76
|
+
"matrix-rb.cpp"
|
|
77
|
+
"image-rb.cpp"
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
When `guards` are configured, the generator groups matching directories and files under raw CMake `if(...)` blocks in the generated `CMakeLists.txt`:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
guards:
|
|
85
|
+
TARGET OpenCV::dnn:
|
|
86
|
+
- opencv2/dnn
|
|
87
|
+
OpenCV_HAS_CUDA:
|
|
88
|
+
- opencv2/cuda*-rb.cpp
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```cmake
|
|
92
|
+
# Subdirectories
|
|
93
|
+
add_subdirectory("core")
|
|
94
|
+
|
|
95
|
+
# Sources
|
|
96
|
+
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
|
97
|
+
"core-rb.cpp"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if(TARGET OpenCV::dnn)
|
|
101
|
+
add_subdirectory("dnn")
|
|
102
|
+
endif()
|
|
103
|
+
|
|
104
|
+
if(OpenCV_HAS_CUDA)
|
|
105
|
+
target_sources(${CMAKE_PROJECT_NAME} PRIVATE
|
|
106
|
+
"cudaarithm-rb.cpp"
|
|
107
|
+
"cudaimgproc-rb.cpp"
|
|
108
|
+
)
|
|
109
|
+
endif()
|
|
110
|
+
```
|