Skip to content

Commit a1dd7d4

Browse files
committed
Add Python bindgen tool and templates
Introduce a new bindgen tool under tools/bindgen. Adds a CLI entrypoint, configuration loader (bindgen.yaml), a libclang-based parser, a normalizer that converts Clang AST to a language-agnostic IR, IR dataclasses and JSON serializer, and a Jinja2-based code generator with template context. Includes Dart templates/lang mapping and helper functions, README design doc, __main__ for module execution, and a .gitignore. Notes: runtime deps include clang.cindex, PyYAML and Jinja2; CLI supports --config, --out, --lang, --dump-ir and --platform.
1 parent 1636a3f commit a1dd7d4

File tree

18 files changed

+1050
-0
lines changed

18 files changed

+1050
-0
lines changed

tools/bindgen/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
__pycache__/
2+
*.py[cod]
3+
*.pyo
4+
*.pyd
5+
out/

tools/bindgen/README.md

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
# Bindgen 设计文档(Python)
2+
3+
本目录目标:使用 Python 实现一个面向 C++ Headers 的 bindgen,自动解析头文件并生成多语言绑定(Dart / Swift / Rust 等)。各语言 API **统一调用 C API**,再由 C API 调用 C++ 实现,确保 ABI 稳定与可移植性。
4+
5+
本文档说明核心需求、架构、数据模型、生成流程、CLI、可扩展性与里程碑计划。
6+
7+
## 目标与非目标
8+
9+
**目标**
10+
11+
- 解析 C++ 公开 API(以头文件为主)
12+
- 生成多语言 bindings(Dart FFI、Swift bridging、Rust FFI)
13+
- 所有语言绑定统一调用 C API 层(避免直接绑定 C++ ABI)
14+
- 统一的中间表示(IR),减少多语言实现成本
15+
- 可配置、可扩展、可测试
16+
- 与现有仓库结构和 CMake 构建保持良好配合
17+
18+
**非目标(第一阶段)**
19+
20+
- 不做完整 C++ 语义分析(模板深度推导、宏复杂展开等)
21+
- 不自动绑定 C++ class 的复杂继承、多态或异常(需经 C API 封装)
22+
- 不直接生成 GUI/业务层封装,仅做 FFI 级别桥接
23+
24+
## 总体架构
25+
26+
```text
27+
C++ Headers
28+
|
29+
v
30+
Parser (libclang/clang.cindex)
31+
|
32+
v
33+
AST Normalizer
34+
|
35+
v
36+
IR (统一中间表示)
37+
|
38+
v
39+
Code Generator (通用生成器)
40+
|
41+
+--------------------+--------------------+--------------------+
42+
| | |
43+
v v v
44+
Dart Templates Swift Templates Rust Templates
45+
| | |
46+
+---------+----------+---------+----------+---------+
47+
| |
48+
v v
49+
Output bindings (ffi glue per language)
50+
|
51+
v
52+
C API (extern "C")
53+
|
54+
v
55+
C++ Implementation
56+
```
57+
58+
- **Parser**:基于 `libclang`/`clang.cindex` 解析头文件,生成 AST
59+
- **Normalizer**:将 AST 规约为可生成的 IR(类型、函数、常量、结构体)
60+
- **IR**:语言无关的描述结构
61+
- **Code Generator**:通用生成器,负责加载模板与语言映射配置并输出 bindings
62+
- **Templates**:语言模板 + 语言映射配置(类型映射、命名风格、ABI 约定)
63+
- **C API**:稳定 ABI 层,`extern "C"` 函数,供所有语言 bindings 调用
64+
- **C++ Implementation**:真实实现层,仅由 C API 访问
65+
66+
## 输入与约束
67+
68+
- 入口:一组 C++ 头文件 + include paths + compile flags
69+
- 建议约定宏:用于筛选可导出 API(例如 `NATIVEAPI_EXPORT`
70+
- 过滤方式:
71+
- `export_macro`:只导出带宏标记的符号
72+
- `// bindgen:ignore`:显式忽略
73+
- 可选白名单/黑名单(按正则匹配函数/类型名)
74+
- 只处理(MVP):
75+
- 可导出的 C API 函数(`extern "C"`
76+
- `struct` + POD 类型
77+
- `enum` 常量
78+
- `#define` 的简单常量
79+
- 入口头文件推荐指定“主头文件”(统一 include 顺序,避免重复解析)
80+
81+
**约定**:C++ 头文件中的导出 API,必须能映射为 C API(如 `extern "C"` wrapper),bindgen 只对 C API 进行绑定生成。
82+
83+
## IR 设计
84+
85+
### 类型系统(IRType)
86+
87+
- 基础类型:`void`, `bool`, `int8/16/32/64`, `uint8/16/32/64`, `float32/64`
88+
- 扩展基础类型:`size_t`, `ssize_t`, `intptr`, `uintptr`
89+
- 类型别名:`typedef/using` 归一化为 `alias`
90+
- 指针类型:`pointer<T>`
91+
- 数组类型:`array<T, N>`
92+
- 结构体:`struct { fields }`
93+
- 枚举:`enum { values }`
94+
- 字符串:`char*` 规范化为 `cstring`
95+
- 函数指针:`fnptr(ret, params)`(MVP 可忽略,后续支持)
96+
- 限定符:`const/volatile` 记录在 `qualifiers`
97+
- C++ `enum class` 归一化为 `enum`(保留 `scoped` 标记)
98+
99+
### 函数(IRFunction)
100+
101+
- 名称、返回类型、参数列表
102+
- 参数可包含 `in/out` 注解
103+
- 支持 `nullable` 标注
104+
- 可记录 `callconv``variadic`(默认不支持变参)
105+
106+
### 模块(IRModule)
107+
108+
- headers
109+
- types
110+
- functions
111+
- constants
112+
- aliases
113+
114+
### IR JSON 示例(最小)
115+
116+
```json
117+
{
118+
"headers": ["include/nativeapi.h"],
119+
"types": [
120+
{
121+
"kind": "struct",
122+
"name": "Point",
123+
"fields": [
124+
{"name": "x", "type": {"kind": "float32"}},
125+
{"name": "y", "type": {"kind": "float32"}}
126+
]
127+
}
128+
],
129+
"functions": [
130+
{
131+
"name": "na_distance",
132+
"return_type": {"kind": "float32"},
133+
"params": [
134+
{"name": "a", "type": {"kind": "pointer", "to": {"kind": "struct", "name": "Point"}}, "nullable": false},
135+
{"name": "b", "type": {"kind": "pointer", "to": {"kind": "struct", "name": "Point"}}, "nullable": false}
136+
]
137+
}
138+
],
139+
"constants": [
140+
{"name": "NA_OK", "type": {"kind": "int32"}, "value": 0}
141+
],
142+
"aliases": [
143+
{"name": "NAHandle", "target": {"kind": "uintptr"}}
144+
]
145+
}
146+
```
147+
148+
## 代码生成策略
149+
150+
**绑定链路约束**
151+
152+
- 生成的 Dart/Swift/Rust bindings **只调用 C API**,不直接依赖 C++ 符号
153+
- C API 负责:参数规约、ABI 稳定、与 C++ 实现交互
154+
- C++ 实现层可自由演进,但对外 ABI 需保持稳定
155+
156+
## 通用模板生成器设计
157+
158+
Generator 可以做成通用的模板渲染器,通过“语言模板 + 语言映射配置”驱动输出,不把语言差异硬编码在 Python 里。
159+
160+
**设计原则**
161+
162+
- 生成器只做:加载 IR → 选择语言 → 渲染模板 → 写文件
163+
- 语言差异放在模板与映射配置(类型映射、命名风格、调用约定)
164+
- 复杂逻辑尽量前置到 Normalizer(保证模板尽量“无脑”)
165+
- 模板约定:模板依赖的上下文字段必须稳定并有文档
166+
167+
**目录结构(建议)**
168+
169+
```
170+
tools/bindgen/
171+
parser.py # 解析 C/C++ headers -> AST
172+
normalizer.py # AST -> IR 规约
173+
ir/
174+
model.py # IR 数据结构
175+
serializer.py # IR JSON 读写
176+
codegen/
177+
generator.py # 通用生成器入口
178+
context.py # 模板上下文构建
179+
templates/
180+
dart/
181+
bindings.j2
182+
helpers.j2
183+
lang.yaml
184+
swift/
185+
bindings.j2
186+
modulemap.j2
187+
lang.yaml
188+
rust/
189+
bindings.j2
190+
lib.j2
191+
lang.yaml
192+
```
193+
194+
**语言映射配置(示例)**
195+
196+
```yaml
197+
language: dart
198+
types:
199+
void: Void
200+
int32: Int32
201+
uint32: Uint32
202+
float32: Float
203+
cstring: Pointer<Utf8>
204+
conventions:
205+
enum_as_int: true
206+
struct_layout: ffi.Struct
207+
callconv: native
208+
naming:
209+
function: camel
210+
constant: upper_snake
211+
```
212+
213+
**通用生成器伪代码**
214+
215+
```python
216+
ir = load_ir(...)
217+
lang = load_lang_config(...)
218+
templates = load_templates(lang)
219+
context = build_context(ir, lang)
220+
render_to_files(templates, context, out_dir)
221+
```
222+
223+
**模板上下文约定(建议)**
224+
225+
- `module`: 当前模块信息(headers, namespaces)
226+
- `types`: 结构体/枚举/别名列表(已排序)
227+
- `functions`: 函数列表(已过滤)
228+
- `constants`: 常量列表
229+
- `mapping`: 语言映射配置(types/conventions/naming)
230+
- `helpers`: 预计算字段(如 dart ffi 类型名、rust type 名)
231+
232+
**优点**
233+
234+
- 新增语言只需新增模板 + 映射配置
235+
- 语言差异集中可见,维护成本低
236+
- 测试可按模板/映射分层
237+
238+
### Dart
239+
240+
- 生成 `ffi.DynamicLibrary` + `typedef` + `lookupFunction`
241+
- `struct` 映射为 `ffi.Struct`
242+
- `enum` 映射为 `int`
243+
- `cstring` -> `Pointer<Utf8>` + helper
244+
- 输出文件(建议):`bindings.dart`, `helpers.dart`
245+
246+
### Swift
247+
248+
- C header 生成 module map(如需)
249+
- `struct` 使用 `@frozen struct``UnsafePointer`
250+
- `enum` -> Swift `enum` with rawValue
251+
- 暴露 C 函数桥接层
252+
- 输出文件(建议):`Bindings.swift`, `module.modulemap`(可选)
253+
254+
### Rust
255+
256+
- 生成 `extern "C"` block
257+
- `struct` -> `#[repr(C)] struct`
258+
- `enum` -> `#[repr(C)] enum` or `type alias` for constants
259+
- `cstring` -> `*const c_char`
260+
- 输出文件(建议):`lib.rs`, `bindings.rs`
261+
262+
## 配置与注解
263+
264+
### YAML 配置文件
265+
266+
```yaml
267+
entry_headers:
268+
- include/nativeapi.h
269+
include_paths:
270+
- include
271+
clang_flags:
272+
- -std=c11
273+
- -DNATIVEAPI_EXPORT
274+
languages:
275+
- dart
276+
- swift
277+
- rust
278+
filters:
279+
export_macro: NATIVEAPI_EXPORT
280+
allowlist_regex: []
281+
denylist_regex: []
282+
platform:
283+
os: macos
284+
abi: clang
285+
```
286+
287+
### 注解建议(可扩展)
288+
289+
- 使用宏或注释标注 API,如:
290+
- `NATIVEAPI_EXPORT` 标记导出
291+
- `// bindgen:ignore` 跳过
292+
- `// bindgen:nullable` 标记可为空
293+
- `// bindgen:rename <NewName>` 重命名
294+
295+
## CLI 设计
296+
297+
```
298+
PYTHONPATH=tools python -m bindgen \
299+
--config tools/bindgen/bindgen.yaml \
300+
--out tools/bindgen/out
301+
```
302+
303+
常用参数:
304+
305+
- `--config` 配置文件
306+
- `--out` 输出目录
307+
- `--lang` 指定语言(可覆盖配置)
308+
- `--dump-ir` 输出 IR JSON 便于调试
309+
- `--platform` 指定平台(如 `windows-msvc`、`linux-gnu`、`macos-clang`)
310+
311+
## 开发计划(里程碑)
312+
313+
1. **MVP**
314+
- 支持解析函数 + 基础类型
315+
- 生成 Dart bindings
316+
- IR JSON dump
317+
- 最小可用示例(单头文件 -> bindings)
318+
319+
2. **扩展**
320+
- 支持 struct、enum
321+
- 生成 Swift/Rust bindings
322+
323+
3. **增强**
324+
- 增加注解/宏过滤
325+
- 增加类型映射配置
326+
327+
4. **稳定化**
328+
- 兼容性测试
329+
- CI 脚本
330+
331+
## 关键技术点
332+
333+
- `libclang` 解析:需要确保 clang 能找到 include paths
334+
- 类型映射:不同语言对 `size_t`, `bool` 的对应关系不一致
335+
- ABI 兼容:必须保证 `repr(C)`/`ffi.Struct` 对齐一致
336+
337+
## 风险与规避
338+
339+
- **宏复杂度高**:MVP 仅处理简单宏与 export 宏
340+
- **C++ 语义**:默认 `extern "C"` 入口
341+
- **跨平台 ABI**:需在生成器中区分平台(Windows/Linux/macOS)
342+
343+
## 下一步
344+
345+
- 补充 `bindgen.yaml` 示例
346+
- 实现 `parser.py` 与 `ir.py` 的最小骨架
347+
- 选择模板引擎(如 `jinja2`)用于 codegen

tools/bindgen/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Bindgen tool package."""
2+
3+
from .cli import main
4+
5+
__all__ = ["main"]

tools/bindgen/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .cli import main
2+
3+
if __name__ == "__main__":
4+
raise SystemExit(main())

tools/bindgen/bindgen.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
entry_headers:
2+
- include/nativeapi.h
3+
include_paths:
4+
- include
5+
clang_flags:
6+
- -x
7+
- c++
8+
- -std=c++17
9+
- -DNATIVEAPI_EXPORT
10+
languages:
11+
- dart
12+
filters:
13+
export_macro: NATIVEAPI_EXPORT
14+
allowlist_regex: []
15+
denylist_regex: []
16+
platform:
17+
os: macos
18+
abi: clang

0 commit comments

Comments
 (0)