Skip to content

Commit 5b61811

Browse files
committed
Add bindgen Dart templates, config, and README
Introduce a bindgen toolchain for generating Dart wrappers from the C++ API: adds bindgen/config.yaml, templates (file and partials for types, enums, functions, classes, constants, aliases), a README with run instructions, and .gitignore (/out). The config maps C/C++ types to Dart/ffi, defines naming rules and symbol_overrides to call existing CNativeApiBindings (ffigen) methods, and specifies entry headers and include paths. Also updates the packages/cnativeapi/cxx_impl submodule commit.
1 parent 07e1edb commit 5b61811

11 files changed

Lines changed: 298 additions & 1 deletion

File tree

bindgen/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/out

bindgen/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Flutter Wrapper Bindgen
2+
3+
This bindgen setup generates Dart wrapper files from C++ API headers.
4+
5+
- Input: `cxx_impl/src/**/*.h` (excluding `capi` and `platform`)
6+
- Output: `bindgen/out/**/*.dart`
7+
- FFI policy: wrappers call existing `CNativeApiBindings` methods from ffigen output
8+
9+
## Run
10+
11+
```bash
12+
cd nativeapi-flutter
13+
PYTHONPATH=packages/cnativeapi/cxx_impl/tools python3 -m bindgen \
14+
--config bindgen/config.yaml \
15+
--dump-ir bindgen/out/ir.json \
16+
--out bindgen/out
17+
```
18+
19+
## Notes
20+
21+
- This does **not** regenerate `lib/src/bindings_generated.dart`.
22+
- Add symbol exceptions in `mapping.options.symbol_overrides`.

bindgen/config.yaml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
clang_flags:
2+
- -x
3+
- c++
4+
- -std=c++17
5+
6+
entry_headers:
7+
- packages/cnativeapi/cxx_impl/src/accessibility_manager.h
8+
- packages/cnativeapi/cxx_impl/src/application.h
9+
- packages/cnativeapi/cxx_impl/src/application_event.h
10+
- packages/cnativeapi/cxx_impl/src/autostart.h
11+
- packages/cnativeapi/cxx_impl/src/dialog.h
12+
- packages/cnativeapi/cxx_impl/src/display.h
13+
- packages/cnativeapi/cxx_impl/src/display_event.h
14+
- packages/cnativeapi/cxx_impl/src/display_manager.h
15+
- packages/cnativeapi/cxx_impl/src/foundation/color.h
16+
- packages/cnativeapi/cxx_impl/src/foundation/event.h
17+
- packages/cnativeapi/cxx_impl/src/foundation/event_emitter.h
18+
- packages/cnativeapi/cxx_impl/src/foundation/geometry.h
19+
- packages/cnativeapi/cxx_impl/src/foundation/id_allocator.h
20+
- packages/cnativeapi/cxx_impl/src/foundation/keyboard.h
21+
- packages/cnativeapi/cxx_impl/src/foundation/native_object_provider.h
22+
- packages/cnativeapi/cxx_impl/src/foundation/object_registry.h
23+
- packages/cnativeapi/cxx_impl/src/image.h
24+
- packages/cnativeapi/cxx_impl/src/keyboard_event.h
25+
- packages/cnativeapi/cxx_impl/src/keyboard_monitor.h
26+
- packages/cnativeapi/cxx_impl/src/menu.h
27+
- packages/cnativeapi/cxx_impl/src/menu_event.h
28+
- packages/cnativeapi/cxx_impl/src/message_dialog.h
29+
- packages/cnativeapi/cxx_impl/src/placement.h
30+
- packages/cnativeapi/cxx_impl/src/positioning_strategy.h
31+
- packages/cnativeapi/cxx_impl/src/preferences.h
32+
- packages/cnativeapi/cxx_impl/src/secure_storage.h
33+
- packages/cnativeapi/cxx_impl/src/shortcut.h
34+
- packages/cnativeapi/cxx_impl/src/shortcut_event.h
35+
- packages/cnativeapi/cxx_impl/src/shortcut_manager.h
36+
- packages/cnativeapi/cxx_impl/src/storage.h
37+
- packages/cnativeapi/cxx_impl/src/tray_icon.h
38+
- packages/cnativeapi/cxx_impl/src/tray_icon_event.h
39+
- packages/cnativeapi/cxx_impl/src/tray_manager.h
40+
- packages/cnativeapi/cxx_impl/src/url_opener.h
41+
- packages/cnativeapi/cxx_impl/src/window.h
42+
- packages/cnativeapi/cxx_impl/src/window_event.h
43+
- packages/cnativeapi/cxx_impl/src/window_manager.h
44+
- packages/cnativeapi/cxx_impl/src/window_registry.h
45+
46+
include_paths:
47+
- packages/cnativeapi/cxx_impl/src
48+
- packages/cnativeapi/cxx_impl/include
49+
50+
mapping:
51+
language: dart
52+
output_note: Generated wrapper from C++ API. Calls existing ffigen bindings.
53+
54+
naming:
55+
file_name: snake_case
56+
type_name: pascal_case
57+
enum_name: pascal_case
58+
enum_value_name: camel_case
59+
function_name: camel_case
60+
method_name: camel_case
61+
class_name: pascal_case
62+
field_name: camel_case
63+
param_name: camel_case
64+
constant_name: screaming_snake_case
65+
alias_name: pascal_case
66+
strip_prefixes:
67+
- na_
68+
- NA_
69+
- nativeapi_
70+
- NATIVEAPI_
71+
strip_suffixes:
72+
- _t
73+
- _T
74+
add_prefix: ""
75+
add_suffix: ""
76+
77+
types:
78+
void: void
79+
bool: bool
80+
_Bool: bool
81+
char: int
82+
short: int
83+
int: int
84+
long: int
85+
"long long": int
86+
int8_t: int
87+
int16_t: int
88+
int32_t: int
89+
int64_t: int
90+
int8: int
91+
int16: int
92+
int32: int
93+
int64: int
94+
"unsigned char": int
95+
"unsigned short": int
96+
"unsigned int": int
97+
"unsigned long": int
98+
"unsigned long long": int
99+
uint8_t: int
100+
uint16_t: int
101+
uint32_t: int
102+
uint64_t: int
103+
uint8: int
104+
uint16: int
105+
uint32: int
106+
uint64: int
107+
size_t: int
108+
float: double
109+
double: double
110+
float32: double
111+
float64: double
112+
cstring: "ffi.Pointer<ffi.Char>"
113+
"void*": "ffi.Pointer<ffi.Void>"
114+
"const void*": "ffi.Pointer<ffi.Void>"
115+
"char*": "ffi.Pointer<ffi.Char>"
116+
"const char*": "ffi.Pointer<ffi.Char>"
117+
118+
default_type: dynamic
119+
passthrough_unknown: false
120+
121+
options:
122+
bindings_class: CNativeApiBindings
123+
bindings_import: package:cnativeapi/src/bindings_generated.dart
124+
symbol_overrides:
125+
nativeapi::RunApp: native_run_app
126+
nativeapi::Application::GetInstance: native_application_get_instance
127+
nativeapi::Window::Window: native_window_create
128+
129+
filters:
130+
allowlist_regex: []
131+
denylist_regex: []
132+
exclude_dirs:
133+
- capi
134+
- platform

bindgen/template/file/dart.j2

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{%- from 'partials/type.j2' import render_type -%}
2+
{%- from 'partials/enum.j2' import render_enum -%}
3+
{%- from 'partials/function.j2' import render_function -%}
4+
{%- from 'partials/class.j2' import render_class -%}
5+
{%- from 'partials/constant.j2' import render_constant -%}
6+
{%- from 'partials/alias.j2' import render_alias -%}
7+
8+
// =============================================================================
9+
// Auto-generated Dart wrapper for: {{ file_path }}
10+
// {{ mapping.options.output_note | default("Generated by bindgen") }}
11+
// =============================================================================
12+
13+
import 'dart:ffi' as ffi;
14+
import '{{ mapping.options.bindings_import }}';
15+
16+
{%- if has_types %}
17+
{% for item in types %}
18+
{{ render_type(item, mapping) }}
19+
{% endfor %}
20+
{%- endif %}
21+
22+
{%- if has_enums %}
23+
{% for item in enums %}
24+
{{ render_enum(item, mapping) }}
25+
{% endfor %}
26+
{%- endif %}
27+
28+
{%- if has_aliases %}
29+
{% for item in aliases %}
30+
{{ render_alias(item, mapping) }}
31+
{% endfor %}
32+
{%- endif %}
33+
34+
{%- if has_constants %}
35+
{% for item in constants %}
36+
{{ render_constant(item, mapping) }}
37+
{% endfor %}
38+
{%- endif %}
39+
40+
{%- if has_functions %}
41+
{% for item in functions %}
42+
{{ render_function(item, mapping) }}
43+
{% endfor %}
44+
{%- endif %}
45+
46+
{%- if has_classes %}
47+
{% for item in classes %}
48+
{{ render_class(item, mapping) }}
49+
{% endfor %}
50+
{%- endif %}

bindgen/template/partials/alias.j2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{%- macro render_alias(item, mapping) -%}
2+
{% set target = item.target.mapped if item.target else 'dynamic' %}typedef {{ item.name }} = {% if '::' in target %}dynamic{% else %}{{ target }}{% endif %};
3+
{%- endmacro -%}

bindgen/template/partials/class.j2

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{%- macro capi_for_method(cls, method, mapping) -%}
2+
{%- set overrides = mapping.options.symbol_overrides if mapping.options and mapping.options.symbol_overrides else {} -%}
3+
{%- set key = ((cls.raw.qualified_name if cls.raw and cls.raw.qualified_name else cls.raw.name) ~ '::' ~ method.raw.name) -%}
4+
{%- if key and overrides.get(key) -%}
5+
{{ overrides.get(key) }}
6+
{%- else -%}
7+
native_{{ cls.raw.name | snake_case }}_{{ method.raw.name | snake_case }}
8+
{%- endif -%}
9+
{%- endmacro -%}
10+
11+
{%- macro render_class(item, mapping) -%}
12+
class {{ item.name }} {
13+
{{ item.name }}(this._bindings, this.handle);
14+
15+
final {{ mapping.options.bindings_class }} _bindings;
16+
final ffi.Pointer<ffi.Void> handle;
17+
18+
{%- for method in item.methods %}
19+
{%- if not (method.raw and (method.raw.name == item.raw.name or method.raw.name.startswith('~'))) %}
20+
{% set ret = method.return_type.mapped if method.return_type else 'void' %}{% if '::' in ret %}{% set ret = 'dynamic' %}{% endif %}{{ ret }} {{ method.name }}(
21+
{%- for param in method.params %}
22+
{% set ptype = param.type.mapped %}{% if '::' in ptype %}dynamic{% else %}{{ ptype }}{% endif %} {{ param.name }}{% if not loop.last %}, {% endif %}
23+
{%- endfor %}
24+
) {
25+
{% if method.return_type and method.return_type.kind != 'void' %}return {% endif %}_bindings.{{ capi_for_method(item, method, mapping) }}(
26+
{%- if not method.static %}
27+
handle{% if method.params|length > 0 %}, {% endif %}
28+
{%- endif %}
29+
{%- for param in method.params %}
30+
{{ param.name }}{% if not loop.last %}, {% endif %}
31+
{%- endfor %}
32+
);
33+
}
34+
35+
{%- endif %}
36+
{%- endfor %}
37+
}
38+
{%- endmacro -%}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{%- macro render_constant(item, mapping) -%}
2+
{% set ctype = item.type.mapped if item.type else 'dynamic' %}const {% if '::' in ctype %}dynamic{% else %}{{ ctype }}{% endif %} {{ item.name }} = {{ item.value }};
3+
{%- endmacro -%}

bindgen/template/partials/enum.j2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{%- macro render_enum(item, mapping) -%}
2+
enum {{ item.name }} {
3+
{%- for value in item.values %}
4+
{{ value.name }}{% if not loop.last %},{% endif %}
5+
{%- endfor %}
6+
}
7+
{%- endmacro -%}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{%- macro capi_for_function(item, mapping) -%}
2+
{%- set overrides = mapping.options.symbol_overrides if mapping.options and mapping.options.symbol_overrides else {} -%}
3+
{%- set key = item.raw.qualified_name if item.raw and item.raw.qualified_name else item.raw.name -%}
4+
{%- if key and overrides.get(key) -%}
5+
{{ overrides.get(key) }}
6+
{%- else -%}
7+
native_{{ item.raw.name | snake_case }}
8+
{%- endif -%}
9+
{%- endmacro -%}
10+
11+
{%- macro render_function(item, mapping) -%}
12+
{%- set ret = item.return_type.mapped if item.return_type else 'void' -%}
13+
{%- if '::' in ret -%}{%- set ret = 'dynamic' -%}{%- endif -%}
14+
{{ ret }} {{ item.name }}(
15+
{{ mapping.options.bindings_class }} bindings
16+
{%- for param in item.params %}
17+
, {% set ptype = param.type.mapped %}{% if '::' in ptype %}dynamic{% else %}{{ ptype }}{% endif %} {{ param.name }}
18+
{%- endfor %}
19+
) {
20+
{% if item.return_type and item.return_type.kind != 'void' %}return {% endif %}bindings.{{ capi_for_function(item, mapping) }}(
21+
{%- for param in item.params %}
22+
{{ param.name }}{% if not loop.last %}, {% endif %}
23+
{%- endfor %}
24+
);
25+
}
26+
{%- endmacro -%}

bindgen/template/partials/type.j2

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{%- macro render_type(item, mapping) -%}
2+
class {{ item.name }} {
3+
{{ item.name }}({
4+
{%- for field in item.fields %}
5+
required this.{{ field.name }},
6+
{%- endfor %}
7+
});
8+
9+
{%- for field in item.fields %}
10+
{% set ftype = field.type.mapped %}final {% if '::' in ftype %}dynamic{% else %}{{ ftype }}{% endif %} {{ field.name }};
11+
{%- endfor %}
12+
}
13+
{%- endmacro -%}

0 commit comments

Comments
 (0)