FEATURED · 精选文章

Cocos Engine bindings-generator 深度指南:基于 Clang 的 C++ 到 JavaScript 自动绑定代码生成器

发布时间 / 2026/9/15 20:56:41
来源 / 创域科博编辑部
栏目 / 资讯中心
Cocos Engine bindings-generator 深度指南:基于 Clang 的 C++ 到 JavaScript 自动绑定代码生成器 Cocos Engine bindings-generator 深度指南基于 Clang 的 C 到 JavaScript 自动绑定代码生成器【免费下载链接】cocos-engineCocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.项目地址: https://gitcode.com/GitHub_Trending/co/cocos-engine本指南围绕 Cocos Engine 仓库内 bindings-generator 的 README 展开系统讲解这套基于libclang的自动绑定代码生成工具从环境搭建、命令行用法、.ini配置到 Cheetah 模板机制与源码级实现原理并结合仓库内 generator.py 与 tojs 集成脚本 给出完整调用链。读完本文你将掌握如何为自己的 C 模块自动生成 JavaScriptJSB绑定代码、如何编写.ini配置控制导出范围以及生成器内部Clang 解析 → 类型归一化 → 模板渲染的完整工作流程。工具定位它是做什么的bindings-generator是 Cocos 引擎用来自动生成 C/C 与脚本语言当前目标为 SpiderMonkey JavaScript 引擎之间绑定胶水代码的代码生成器。它不依赖手写大量重复的jsval ↔ native转换逻辑而是通过以下方式工作读取.ini配置文件得知要解析哪些头文件、导出哪些类与函数调用libclang预编译的 clang 12.0 动态库解析 C 头文件构建 AST将 AST 中的类、方法、字段、枚举、模板类型等归一化为内部的NativeClass/NativeFunction/NativeType等模型基于Cheetah 模板按目标 VM 分目录存放渲染出最终的.hpp/.cpp/.jsAPI 文档文件。在 Cocos 引擎中的实际场景是native/tools/tojs/genbindings.py调用本生成器为native/cocos下的 C 模块产出jsb_xxx_auto.h/.cpp从而让 JavaScript 层能够直接调用原生引擎能力。官方在 native/tools/tojs/README.mdown 中注明Cocos Creator 3.7.0 起引擎内部已改用更便捷的 SWIG 方案见 native/tools/swig-config但本工具仍可用于为自己的项目生成 JS 绑定代码本文讲解的机制对理解新旧方案都同样有价值。环境要求与预编译 libclang 12.0依赖清单运行生成器需要以下环境来自 README依赖说明Python 3.x64 位生成器主程序运行环境generator.py兼容 Python 2/3 两套configparser导入PyYAML 5.4.1解析目标目录下的${target}.yaml类型转换配置Cheetah3模板引擎用于渲染绑定代码libclang 动态库clang 的 Python 绑定clang.cindex所需的底层动态库仓库内已附带预编译的 libclang 12.0位于 native/tools/bindings-generator/libclang/libclang.dllWindowslibclang.dylibmacOSlibclang.soLinux目录中的 VERSION.txt 明确标注了版本为libclang in LLVM 12.0.0。一个关键的版本兼容性提醒README 特别强调如果你要让预编译的 libclang 12.0 配合 Android NDK 工作只有 NDK r21 及以上版本才能与之正确配合README 原文为 only the NDK r21 can work corrently with it。这一约束也贯穿了后面所有平台的环境搭建步骤。手动下载 libclang可选如果你不使用仓库自带的预编译库也可以自行下载从 LLVM 12.0.0 官方 release 按平台下载预编译二进制例如 macOS 对应clangllvm-12.0.0-x86_64-apple-darwin.tar.xz解压或安装找到libclang.dllWindows或libclang.dylibmacOS将动态库复制到bindings-generator/libclang/目录下。Python 绑定源码与动态库配套的 Python 绑定clang.cindex、clang.enumerations、clang.__init__就放在仓库的 native/tools/bindings-generator/clang/ 目录中generator.py通过from clang import cindex直接使用。命令行用法与参数说明生成器入口为 generator.pymain()位于 generator.py#L2271完整用法如下Usage: generator.py [options] {configfile} Options: -h, --help show this help message and exit -s SECTION sets a specific section to be converted -t TARGET specifies the target vm. Will search for TARGET.yaml从main()源码中还可以看到两个额外参数-o OUTDIR指定生成代码的输出目录未指定时默认为bindings-generator/gen/-n OUT_FILE指定输出文件主名默认取.ini配置节中的prefix。生成结果会是${out_file}.cpp、${out_file}.h、${out_file}.json与${out_file}.inl四个文件见 generator.py#L1983-L1986。工作方式概括指定目标 VM当前唯一目标是spidermonkey和.ini文件中你想生成代码的配置节section。目标 VM 会决定两件事从targets/目录下选择对应的模板目录以及加载targets/${target}/conversions.yaml。目标target的发现机制main()会扫描bindings-generator/targets/下的所有子目录作为可用 target 列表并自动跳过.svn、.cvs、.git等隐藏目录generator.py#L2323-L2346。当前仓库中只有spidermonkey一个 target位于 native/tools/bindings-generator/targets/spidermonkey/。关键执行细节userconf.ini 与 libclang 路径执行时main()会先读取native/tools/tojs/userconf.ini由genbindings.py生成从中取得cxxgeneratordir配置然后通过cindex.Config.set_library_path()把 libclang 动态库目录设置到libclang/generator.py#L2293-L2302。这也解释了为什么 libclang 动态库必须放在bindings-generator/libclang下。环境搭建与自检测试README 附带了一个简单测试用来确认生成器工作正常、环境配置正确。该测试依赖预编译的 libclang 12.0因此要求 Android NDK r21 或更高版本同时测试代码使用了string与stdint.h需要提供这些头文件的 C 实现测试脚本默认使用 Android NDK 自带的 LLVM libc。macOS 环境搭建# 1. 安装 pythonmacOS 10.9 自带 python2.7若缺失可用 Homebrew brew install python # 2. 安装 Python 依赖 sudo easy_install pip3 sudo pip3 install PyYAML5.4.1 Cheetah3 # 3. 下载 NDK r21 # 4.可选若 python 为自定义安装复制 user.cfg.sample 并改名为 user.cfg # 在其中设置 PYTHON_BIN 为 python 的绝对路径 # 5. 运行测试 export NDK_ROOT/path/to/android-ndk-r21 ./test.shtest.sh会生成一个userconf.ini请检查其中的值是否正确、有无报错。Windows 7 64bit 环境搭建# 1. 下载 64 位 python3 # 2. 将 python 安装路径如 C:\Python39加入 PATH 环境变量 # 3. 安装 Python 依赖 python -m pip install PyYAML5.4.1 Cheetah3 # 4. 下载 NDK r21 或更高版本 # 5. 设置环境变量 PYTHON_ROOT 和 NDK_ROOT也可直接在 test.bat 中填写 # 6. 运行 test.bat生成的代码位于 simple_test_bindings 目录预期输出运行测试后可能出现一些 warning但不应出现 error。测试会创建一个名为simple_test_bindings的目录内含 3 个文件文件作用.hpp头文件绑定类的头文件.cpp实现文件绑定类的实现.js文档文件说明如何从 JavaScript 调用该 C 类暴露的方法需要说明README 中提到的test.sh、test.bat、user.cfg.sample属于文档描述的历史版本遗留物在当前仓库快照的bindings-generator/目录中并不存在。在当前 Cocos 引擎仓库中真正的入口是 native/tools/tojs/genbindings.py它会自动完成userconf.ini的生成与生成器调用详见下文与引擎 JSB 流程的集成一节。.ini配置文件详解.ini是一个简单文本文件用来描述代码生成器的设置。README 给出了 cocos2d-x 时代使用的默认示例[cocos2d-x] prefix cocos2dx events CCNode#onEnter CCNode#onExit extra_arguments -I../../cocos2dx/include -I../../cocos2dx/platform -I../../cocos2dx/platform/ios -I../../cocos2dx -I../../cocos2dx/kazmath/include -arch i386 -DTARGET_OS_IPHONE -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk -x c headers ../../cocos2dx/include/cocos2d.h classes CCSprite functions my_free_function必需配置项配置项含义关键约束prefix项目前缀必须是目标 VM 语言的合法标识符。大多数情况下会与类名、函数名交错拼接因为生成的方法基本都是自由函数这样做可避免命名冲突。生成结果文件名为${prefix}.cpp与${prefix}.hpp必填events形如ClassName#functionName的标识符列表表示从原生世界回调到目标 VM 的事件必填extra_arguments传给 clang 接口的额外参数可理解为传给编译器的参数。若目标是 C务必以-x c结尾来强制以 C 模式解析.h文件否则请把头文件命名为.hpp必填headers需要解析的头文件列表。通常只添加一个头文件由它#include其余所有文件必填classes要解析的类目前只是字符串但支持正则表达式必填functions要绑定的自由函数列表空格分隔与classes一样支持正则表达式必填skip空格分隔的Classes::functions或functions列表表示不为其生成任何代码可选从源码补充的更多配置项对照 generator.py#L2351-L2384 的gen_opts构造实际支持的配置项远比 README 列出的丰富这里补充几个高频项及其底层行为remove_prefix对类名做正则替换去除指定前缀后再注册到脚本层Generator.__init__与NativeClass中均有使用见 generator.py#L1595target_namespace/cpp_namespace脚本命名空间与 C 命名空间映射cpp_namespace用于限定只导出特定 C 命名空间下的类generator.py#L2104-L2109abstract_classes、persistent_classes、classes_owned_by_cpp抽象类、持久类、由 C 持有的类清单影响构造函数与析构的绑定方式getter_setter以ClassName::field1/getter/setter形式声明将某字段以属性getter/setter方式导出到脚本层未指定时默认用getXxx/setXxx命名generator.py#L1747-L1788skip_public_fields、field分别控制跳过与强制绑定的公开字段obtain_return_value标记哪些方法返回值需要以获取方式处理rename_functions/rename_classes/replace_headers方法重命名、类重命名、头文件替换class_module_configs/method_module_configs为类/方法挂接宏判断macro_judgement用于按编译宏裁剪绑定hpp_headers/cpp_headers/win32_clang_flags补充的 C 头文件与 Windows 平台额外的 clang 参数。这些配置项的解析逻辑大多采用ClassName::[item1 item2 ...]的语法例如skip Node::[removeFromParent removeAllChildren]并通过正则拆分实现细节可查阅 generator.py#L1653-L1788。生成器内部实现源码级工作流整个生成流程可以划分为五个阶段全部体现在 generator.py 中。阶段一配置解析与目标加载main()读取.ini通过configparser按-s指定的 section不指定则处理全部 section逐 section 构造gen_opts字典并逐一实例化Generator调用generate_code()generator.py#L2349-L2386。Generator.__init__generator.py#L1576会做大量预处理其中包括自动补全 clang include 路径对每个-I参数若路径不存在则尝试在该目录的 clang 版本子目录中查找include并追加Windows 平台还会附加win32_clang_flags。阶段二生成元信息文件并调用 clang 解析generate_code()generator.py#L1978首先读取targets/${target}/conversions.yaml作为self.config随后按输出主名打开 4 个输出文件.cpp、.h、.json、.inl并写入模板layout_head.h/.c。接着_parse_headers()generator.py#L2061把配置中的每个头文件以#include ...形式写入临时文件batch_input.h然后调用tu self.index.parse(header, self.clang_args)即用cindex.Index对整个批量头文件做一次统一解析得到 TranslationUnit。解析产生的诊断信息通过_pretty_print()按严重级别输出一旦出现 Error/Fatal 级别错误就抛异常终止generator.py#L2047-L2081。阶段三AST 遍历与模型构建_deep_iterate()generator.py#L2084递归遍历 AST cursor遇到CLASS_DECL/STRUCT_DECL且匹配classes正则同时受cpp_ns约束时构建NativeClass并调用generate_code()遇到ENUM_DECL且精确匹配时构建NativeEnum。NativeClass.parse()内部通过_process_node()generator.py#L1418处理各类型节点核心逻辑包括基类递归构建父类NativeClass用于继承方法分析与RefCount引用类判定is_ref_class公开方法过滤private/protected与DEPRECATED通过get_availability检查 clang availability 属性并跳过可变参数函数cursor.type.is_function_variadic()方法重载同名方法被归并进NativeOverloadedFunction容器generator.py#L980。值得注意的是README 指出当前 SpiderMonkey 实现对重载的支持仅适用于参数个数不同的重载构造函数跳过拷贝构造ClassName(const Class )形态其余构造进入methods[constructor]同样支持重载公开字段struct 的公开字段默认导出class 的公开字段按field/skip_public_fields配置决定。NativeFunctiongenerator.py#L841负责解析函数签名逐个参数转换为NativeType若任一参数类型不支持not_supported则整函数不导出还会通过遍历参数 AST 子节点default_arg_type_arr涵盖整型/浮点/字符串/字符/布尔/空指针/声明引用等字面量见 generator.py#L59-L87检测默认参数从而计算min_args最少可调用参数个数供脚本层做参数个数校验。阶段四类型归一化NativeTypeNativeType.from_type()generator.py#L469递归处理 C 类型修饰POINTERT*、LVALUEREFERENCET、RVALUEREFERENCET分别递归展开并设置is_pointer/is_reference/is_rreference标志基础类型通过 type_mapgenerator.py#L28-L53映射为 C 原生类型如INT → int、LONGLONG → int64_tstd::string、std::function被特殊识别前者归一化为std::string后者解析出返回类型与参数列表标记为函数类型STL 容器std::vector、std::map、std::unordered_map、std::set等通过 stl_type_mapgenerator.py#L89-L124记录模板参数个数并由normalize_type_str做归一化其中 map 类容器允许 2 个模板参数、序列容器 1 个常量数组被归一化为std::arrayT, N无法识别的类型标记为INVALID_NATIVE_TYPE??从而把函数标记为不支持而跳过。阶段五模板渲染与文件收尾每个NativeClass.generate_code()generator.py#L1294依次写入prelude.h/.c头尾、各方法含重载的声明与实现、公开字段、register.c注册段并把类的 JSON 描述追加进class_json_list。generate_code()最后写入layout_foot.h/.c将类的 JSON 列表序列化到${out_file}.json并通过inplace_change()把.h文件中的占位符// placeholder for jsb_register_types替换为收集到的reg_types.h内容generator.py#L2034。由此可以理解最终产物.h是绑定类声明与注册宏占位.cpp是全部绑定函数实现与注册入口.json是面向文档生成的类/方法/字段结构化描述.inl是类型注册片段。模板系统与 conversions.yaml生成器采用Cheetah 模板来保持灵活性。设计思路是对于每个目标环境都提供一套生成相同 C/C 功能的模板每个模板都可以访问代码/生成器的元信息函数、类等。模板存放在templates/${target}/目录当前为 native/tools/bindings-generator/targets/spidermonkey/templates/。模板分类来自 README结合仓库文件印证模板文件用途prelude.c/prelude.h生成文件的头部ifunction.c/ifunction.h实例函数的模板ifunction_overloaded.c重载实例函数实现模板。重载函数与普通函数相同但内部有一个共享同名函数的数组当前 SpiderMonkey 实现仅支持参数个数不同的重载sfunction.c/sfunction.h静态函数模板sfunction_overloaded.c重载静态函数模板register.c构造/析构、注册函数与头文件尾部是最后生成的代码块除 README 列出的核心模板外仓库中还有constructor.c/constructor_overloaded.c构造函数、ctor.c/ctor_overloaded.c脚本侧构造、enum.c枚举注册、lambda.cstd::function参数转换、struct_constructor.cstruct 构造、public_field.c/public_static_field.c字段、layout_head/foot、reg_types.h、apidoc_*API 文档等共同构成完整的绑定生成体系。模板中的函数命名规范定义在conversions.yaml的definitions段native/tools/bindings-generator/targets/spidermonkey/conversions.yamldefinitions: ifunction: js_${generator.prefix}_${class_name}_${func_name} sfunction: js_${generator.prefix}_${class_name}_${func_name}_static constructor: js_${generator.prefix}_${class_name}_constructor ctor: js_${generator.prefix}_${class_name}_ctor public_field: js_${generator.prefix}_${class_name}即实例方法导出名为js_prefix_ClassName_funcName静态方法追加_static后缀构造函数为js_prefix_ClassName_constructor。这也呼应了.ini中prefix必须合法且唯一的初衷——所有绑定函数都以它作前缀以避免冲突。${target}.yaml类型转换片段README 指出${target}.yaml即conversions.yaml是整个机制的最后一块拼图它包含供模板使用的类型转换代码片段。以 SpiderMonkey 为例这里定义了原生类型与 JS 值互转的例程。该文件主要分四部分definitions上文提到的导出函数命名模板native_types特殊原生类型映射例如 SpiderMonkey 中bool实际是整数所以short → int16_t、unsigned char → uint8_t、char → int8_t、long long → int64_t避免直接传bool地址导致转换失败ns_mapC 命名空间到脚本命名空间的映射如cc:: → jsb.、cc::ui:: → ccui.、cc::gfx:: → gfx.、se:: → jsb.、spine:: → sp.等to_native/from_nativeJS 值到原生值、原生值到 JS 值的转换代码模板。to_native中可看到大量seval_to_*例程例如int先转int32_t再窄化、char*先转std::string再取c_str()、Vec2/Vec3/Mat4/Size/Color3B/Color4B/Color4F等 Cocos 值类型都有专门转换函数键名支持前缀的正则匹配如Vector.*、mapstd::string.*,\s*std::string.*NativeType.dict_has_key_re/dict_get_value_re等辅助方法generator.py#L621-L660正是为支持这类正则键而存在的。正是这套模板 yaml 转换片段的架构让生成器能够用同一套 AST 解析逻辑服务不同的脚本引擎——理论上只需新增targets/new-vm/templates/与conversions.yaml即可为新的 VM 生成绑定。与引擎 JSB 流程的集成tojs 调用链虽然生成器本身是通用工具但在 Cocos 引擎中它是通过 native/tools/tojs/genbindings.py 接入的。该脚本的关键行为环境检测从ANDROID_NDK_HOME或NDK_ROOT环境变量获取 NDK 路径从PYTHON_BIN未设置则用当前解释器获取 Python 路径genbindings.py#L21-L47工具链探测按平台自动定位 NDK 内toolchains/llvm/prebuilt/platform-x86_64的 LLVM 路径与 GCC 交叉工具链genbindings.py#L96-L126生成userconf.ini把androidndkdir、clangllvmdir、gcc_toolchain_dir、cocosdir、cxxgeneratordir即tools/bindings-generator写入native/tools/tojs/userconf.inigenbindings.py#L132-L144——这正是generator.py读取的配置文件设置动态库路径Linux/macOS 下将bindings-generator/libclang加入LD_LIBRARY_PATHWindows 下加入PATHgenbindings.py#L148-L153调用生成器对每个 section 执行python generator.py xxx.ini -s section -t spidermonkey -o outdir -n jsb_xxx_auto默认输出到native/cocos/bindings/autogenbindings.py#L163-L185。完整的 JSB 接入流程创建 ts/cpp 类、配置.ini与 CMakeLists、在jsb_module_register.cpp注册register_all_xxx、在cc.config.json的moduleOverrides/NATIVE分组登记native_xxx.jsb.ts等在 native/tools/tojs/README.mdown 中有逐步图文说明可以作为本文的延伸阅读。已知限制README 明确说明生成器依赖 clang 获取 C/C 代码信息因此只能获得 clang 能提供的信息。已知不支持的场景是可变参数函数variable number of arguments生成器会在解析阶段直接跳过见_process_node中not cursor.type.is_function_variadic()的过滤解决方案是手写一个包装函数再进行绑定。除此之外从源码还可以推断出另一些边界重载函数仅支持参数个数不同的情形SpiderMonkey 目标无法识别的类型INVALID_NATIVE_TYPE会导致包含该类型的函数整体不被导出。小结bindings-generator是一套成熟、自洽的Clang AST → 类型模型 → 模板渲染三层流水线.ini决定导出边界conversions.yaml提供类型转换规则Cheetah 模板决定输出形态。理解这套机制无论你是想继续使用它为自己的 C 模块生成 JS 绑定还是想看懂 Cocos 引擎 JSB 绑定代码的来龙去脉都极具参考价值。相关可继续研读的文件包括generator.py核心实现、conversions.yaml转换规则、spidermonkey 模板目录输出形态以及 genbindings.py引擎侧调用入口。【免费下载链接】cocos-engineCocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performance, engaging 2D/3D games and instant web entertainment.项目地址: https://gitcode.com/GitHub_Trending/co/cocos-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻