|
| 1 | +# 收录 godot-cpp:compat.godot-cpp(本 PR)与模块层 godotengine.godot-cpp(后续 PR) |
| 2 | + |
| 3 | +日期:2026-08-04 |
| 4 | +上游:<https://github.com/godotengine/godot-cpp> · 版本 `godot-4.5-stable` → 索引版本 `4.5.0` |
| 5 | + |
| 6 | +## 1. 形态判定 |
| 7 | + |
| 8 | +godot-cpp 是 Godot GDExtension 的 C++ 绑定库。表面上属于「C++ 源码 compat」,但它有一个决定性特征: |
| 9 | + |
| 10 | +**上游 tag 归档只是半棵源码树。** 另一半 —— 约 1000 个引擎类与全部 builtin Variant 类型,即 |
| 11 | +`gen/include/` 与 `gen/src/` —— 由上游自带的 `binding_generator.py` 从 |
| 12 | +`gdextension/extension_api.json` 在构建时生成,任何上游 tag 归档或 release 都不含它: |
| 13 | + |
| 14 | +``` |
| 15 | +上游归档: include/ 67 个 .hpp src/ 32 个 .cpp |
| 16 | +生成之后: +1010 个 .hpp +990 个 .cpp (共 18 MB) |
| 17 | +``` |
| 18 | + |
| 19 | +于是形态判定的真正问题不是模板选哪个,而是**这一步 codegen 在哪里跑**。三个选项: |
| 20 | + |
| 21 | +| 方案 | 结论 | |
| 22 | +|---|---| |
| 23 | +| 消费侧 `install()` 钩子跑 python | 否决。会让 Python 成为每个消费者、每个平台的硬依赖,与本索引既有做法相悖(compat.ffmpeg 的 config 快照、opencv 包的 gen/ 快照都是「消费侧不跑 codegen」) | |
| 24 | +| 把 gen/ 塞进 `generated_files` | 否决。18 MB 文本进描述符 | |
| 25 | +| **离线跑一次,产物随镜像归档发布** | **采用** | |
| 26 | + |
| 27 | +因此下载地址不是上游归档,而是 `xlings-res/godot-cpp` 的重打包归档 —— 与 |
| 28 | +[[windows-symlink-archives]] 里 asio 的 repack 惯例同源,只是这次加的是 `gen/` 而不是去符号链接。 |
| 29 | + |
| 30 | +`tools/godot-cpp/repack.sh` 是该归档的唯一来源与验证器: |
| 31 | + |
| 32 | +1. 拉上游 tag 归档(`ac78539c…e339c`); |
| 33 | +2. 跑**未修改**的上游 `binding_generator.py`(默认配置:64 位、`precision=single`、`template_get_node` 开); |
| 34 | +3. 把重新解包的上游树与生成后的树做 `diff -r`,**除 `gen/` 之外任何差异即拒绝打包**; |
| 35 | +4. 确定性打包(`--sort=name`、固定 mtime、`gzip -n`)。 |
| 36 | + |
| 37 | +连跑两次 sha256 相同:`b0c36e77f02c4181352cdd7547b209b93a833be1ad6197f8c650d92987221a00`。 |
| 38 | + |
| 39 | +### 版本号 |
| 40 | + |
| 41 | +上游 tag 是 `godot-4.5-stable`,索引版本取裸版本 `4.5.0`(lint 拦前导 `v`,且给 4.5.x 留位)。 |
| 42 | + |
| 43 | +## 2. 镜像 |
| 44 | + |
| 45 | +| 区域 | 地址 | |
| 46 | +|---|---| |
| 47 | +| GLOBAL | `https://github.com/xlings-res/godot-cpp/releases/download/4.5.0/godot-cpp-4.5.0.tar.gz` | |
| 48 | +| CN | `https://gitcode.com/mcpp-res/godot-cpp/releases/download/4.5.0/godot-cpp-4.5.0.tar.gz` | |
| 49 | + |
| 50 | +两侧 sha256 一致,均等于本地打包结果(下载回来逐一核过)。仓库 README 记录了复现方式与上游归档 sha。 |
| 51 | + |
| 52 | +三平台共用同一份 OS 中立归档:godot-cpp 是可移植 C++,没有按平台切换的源码集合(平台差异在 Godot 本体里, |
| 53 | +被 `gdextension_interface.h` 这层 C ABI 挡住了)。 |
| 54 | + |
| 55 | +## 3. 描述符要点 |
| 56 | + |
| 57 | +- `include_dirs = { "*/include", "*/gen/include", "*/gdextension" }` —— 与上游 cmake 暴露的三个根一致。 |
| 58 | +- `sources` 逐层枚举(`*/src/*.cpp`、`*/src/*/*.cpp`、`*/gen/src/*/*.cpp`)而非 `**`:上游自带的 |
| 59 | + `test/src/*.cpp` 是它自己的示例扩展,不能被扫进库里。 |
| 60 | +- 目标名 `godot-cpp`,kind = lib,共 1022 个 TU。 |
| 61 | +- `src/core/object.cpp` 与 `gen/src/classes/object.cpp` **basename 撞名**。mcpp ≥ 0.0.98 的 obj 路径消歧 |
| 62 | + (mcpp#233/#240)已覆盖这种同包内撞名,本地实测链接正常;这是本包对客户端版本下限的隐含依赖, |
| 63 | + index.toml 现有 floor 远高于它。 |
| 64 | + |
| 65 | +### define 与 feature 评估 |
| 66 | + |
| 67 | +`GDEXTENSION` 是上游 cmake 挂在 target INTERFACE 上的 **PUBLIC** define,库与消费者 TU 必须一致, |
| 68 | +所以走 `default = { implies = { "gdextension" } }`(feature 的 `defines` 才到得了消费端,而 |
| 69 | +`default.implies` 无条件生效 —— 与 compat.curl 的 `CURL_STATICLIB` 同一解法)。 |
| 70 | + |
| 71 | +以下 define **有意不做成 feature**: |
| 72 | + |
| 73 | +- `DEBUG_ENABLED` / `DEV_ENABLED`:额外检查,上游 release 默认关。 |
| 74 | +- `HOT_RELOAD_ENABLED`:会改 `Wrapped` 的布局 —— 属于 ABI,不是开关。 |
| 75 | +- `REAL_T_IS_DOUBLE`:需要用 `precision=double` 重新生成的另一棵 `gen/` 树,归档里没有, |
| 76 | + 所以它根本不可能是本包的 feature;真要支持是另一个版本/包。 |
| 77 | + |
| 78 | +共同理由:每个都会把 store 重新 key 一次,等于把同一个库的 1000 TU 再全量编一遍。 |
| 79 | + |
| 80 | +## 4. 测试成员 |
| 81 | + |
| 82 | +`tests/examples/godot-cpp/`(根 `[indices] compat` 继承,成员不再声明)。断言分两类,都能真失败: |
| 83 | + |
| 84 | +- **链接类**:`Vector2::length()`、`Basis::orthonormalized()`、`Color::to_rgba32()`、`AABB::get_volume()` |
| 85 | + 在头里只有声明,定义在 `src/variant/*.cpp` —— 跑通即证明这 1022 个 TU 真的编了并链进来了 |
| 86 | + (对照 [[verify-obj-count-not-green-ci]]:绿 CI 不等于包被编译)。 |
| 87 | +- **生成绑定类**:`<godot_cpp/classes/node.hpp>`、`Node::PROCESS_MODE_*`、`godot::OK`、 |
| 88 | + `godot::ERR_FILE_NOT_FOUND`、`Variant::OBJECT` —— 这些只存在于 `gen/`,缺了就编不过。 |
| 89 | + |
| 90 | +**不能测什么**:`String`/`Array` 等一切要走 `gdextension_interface_*` 函数指针的 API,以及类注册 |
| 91 | +(`GDREGISTER_CLASS`),都需要一个已加载该扩展的 Godot 进程。纯数学那半边不需要,所以断言全落在那里。 |
| 92 | + |
| 93 | +## 5. 本地验证 |
| 94 | + |
| 95 | +与 CI 同版本(`.github/workflows/validate.yml` 的 `MCPP_VERSION = 2026.8.3.3`),冷验证: |
| 96 | + |
| 97 | +``` |
| 98 | +$ mcpp test -p godot-cpp |
| 99 | + Compiling compat.godot-cpp v4.5.0 |
| 100 | + Running bin/godot_cpp |
| 101 | +vec2=1 vec3=1 basis=1 color=1 aabb=1 gen=1 |
| 102 | +godot_cpp ... ok (0.12s) |
| 103 | + test result ok. 1 passed; 0 failed; finished in 106.54s (build 55.60s + run 0.02s) |
| 104 | +``` |
| 105 | + |
| 106 | +另外用 gcc 13 单独全量编过一遍 1022 个 TU(`-std=c++23`,零告警失败),并把全部 .o 与测试 main |
| 107 | +直接链接跑通 —— 用来提前确认「依赖 .o 全量入链」下没有未解析符号(不需要 `-ldl`/`-lpthread`)。 |
| 108 | +CPU 时间约 16 分钟,4 核 runner 上折合 4~5 分钟。 |
| 109 | + |
| 110 | +## 6. 后续:模块层 godotengine.godot-cpp |
| 111 | + |
| 112 | +模块包**不**放在本索引里内联(避免索引变重,也避免把 1000 TU 再编一遍):按 Form A 走外部仓 |
| 113 | +`mcpplibs/godot-cpp-m`,其 `mcpp.toml` 依赖 `compat.godot-cpp`,提供 `import godot_cpp;` |
| 114 | +(单段模块名,与 `#include <godot_cpp/...>` 和库名对应;点号在本索引里留给子模块,如 `opencv.cv`)。 |
| 115 | + |
| 116 | +**顺序是硬约束**:模块成员的测试工程只能声明一条 `[indices]`,给 `godotengine`;它的传递依赖 |
| 117 | +`compat.godot-cpp` 于是从**已发布**的远端索引解析(tests/examples/ffmpeg-module 的注释写的就是这件事)。 |
| 118 | +所以必须先合并本 PR、`publish-artifact` 重新发布 artifact,第二个 PR 的 CI 才可能绿。 |
| 119 | + |
| 120 | +模块 wrapper 的生成方式已验证可行:1077 个头在单个 TU 里全部编过只要 3.5 秒 / 728 MB RSS, |
| 121 | +按命名空间作用域声明批量产出 `export using ::godot::X;` 即可。**宏不在其中** —— `GDCLASS`、 |
| 122 | +`GDREGISTER_CLASS`、`memnew`、`ERR_*`、`GDVIRTUAL_*` 是预处理器构造,模块带不走,做类注册的 TU |
| 123 | +仍需 `#include` 对应头;这一条要在 godot-cpp-m 的 README 与描述符注释里写明。 |
0 commit comments