从 C++ 头文件自动生成 C ABI 以及 Rust / Dart / C# 各端 FFI 绑定的代码生成工具。
┌──────────────┐ ┌──────────────────────┐ ┌────────────┐ ┌──────────────────┐
│ C++ Header │──→│ codegen-capi │──→│ IR (JSON) │──→│ codegen-bindings │
│ (core/src) │ │ libclang 解析 + IR │ │ │ │ rust/dart/cs │
│ │ │ 生成 C ABI + 伞头文件 │ │ │ │ │
└──────────────┘ └──────────────────────┘ └────────────┘ └──────────────────┘
只有 codegen-capi 依赖 libclang;codegen-bindings 消费它导出的 IR JSON。
tools/codegen/
├── Cargo.toml # Cargo workspace
├── shared/ # codegen-shared:parser(libclang)、IR 模型与序列化、
│ # naming(命名工具 + 跨头文件类型索引)、文件写入/校验、API_HEADERS 清单
├── capi/ # codegen-capi(bin):C API(.h + .cpp)与 umbrella header 生成
└── bindings/ # codegen-bindings(bin):Rust / Dart / C# FFI 绑定生成
日常统一通过 workspace 根目录的 ./codegen 脚本执行(它负责按 workspace 布局
传路径并串联两个 generator):
./codegen # 全量:C ABI → 三端绑定
./codegen capi # 只生成 C ABI
./codegen bindings # 只生成绑定(--lang rust,dart,csharp 可选子集)
./codegen check # 只读校验,生成物过期时非零退出(给 CI 用)
./codegen sync # 改完 core 后的一键联动(见下)
./codegen --dump-ir ir.json # 把中间 IR 保留到指定路径调试改了 core 的头文件后执行,按序完成:
- 全量重新生成(C ABI + 三端绑定)
- 提交 core(消息用
-m指定,默认Update API) - 对每个 binding 仓库:把内嵌的 core submodule(
cxx_impl) 更新到 core 的最新提交(从本地 core 取,不要求先 push);rust 额外重跑 bindgen 刷新crates/cnativeapi/src/bindings.rs;flutter 额外执行仓库自带的codegen.py(.mm include、umbrella header、ffigen.yaml、dart ffigen) - 有变化的 binding 仓库各提交一个
Sync with core <sha> - 提交 workspace 的 submodule 指针
默认只提交不推送;--push 会按 core → bindings → workspace 的顺序推送
(保证远端的 submodule 指针不悬空)。任一仓库处于 detached HEAD 会直接报错。
bindgen / dart 未安装时对应步骤跳过并告警。
未初始化的 binding submodule 会被自动跳过,此时照常产出完整的 C ABI。
生成产物:
- C ABI →
core/src/capi/ - umbrella header →
core/include/nativeapi.h - Rust FFI →
bindings/rust/crates/nativeapi/src/ - Dart FFI →
bindings/flutter/packages/nativeapi/lib/src/ - C# FFI →
bindings/csharp/src/CNativeAPI/generated/(raw 层)+bindings/csharp/src/NativeAPI/(公开层)
core/src/capi/ 全部由本工具生成,唯一的例外是手写支持层
string_utils_c.{h,cpp}(字符串 / 值容器的所有权原语)。生成物首行都带
// AUTO-GENERATED. DO NOT EDIT.,改头文件再重新生成,不要改生成物。
没有该 banner 的已存在文件永远不会被覆盖(保护手写代码)。
要纳入生成的头文件清单 API_HEADERS 定义在 shared/src/lib.rs。
C ABI 是所有绑定的地基,新增符号后需要同步下游:
-
类型标签:新增的句柄类型需要在
core/src/foundation/id_allocator.h的IdTypeTag<T>注册表里追加一个编号(只追加,不改已有编号)。漏了会在 编译期报错,不会静默出问题。 -
submodule:
bindings/rust/crates/cnativeapi/cxx_impl、bindings/flutter/packages/cnativeapi/cxx_impl、bindings/csharp/src/CNativeAPI/cxx_impl都是 nativeapi 仓库的 submodule,提交 core 后需要git submodule update --remote -
Rust raw FFI:
crates/cnativeapi/src/bindings.rs由 bindgen 生成并入库, 新增 C 符号后需重新生成(在 workspace 根目录执行):bindgen core/include/nativeapi.h \ --allowlist-function 'native_.*|free_c_str' --allowlist-type 'native_.*' \ --allowlist-var 'NATIVE_.*' \ --with-derive-default --no-layout-tests --no-prepend-enum-name \ --raw-line '#![allow(non_upper_case_globals)]' \ --raw-line '#![allow(non_camel_case_types)]' \ --raw-line '#![allow(non_snake_case)]' \ -o bindings/rust/crates/cnativeapi/src/bindings.rs \ -- -x c -isysroot "$(xcrun --show-sdk-path)" -Icore/src -Icore/include
-
模块声明:Rust 侧新增的
xxx.rs需要在crates/nativeapi/src/lib.rs中pub mod