From dd74694572a0f5b9491ad63e25fb20c83eb5eb37 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Tue, 4 Aug 2026 04:26:48 +0800 Subject: [PATCH 1/4] feat: add godotengine.godot-cpp 0.0.1 (import godot_cpp) Form A registration of mcpplibs/godot-cpp-m: the C++23 module layer over godot-cpp's unchanged API. `import godot_cpp;` re-exports the whole public `godot` namespace, so a file that opened with a stack of The index stays light: the 1022-TU source build lives in compat.godot-cpp and the module layer lives in its own repo, so this side carries only the descriptor -- the same split ffmpeg/opencv use. Module name is one segment, `godot_cpp`, matching the library and the reserved for packages exposing several submodules (opencv.cv, ffmpeg.av). Two things the module deliberately does not carry, both upstream's shape rather than a wrapper choice: * MACROS -- GDCLASS, GDREGISTER_CLASS, GDVIRTUAL_*, D_METHOD, memnew and the ERR_* family. A named module cannot export them, and GDExtension code is written in them, so the package ships to include next to the import. godot-cpp's headers are in the module's global module fragment, so both spellings denote the same entities. * HashMap/HashSet and their default hashers, which reach hash_murmur3_one_float/double -- `static` functions declaring an unnamed union. Exposing a TU-local TYPE from a module interface is a hard error, not the -Wexpose-global-module-tu-local warning. The member consumes the package the way a user would and asserts the same numbers tests/examples/godot-cpp asserts through headers, so a divergence means the re-export changed behaviour. Note on ordering: this member declares [indices] godotengine, and a member-level table REPLACES the root's, so the transitive compat.godot-cpp resolves from the PUBLISHED index -- the constraint ffmpeg-module documents. It therefore only goes green after the compat.godot-cpp PR merges and publish-artifact reruns. Simulating a published index locally does not work: mcpp refreshes on a resolution miss and wipes the injected state. --- .agents/docs/2026-08-04-add-godot-cpp-plan.md | 34 ++++++++- README.md | 1 + README.zh-CN.md | 1 + mcpp.toml | 1 + pkgs/g/godotengine.godot-cpp.lua | 75 +++++++++++++++++++ tests/examples/godot-cpp-module/mcpp.toml | 24 ++++++ .../tests/godot_cpp_module.cpp | 69 +++++++++++++++++ 7 files changed, 201 insertions(+), 4 deletions(-) create mode 100644 pkgs/g/godotengine.godot-cpp.lua create mode 100644 tests/examples/godot-cpp-module/mcpp.toml create mode 100644 tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp diff --git a/.agents/docs/2026-08-04-add-godot-cpp-plan.md b/.agents/docs/2026-08-04-add-godot-cpp-plan.md index 8f50646..be4a216 100644 --- a/.agents/docs/2026-08-04-add-godot-cpp-plan.md +++ b/.agents/docs/2026-08-04-add-godot-cpp-plan.md @@ -126,7 +126,33 @@ CPU 时间约 16 分钟,4 核 runner 上折合 4~5 分钟。 `compat.godot-cpp` 于是从**已发布**的远端索引解析(tests/examples/ffmpeg-module 的注释写的就是这件事)。 所以必须先合并本 PR、`publish-artifact` 重新发布 artifact,第二个 PR 的 CI 才可能绿。 -模块 wrapper 的生成方式已验证可行:1077 个头在单个 TU 里全部编过只要 3.5 秒 / 728 MB RSS, -按命名空间作用域声明批量产出 `export using ::godot::X;` 即可。**宏不在其中** —— `GDCLASS`、 -`GDREGISTER_CLASS`、`memnew`、`ERR_*`、`GDVIRTUAL_*` 是预处理器构造,模块带不走,做类注册的 TU -仍需 `#include` 对应头;这一条要在 godot-cpp-m 的 README 与描述符注释里写明。 +### 已完成(mcpplibs/godot-cpp-m 0.0.1) + +`src/godot_cpp.cppm` 由 `tools/gen_module_cppm.py` **生成**:扫描头文件(带花括号/命名空间状态机), +把 `godot` 命名空间作用域的全部声明批量产出为 `export using ::godot::X;`,约 1750 个名字 —— +全部引擎类、全部 builtin Variant 类型、全局枚举**连同枚举量**(所以 `godot::OK` 拼法不变)、 +`godot::Math`、模板。手工罗列这个面积不现实,且每个 Godot 版本都会变,故生成 + 编译器校验。 + +踩到的两类坑: + +- **宏带不走**。`GDCLASS`、`GDREGISTER_CLASS`、`GDVIRTUAL_*`、`D_METHOD`、`memnew`、`ERR_*` 是预处理器 + 构造,而 GDExtension 代码正是用它们写的。解法照搬 ffmpeg-m:附一个侧头文件 + ``,与 import 并排包含。godot-cpp 的头在模块的 GMF 里,两种拼法指向同一批 + global-module 实体,混用是良构的 —— `tests/godot_cpp_macros.cpp` 就是这个形状,通过。 +- **TU-local 暴露是硬错误,不是告警**。`HashMap`/`HashSet` 及其默认 hasher 的内联体会走到 + `hash_murmur3_one_float/double`,这两个既是 `static`,体内又声明了匿名 union;从模块接口暴露一个 + TU-local **类型**是 error(不是 `-Wexpose-global-module-tu-local` 那条 warning)。用二分法在 1759 个 + 导出名里定位到 7 个(含 `PairHash`),排除即可 —— 它们是 godot-cpp 内部容器,扩展代码用 + Dictionary/Array/TypedArray,头文件里也仍然拿得到。另外那 3300 条 warning 级暴露用 + `-Wno-expose-global-module-tu-local` 静掉,是上游头的形态,包一层改变不了。 + +验证:`mcpp test` 两条全过(模块面 + 宏面),`examples/summator`(真 GDExtension,`kind = "shared"`) +构建出 `libsummator.so` 且导出 `summator_library_init`。 + +### 本 PR(索引侧)可验证到哪一步 + +`godotengine.godot-cpp` 是 Form A,索引侧只有描述符;成员的 `[indices]` 给了 `godotengine`, +所以传递依赖 `compat.godot-cpp` 必须从**已发布**的索引解析。合并前本地伪造 published index 来验证 +是走不通的:mcpp 在解析不到时会主动 refresh 一次,把注入的状态直接冲掉(参见 +[[stale-global-index-masks-descriptor-bugs]] 的反面)。因此这一步的唯一真实验证就是 +compat PR 合并 + artifact 重新发布之后的 CI。 diff --git a/README.md b/README.md index db0f245..8a24593 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Two kinds of packages live here: | Multiple majors in one package (shape switches with the version) | [`compat.catch2`](pkgs/c/compat.catch2.lua) (3.x compiles `src/catch2/` into a static library; 2.x goes header-only through `single_include/`) | | External build system (`install()` builds from source) | [`compat.openblas`](pkgs/c/compat.openblas.lua) (Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua) (Perl Configure + Make, static libssl/libcrypto) | | Whole-source direct build (config snapshot + source list, no external build system) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua) (2281 TUs including NASM assembly, declared through 28 directory globs) | +| Module layer over a compat source build (external Form-A repo) | [`godotengine.godot-cpp`](pkgs/g/godotengine.godot-cpp.lua) (`import godot_cpp;` re-exports the whole `godot` namespace, ~1750 names GENERATED from the headers rather than curated; the 1022-TU build stays in `compat.godot-cpp`, so the index carries only this descriptor. Macros — `GDCLASS`, `GDREGISTER_CLASS`, `memnew`, `ERR_*` — are the one thing a named module cannot export, so the package ships a side header to include next to the import) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua) (upstream's own `include/boost/ut.cppm` reproduced verbatim but for one `__argc`/`__argv` shim that Clang-on-MSVC needs; namespace `boost-ext` since it is NOT an official Boost library) | ### Adding a package diff --git a/README.zh-CN.md b/README.zh-CN.md index 907e957..b575506 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -48,6 +48,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 | 单包多 major(形态随版本切换) | [`compat.catch2`](pkgs/c/compat.catch2.lua)(3.x 编 `src/catch2/` 出静态库;2.x 走 `single_include/` header-only) | | 外部构建系统(`install()` 从源码构建) | [`compat.openblas`](pkgs/c/compat.openblas.lua)(Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua)(Perl Configure + Make,静态 libssl/libcrypto) | | 全源码直编(config 快照 + 源列表,零外部构建系统) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua)(2281 TU 含 NASM 汇编,28 个目录 glob 声明) | +| 模块层叠在 compat 源码构建之上(外部 Form-A 仓) | [`godotengine.godot-cpp`](pkgs/g/godotengine.godot-cpp.lua)(`import godot_cpp;` 重导出整个 `godot` 命名空间,约 1750 个名字由头文件**生成**而非手工罗列;1022 个 TU 的构建留在 `compat.godot-cpp`,索引侧只留这一个描述符。宏 —— `GDCLASS`、`GDREGISTER_CLASS`、`memnew`、`ERR_*` —— 是具名模块唯一带不走的东西,故包内附一个与 import 并排包含的侧头文件) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua)(逐字复用上游自带的 `include/boost/ut.cppm`,仅加一处 Clang-on-MSVC 需要的 `__argc`/`__argv` shim;命名空间取 `boost-ext`,因其并非 boost 官方库) | ### 新增一个包 diff --git a/mcpp.toml b/mcpp.toml index e67aabf..14b9096 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -30,6 +30,7 @@ members = [ "tests/examples/ffmpeg-module", "tests/examples/fmtlib.fmt", "tests/examples/godot-cpp", + "tests/examples/godot-cpp-module", "tests/examples/godot-cpp-v10", "tests/examples/gui-stack", "tests/examples/imgui", diff --git a/pkgs/g/godotengine.godot-cpp.lua b/pkgs/g/godotengine.godot-cpp.lua new file mode 100644 index 0000000..15d7b0a --- /dev/null +++ b/pkgs/g/godotengine.godot-cpp.lua @@ -0,0 +1,75 @@ +-- Form A descriptor: the godot-cpp module package ships its own mcpp.toml. +-- mcpp's default lookup finds /*/mcpp.toml inside the GitHub source +-- tarball wrap. +-- +-- The package is the thin C++23 module layer over godot-cpp's unchanged C++ +-- API: `import godot_cpp;` re-exports the whole public `godot` namespace, so +-- code that used to open with a stack of `#include ` opens with +-- one import and is otherwise the same. godot-cpp's sources and its +-- pre-generated GDExtension bindings arrive through the package's own +-- compat.godot-cpp dependency (1022-TU source build -- see +-- pkgs/c/compat.godot-cpp.lua). +-- +-- Module name: ONE segment, `godot_cpp`, matching the library and the +-- `#include ` root users already type. The dotted spelling is +-- reserved here for packages that expose several submodules (opencv.cv, +-- ffmpeg.av); this one has a single interface unit. +-- +-- What the module cannot carry: MACROS. GDCLASS, GDREGISTER_CLASS, +-- GDVIRTUAL_*, D_METHOD, memnew/memdelete and the ERR_* family are +-- preprocessor constructs, and GDExtension code is written in them. The +-- package ships `` for exactly that, to be included +-- next to the import; godot-cpp's headers sit in the module's global module +-- fragment, so the two spellings denote the same entities and mixing them is +-- well-formed. The package's own tests cover both shapes. +-- +-- Also not re-exported (upstream's shape, not a wrapper choice): the internal +-- container templates HashMap/HashSet and their default hashers. Their inline +-- bodies reach `hash_murmur3_one_float/double`, which are `static` AND declare +-- an unnamed union -- and exposing a TU-local TYPE from a module interface is +-- a hard error, not the -Wexpose-global-module-tu-local warning. Extension +-- code uses Dictionary/Array/TypedArray; the headers still have the rest. +-- +-- Three platforms, one OS-neutral tarball: godot-cpp is portable C++ and +-- compat.godot-cpp covers all three. +package = { + spec = "1", + name = "godot-cpp", + namespace = "godotengine", + description = "C++23 module package for godot-cpp (import godot_cpp) — Godot GDExtension API, C++ API unchanged", + licenses = {"MIT"}, -- module layer; upstream godot-cpp is MIT as well + repo = "https://github.com/mcpplibs/godot-cpp-m", + type = "package", + + xpm = { + linux = { + ["0.0.1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + }, + sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + }, + }, + macosx = { + ["0.0.1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + }, + sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + }, + }, + windows = { + ["0.0.1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + }, + sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + }, + }, + }, + + -- (no `mcpp` field -- default lookup will find /*/mcpp.toml) +} diff --git a/tests/examples/godot-cpp-module/mcpp.toml b/tests/examples/godot-cpp-module/mcpp.toml new file mode 100644 index 0000000..8e4650e --- /dev/null +++ b/tests/examples/godot-cpp-module/mcpp.toml @@ -0,0 +1,24 @@ +# godotengine.godot-cpp (module layer) test project: consumes `import godot_cpp;` +# and asserts the same numbers the compat.godot-cpp member asserts through +# headers -- if the module re-export changed behaviour, these would move. +# +# Overrides the workspace-root redirect: root declares `compat`, this member +# needs `godotengine`. A member-level [indices] REPLACES the inherited table +# rather than merging with it, which is what keeps this to ONE project index +# repo -- two repos pointing at the same tree make every lookup ambiguous. +# +# The module package's own transitive dep (compat.godot-cpp) therefore resolves +# from the GLOBAL published index, not from this checkout; the compat +# descriptor is pre-merge-validated by its own member (tests/examples/godot-cpp). +[indices] +godotengine = { path = "../../.." } + +[package] +name = "godot-cpp-module-tests" +version = "0.1.0" + +# Dotted spelling on purpose: CI maps a changed pkgs//.lua to the +# members whose mcpp.toml mentions , and here is the whole +# `godotengine.godot-cpp`. +[dependencies] +godotengine.godot-cpp = "0.0.1" diff --git a/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp b/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp new file mode 100644 index 0000000..bb4c880 --- /dev/null +++ b/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp @@ -0,0 +1,69 @@ +// Behavioral test for the godotengine.godot-cpp module package: godot-cpp's +// API reached with `import godot_cpp;` and no #include at all. +// +// The numbers are deliberately the same ones tests/examples/godot-cpp asserts +// through headers -- that member is the header spelling of this one, so a +// divergence here means the module re-export changed behaviour. +// +// Both halves can fail: +// * Vector2::length(), Basis::orthonormalized(), Color::to_rgba32() and +// AABB::get_volume() are DEFINED in compat.godot-cpp's src/variant/*.cpp, +// so they only resolve if that dependency really compiled and linked. +// * Node, Node2D, Node::PROCESS_MODE_*, godot::OK and Variant::OBJECT exist +// only in the pre-generated gen/ tree. +// +// Not covered here, by nature: anything routed through the +// gdextension_interface_* function pointers (String, Array, class +// registration) needs a Godot process that has loaded the extension. The +// GDCLASS/macro shape is covered by the package's own test suite. + +import std; +import godot_cpp; + +namespace { + +bool close(double a, double b) { + return std::fabs(a - b) < 1e-5; +} + +} // namespace + +int main() { + using namespace godot; + + const bool vec2_ok = close(Vector2(3, 4).length(), 5.0) && + close(Vector2(3, 4).normalized().length(), 1.0); + + const Vector3 cross = Vector3(1, 0, 0).cross(Vector3(0, 1, 0)); + const bool vec3_ok = cross == Vector3(0, 0, 1) && + close(Vector3(2, 3, 6).length(), 7.0); + + const bool basis_ok = close(Basis().orthonormalized().determinant(), 1.0); + + const bool color_ok = Color(1.0f, 0.0f, 0.0f, 1.0f).to_rgba32() == 0xff0000ffu; + + const AABB box(Vector3(0, 0, 0), Vector3(2, 3, 4)); + const AABB other(Vector3(1, 1, 1), Vector3(4, 4, 4)); + const bool aabb_ok = close(box.get_volume(), 24.0) && + box.intersects(other) && + close(box.intersection(other).get_volume(), 6.0); + + const bool gen_ok = sizeof(Node) > 0 && + sizeof(Node2D) > 0 && + Node::PROCESS_MODE_INHERIT == 0 && + Node::PROCESS_MODE_DISABLED == 4 && + godot::OK == 0 && + godot::ERR_FILE_NOT_FOUND == 7 && + godot::SIDE_LEFT == 0 && + Variant::OBJECT != Variant::NIL; + + const bool math_ok = Math::is_equal_approx(1.0f, 1.0f) && + !Math::is_zero_approx(1.0f) && + close(Math::lerp(0.0, 10.0, 0.25), 2.5); + + const bool ok = vec2_ok && vec3_ok && basis_ok && color_ok && aabb_ok && + gen_ok && math_ok; + std::println("vec2={} vec3={} basis={} color={} aabb={} gen={} math={}", + vec2_ok, vec3_ok, basis_ok, color_ok, aabb_ok, gen_ok, math_ok); + return ok ? 0 : 1; +} From a3dd28f8bc253a70febcc9aa1580b4da4ed38764 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Tue, 4 Aug 2026 04:44:42 +0800 Subject: [PATCH 2/4] ci: refresh the published index before the workspace tests The workspace jobs never refreshed the published index. The snapshot in play is whatever the pinned mcpp release vendored -- the Download step `cp -a`s the release's registry/ over ~/.mcpp/registry, on top of the restored cache -- so it is by construction older than main, and it never moves, because the cache is saved with that same stale copy inside it. This stayed invisible because mcpp DOES refresh on a miss for a direct dependency, and because every member so far resolved its packages either from this checkout or from compat packages that have been in the index far longer than any snapshot. tests/examples/godot-cpp-module is the first member to depend on a package added in the same cycle THROUGH a Form-A package -- a transitive dependency, which is the path with no refresh -- and it failed with error: dependency 'compat.godot-cpp': no package found ... index: local index 160c389 (never refreshed) on all three platforms, minutes after `Publish Index Artifact` had already republished an index that contained it. Reproduced locally against a snapshot of the same age, and `mcpp index update` alone turns that run green: test result ok. 1 passed; 0 failed; finished in 72.94s --- .github/workflows/validate.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 79ac841..e96666b 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -409,6 +409,30 @@ jobs: echo "MEMBERS=$sel" >> "$GITHUB_ENV" echo "selected members: ${sel:-}" + # ── Refresh the PUBLISHED index before testing ──────────────────── + # Most members resolve everything from this checkout, but a member that + # redirects a namespace other than `compat` gets the REST from the + # published index — and nothing here ever refreshed it. The snapshot in + # play is whatever the pinned mcpp release vendored (the Download step + # `cp -a`s the release's registry/ over ~/.mcpp/registry, on top of the + # restored cache), so it is by construction older than main, and it + # never moves: the cache is saved with that same stale copy inside it. + # + # mcpp does refresh on a miss for a DIRECT dependency, which is why this + # went unnoticed — the gap is a Form-A package's TRANSITIVE dependency. + # tests/examples/godot-cpp-module hit it head-on: the module package's + # own compat.godot-cpp dep resolved against a snapshot predating the + # commit that added it, and failed with `index: local index (never + # refreshed)` even though the artifact had already been republished. + # (Older members never noticed: their compat packages have been in the + # index far longer than any snapshot.) + - name: Refresh the published package index + shell: bash + env: + MCPP_INDEX_MIRROR: GLOBAL + run: | + "$MCPP" index update + - name: mcpp test (workspace or affected members) shell: bash env: From f8512c41cf52971657d1e0c677e12ce8cae52f10 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Tue, 4 Aug 2026 05:26:45 +0800 Subject: [PATCH 3/4] fix: name the module package godot-cpp-m, version tracks upstream Two changes that only make sense together. The version becomes 4.5.0 rather than 0.0.1: a module layer that only re-exports an API has no version of its own worth inventing, and what a consumer needs from the number is which Godot they are targeting -- the same version compat.godot-cpp carries. That is exactly what makes the short name matter. This package and its own compat.godot-cpp dependency are always resolved together, and mcpp's installed-package lookup matches on (name, version) WITHOUT the namespace. With both named `godot-cpp` at 4.5.0, resolving this one lands on compat's unpacked directory: error: dependency 'godotengine.godot-cpp': index entry has no `mcpp = ...` field, and no mcpp.toml was found at /mcpp.toml or /*/mcpp.toml and it is cache-order dependent, so a clean runner passes and a warm one fails. Reproduced with a namespace unrelated to either package: leave a single -x-godot-cpp/4.5.0 in the store and the resolution goes to it; remove it and the same descriptor installs and builds fine. The store DIRECTORIES are namespaced (ns-x-name) -- the lookup is not. `godot-cpp-m` sidesteps it, keeps the version tracking upstream, and matches the repository name. `import godot_cpp;` is unchanged. Verified both ways with the CI-pinned mcpp 2026.8.3.3: cold store (both packages downloaded, 27.80s) and warm store with compat-x-godot-cpp and godotengine-x-godot-cpp-m side by side (3.52s) -- the state that previously failed. Both `test result ok`. --- .agents/docs/2026-08-04-add-godot-cpp-plan.md | 5 +- README.md | 2 +- README.zh-CN.md | 2 +- ...ot-cpp.lua => godotengine.godot-cpp-m.lua} | 47 ++++++++++++++----- tests/examples/godot-cpp-module/mcpp.toml | 4 +- .../tests/godot_cpp_module.cpp | 2 +- 6 files changed, 42 insertions(+), 20 deletions(-) rename pkgs/g/{godotengine.godot-cpp.lua => godotengine.godot-cpp-m.lua} (60%) diff --git a/.agents/docs/2026-08-04-add-godot-cpp-plan.md b/.agents/docs/2026-08-04-add-godot-cpp-plan.md index be4a216..0a4e30c 100644 --- a/.agents/docs/2026-08-04-add-godot-cpp-plan.md +++ b/.agents/docs/2026-08-04-add-godot-cpp-plan.md @@ -116,10 +116,11 @@ godot_cpp ... ok (0.12s) 直接链接跑通 —— 用来提前确认「依赖 .o 全量入链」下没有未解析符号(不需要 `-ldl`/`-lpthread`)。 CPU 时间约 16 分钟,4 核 runner 上折合 4~5 分钟。 -## 6. 后续:模块层 godotengine.godot-cpp +## 6. 后续:模块层 godotengine.godot-cpp-m 模块包**不**放在本索引里内联(避免索引变重,也避免把 1000 TU 再编一遍):按 Form A 走外部仓 -`mcpplibs/godot-cpp-m`,其 `mcpp.toml` 依赖 `compat.godot-cpp`,提供 `import godot_cpp;` +`mcpplibs/godot-cpp-m`(版本号**与上游对齐**,即 4.5.0 —— 只做 API 重导出的模块层没有自己的版本值得编, +消费者从 `godot-cpp = "4.5.0"` 需要知道的就是它对着哪个 Godot),其 `mcpp.toml` 依赖 `compat.godot-cpp`,提供 `import godot_cpp;` (单段模块名,与 `#include ` 和库名对应;点号在本索引里留给子模块,如 `opencv.cv`)。 **顺序是硬约束**:模块成员的测试工程只能声明一条 `[indices]`,给 `godotengine`;它的传递依赖 diff --git a/README.md b/README.md index 8a24593..33ce569 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Two kinds of packages live here: | Multiple majors in one package (shape switches with the version) | [`compat.catch2`](pkgs/c/compat.catch2.lua) (3.x compiles `src/catch2/` into a static library; 2.x goes header-only through `single_include/`) | | External build system (`install()` builds from source) | [`compat.openblas`](pkgs/c/compat.openblas.lua) (Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua) (Perl Configure + Make, static libssl/libcrypto) | | Whole-source direct build (config snapshot + source list, no external build system) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua) (2281 TUs including NASM assembly, declared through 28 directory globs) | -| Module layer over a compat source build (external Form-A repo) | [`godotengine.godot-cpp`](pkgs/g/godotengine.godot-cpp.lua) (`import godot_cpp;` re-exports the whole `godot` namespace, ~1750 names GENERATED from the headers rather than curated; the 1022-TU build stays in `compat.godot-cpp`, so the index carries only this descriptor. Macros — `GDCLASS`, `GDREGISTER_CLASS`, `memnew`, `ERR_*` — are the one thing a named module cannot export, so the package ships a side header to include next to the import) | +| Module layer over a compat source build (external Form-A repo) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua) (`import godot_cpp;` re-exports the whole `godot` namespace, ~1750 names GENERATED from the headers rather than curated; the 1022-TU build stays in `compat.godot-cpp`, so the index carries only this descriptor. Macros — `GDCLASS`, `GDREGISTER_CLASS`, `memnew`, `ERR_*` — are the one thing a named module cannot export, so the package ships a side header to include next to the import) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua) (upstream's own `include/boost/ut.cppm` reproduced verbatim but for one `__argc`/`__argv` shim that Clang-on-MSVC needs; namespace `boost-ext` since it is NOT an official Boost library) | ### Adding a package diff --git a/README.zh-CN.md b/README.zh-CN.md index b575506..b0533ab 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -48,7 +48,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 | 单包多 major(形态随版本切换) | [`compat.catch2`](pkgs/c/compat.catch2.lua)(3.x 编 `src/catch2/` 出静态库;2.x 走 `single_include/` header-only) | | 外部构建系统(`install()` 从源码构建) | [`compat.openblas`](pkgs/c/compat.openblas.lua)(Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua)(Perl Configure + Make,静态 libssl/libcrypto) | | 全源码直编(config 快照 + 源列表,零外部构建系统) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua)(2281 TU 含 NASM 汇编,28 个目录 glob 声明) | -| 模块层叠在 compat 源码构建之上(外部 Form-A 仓) | [`godotengine.godot-cpp`](pkgs/g/godotengine.godot-cpp.lua)(`import godot_cpp;` 重导出整个 `godot` 命名空间,约 1750 个名字由头文件**生成**而非手工罗列;1022 个 TU 的构建留在 `compat.godot-cpp`,索引侧只留这一个描述符。宏 —— `GDCLASS`、`GDREGISTER_CLASS`、`memnew`、`ERR_*` —— 是具名模块唯一带不走的东西,故包内附一个与 import 并排包含的侧头文件) | +| 模块层叠在 compat 源码构建之上(外部 Form-A 仓) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua)(`import godot_cpp;` 重导出整个 `godot` 命名空间,约 1750 个名字由头文件**生成**而非手工罗列;1022 个 TU 的构建留在 `compat.godot-cpp`,索引侧只留这一个描述符。宏 —— `GDCLASS`、`GDREGISTER_CLASS`、`memnew`、`ERR_*` —— 是具名模块唯一带不走的东西,故包内附一个与 import 并排包含的侧头文件) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua)(逐字复用上游自带的 `include/boost/ut.cppm`,仅加一处 Clang-on-MSVC 需要的 `__argc`/`__argv` shim;命名空间取 `boost-ext`,因其并非 boost 官方库) | ### 新增一个包 diff --git a/pkgs/g/godotengine.godot-cpp.lua b/pkgs/g/godotengine.godot-cpp-m.lua similarity index 60% rename from pkgs/g/godotengine.godot-cpp.lua rename to pkgs/g/godotengine.godot-cpp-m.lua index 15d7b0a..3bab811 100644 --- a/pkgs/g/godotengine.godot-cpp.lua +++ b/pkgs/g/godotengine.godot-cpp-m.lua @@ -30,11 +30,32 @@ -- a hard error, not the -Wexpose-global-module-tu-local warning. Extension -- code uses Dictionary/Array/TypedArray; the headers still have the rest. -- +-- The version TRACKS UPSTREAM rather than counting the wrapper's own +-- iterations: 4.5.0 is the godot-cpp release it wraps, and the version +-- compat.godot-cpp carries, so the version tells a consumer the one thing +-- that matters -- which Godot they are targeting. +-- +-- The name is `godot-cpp-m`, NOT `godot-cpp`, and that is load-bearing rather +-- than cosmetic. This package and its own compat.godot-cpp dependency are +-- always resolved together, and mcpp's installed-package lookup matches on +-- (name, version) WITHOUT the namespace: with both named `godot-cpp` at the +-- same version -- which "the version tracks upstream" guarantees -- resolving +-- this one lands on compat's unpacked directory and fails with "no mcpp.toml +-- at /*/mcpp.toml". Reproducible with a namespace unrelated to either +-- package: leave a single -x-godot-cpp/4.5.0 in the store and the +-- resolution goes to it; remove it and the same descriptor installs fine. The +-- store DIRECTORIES are namespaced (ns-x-name) -- the lookup is not. Distinct +-- short names sidestep it, keep the version tracking upstream, and `-m` +-- matches the repository name. `import godot_cpp;` is unaffected. +-- +-- CN tag is `v4.5.0-m`: the gitcode mirror repo is shared with the +-- compat.godot-cpp archives, which already hold the bare-version tags. +-- -- Three platforms, one OS-neutral tarball: godot-cpp is portable C++ and -- compat.godot-cpp covers all three. package = { spec = "1", - name = "godot-cpp", + name = "godot-cpp-m", namespace = "godotengine", description = "C++23 module package for godot-cpp (import godot_cpp) — Godot GDExtension API, C++ API unchanged", licenses = {"MIT"}, -- module layer; upstream godot-cpp is MIT as well @@ -43,30 +64,30 @@ package = { xpm = { linux = { - ["0.0.1"] = { + ["4.5.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", - CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v4.5.0-m/godot-cpp-m-4.5.0.tar.gz", }, - sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + sha256 = "d91794f46ec4a74c1f8c61c894efdecceaf54feff103b9b139fd6a8f16e43051", }, }, macosx = { - ["0.0.1"] = { + ["4.5.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", - CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v4.5.0-m/godot-cpp-m-4.5.0.tar.gz", }, - sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + sha256 = "d91794f46ec4a74c1f8c61c894efdecceaf54feff103b9b139fd6a8f16e43051", }, }, windows = { - ["0.0.1"] = { + ["4.5.0"] = { url = { - GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v0.0.1.tar.gz", - CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v0.0.1/godot-cpp-m-0.0.1.tar.gz", + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v4.5.0-m/godot-cpp-m-4.5.0.tar.gz", }, - sha256 = "5145f1e539b4b42bdb4064a2df0dcb54ffac4d70f88edc42f38bf2304a37a344", + sha256 = "d91794f46ec4a74c1f8c61c894efdecceaf54feff103b9b139fd6a8f16e43051", }, }, }, diff --git a/tests/examples/godot-cpp-module/mcpp.toml b/tests/examples/godot-cpp-module/mcpp.toml index 8e4650e..5dedd25 100644 --- a/tests/examples/godot-cpp-module/mcpp.toml +++ b/tests/examples/godot-cpp-module/mcpp.toml @@ -19,6 +19,6 @@ version = "0.1.0" # Dotted spelling on purpose: CI maps a changed pkgs//.lua to the # members whose mcpp.toml mentions , and here is the whole -# `godotengine.godot-cpp`. +# `godotengine.godot-cpp-m`. [dependencies] -godotengine.godot-cpp = "0.0.1" +godotengine.godot-cpp-m = "4.5.0" diff --git a/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp b/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp index bb4c880..3022cf7 100644 --- a/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp +++ b/tests/examples/godot-cpp-module/tests/godot_cpp_module.cpp @@ -1,4 +1,4 @@ -// Behavioral test for the godotengine.godot-cpp module package: godot-cpp's +// Behavioral test for the godotengine.godot-cpp-m module package: godot-cpp's // API reached with `import godot_cpp;` and no #include at all. // // The numbers are deliberately the same ones tests/examples/godot-cpp asserts From f9bded9ae65ca9132fe2cf1703c62215abd63fed Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Tue, 4 Aug 2026 12:57:34 +0800 Subject: [PATCH 4/4] feat: godotengine.godot-cpp-m 10.0.0-rc1 (Godot 4.6) Second version of the module layer, tracking upstream one for one as compat.godot-cpp's do: 10.0.0-rc1 binds Godot 4.6, 4.5.0 binds Godot 4.5. Getting there needed a shim on the package side. godot-cpp declares hash_murmur3_one_float/_double `static`, and each declares an unnamed union in its body -- a class with no linkage inside a TU-local function. With 10.x the module interface exposes those functions (reachable from many inline bodies) and GCC rejects it outright: error: 'uint32_t godot::hash_murmur3_one_float(float, uint32_t)' exposes TU-local entity 'union ...::' -Wno-expose-global-module-tu-local, -fpermissive and -Wno-error= all leave it standing, and exporting less does not converge -- a single engine class is enough to trigger it. The package now generates a shim: upstream's header with `static` dropped from those two, scoped to its own include path, so compat.godot-cpp still compiles upstream's copy untouched. With it the export surface actually GREW (1818 names) because nothing has to be held back any more -- HashMap, HashSet, AHashMap and PairHash re-export normally. The new member asserts the version through TYPES rather than GODOT_VERSION_MAJOR/_MINOR: those are macros, and a module cannot export macros. EditorDock and AHashMap exist in 4.6 and not in 4.5, which also doubles as proof the shim did its job. Verified with the CI-pinned mcpp 2026.8.3.3: godot-cpp-module -> ok (1 passed) godot-cpp-module-v10 -> version=true ... ok (1 passed) --- README.md | 2 +- README.zh-CN.md | 2 +- mcpp.toml | 1 + pkgs/g/godotengine.godot-cpp-m.lua | 33 +++++++- tests/examples/godot-cpp-module-v10/mcpp.toml | 20 +++++ .../tests/godot_cpp_module_v10.cpp | 76 +++++++++++++++++++ 6 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 tests/examples/godot-cpp-module-v10/mcpp.toml create mode 100644 tests/examples/godot-cpp-module-v10/tests/godot_cpp_module_v10.cpp diff --git a/README.md b/README.md index 33ce569..5140234 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Two kinds of packages live here: | Multiple majors in one package (shape switches with the version) | [`compat.catch2`](pkgs/c/compat.catch2.lua) (3.x compiles `src/catch2/` into a static library; 2.x goes header-only through `single_include/`) | | External build system (`install()` builds from source) | [`compat.openblas`](pkgs/c/compat.openblas.lua) (Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua) (Perl Configure + Make, static libssl/libcrypto) | | Whole-source direct build (config snapshot + source list, no external build system) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua) (2281 TUs including NASM assembly, declared through 28 directory globs) | -| Module layer over a compat source build (external Form-A repo) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua) (`import godot_cpp;` re-exports the whole `godot` namespace, ~1750 names GENERATED from the headers rather than curated; the 1022-TU build stays in `compat.godot-cpp`, so the index carries only this descriptor. Macros — `GDCLASS`, `GDREGISTER_CLASS`, `memnew`, `ERR_*` — are the one thing a named module cannot export, so the package ships a side header to include next to the import) | +| Module layer over a compat source build (external Form-A repo) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua) (two versions tracking upstream: `10.0.0-rc1` = Godot 4.6, `4.5.0` = Godot 4.5. `import godot_cpp;` re-exports the whole `godot` namespace, ~1800 names GENERATED from the headers rather than curated; the 1022-TU build stays in `compat.godot-cpp`, so the index carries only this descriptor. Macros — `GDCLASS`, `GDREGISTER_CLASS`, `memnew`, `ERR_*` — are the one thing a named module cannot export, so the package ships a side header to include next to the import. It also ships a generated `hashfuncs.hpp` shim — upstream's header minus `static` on two functions whose bodies declare an unnamed union — without which GCC refuses the module interface outright, a hard error no `-W` flag reaches) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua) (upstream's own `include/boost/ut.cppm` reproduced verbatim but for one `__argc`/`__argv` shim that Clang-on-MSVC needs; namespace `boost-ext` since it is NOT an official Boost library) | ### Adding a package diff --git a/README.zh-CN.md b/README.zh-CN.md index b0533ab..d6c4f10 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -48,7 +48,7 @@ mcpp self config --mirror CN # 切换至国内镜像,默认使用 GLOBAL 上 | 单包多 major(形态随版本切换) | [`compat.catch2`](pkgs/c/compat.catch2.lua)(3.x 编 `src/catch2/` 出静态库;2.x 走 `single_include/` header-only) | | 外部构建系统(`install()` 从源码构建) | [`compat.openblas`](pkgs/c/compat.openblas.lua)(Make) · [`compat.openssl`](pkgs/c/compat.openssl.lua)(Perl Configure + Make,静态 libssl/libcrypto) | | 全源码直编(config 快照 + 源列表,零外部构建系统) | [`compat.ffmpeg`](pkgs/c/compat.ffmpeg.lua)(2281 TU 含 NASM 汇编,28 个目录 glob 声明) | -| 模块层叠在 compat 源码构建之上(外部 Form-A 仓) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua)(`import godot_cpp;` 重导出整个 `godot` 命名空间,约 1750 个名字由头文件**生成**而非手工罗列;1022 个 TU 的构建留在 `compat.godot-cpp`,索引侧只留这一个描述符。宏 —— `GDCLASS`、`GDREGISTER_CLASS`、`memnew`、`ERR_*` —— 是具名模块唯一带不走的东西,故包内附一个与 import 并排包含的侧头文件) | +| 模块层叠在 compat 源码构建之上(外部 Form-A 仓) | [`godotengine.godot-cpp-m`](pkgs/g/godotengine.godot-cpp-m.lua)(两个版本与上游对齐:`10.0.0-rc1` 对应 Godot 4.6,`4.5.0` 对应 Godot 4.5。`import godot_cpp;` 重导出整个 `godot` 命名空间,约 1800 个名字由头文件**生成**而非手工罗列;1022 个 TU 的构建留在 `compat.godot-cpp`,索引侧只留这一个描述符。宏 —— `GDCLASS`、`GDREGISTER_CLASS`、`memnew`、`ERR_*` —— 是具名模块唯一带不走的东西,故包内附一个与 import 并排包含的侧头文件。另外还带一份生成的 `hashfuncs.hpp` 遮蔽头 —— 上游那个头去掉两个函数的 `static`(它们体内声明了匿名 union)—— 否则 GCC 直接拒绝该模块接口,且是任何 `-W` 开关都够不到的硬错误) | | C++23 module wrapper | [`nlohmann.json`](pkgs/n/nlohmann.json.lua) · [`marzer.tomlplusplus`](pkgs/m/marzer.tomlplusplus.lua) · [`neargye.magic_enum`](pkgs/n/neargye.magic_enum.lua) · [`boost-ext.ut`](pkgs/b/boost-ext.ut.lua)(逐字复用上游自带的 `include/boost/ut.cppm`,仅加一处 Clang-on-MSVC 需要的 `__argc`/`__argv` shim;命名空间取 `boost-ext`,因其并非 boost 官方库) | ### 新增一个包 diff --git a/mcpp.toml b/mcpp.toml index 14b9096..023726e 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -31,6 +31,7 @@ members = [ "tests/examples/fmtlib.fmt", "tests/examples/godot-cpp", "tests/examples/godot-cpp-module", + "tests/examples/godot-cpp-module-v10", "tests/examples/godot-cpp-v10", "tests/examples/gui-stack", "tests/examples/imgui", diff --git a/pkgs/g/godotengine.godot-cpp-m.lua b/pkgs/g/godotengine.godot-cpp-m.lua index 3bab811..3336fe4 100644 --- a/pkgs/g/godotengine.godot-cpp-m.lua +++ b/pkgs/g/godotengine.godot-cpp-m.lua @@ -48,7 +48,17 @@ -- short names sidestep it, keep the version tracking upstream, and `-m` -- matches the repository name. `import godot_cpp;` is unaffected. -- --- CN tag is `v4.5.0-m`: the gitcode mirror repo is shared with the +-- Versions track upstream godot-cpp one for one, as compat.godot-cpp's do: +-- 10.0.0-rc1 binds Godot 4.6, 4.5.0 binds Godot 4.5. +-- +-- 10.0.0-rc1 additionally ships a generated `hashfuncs.hpp` shim -- upstream's +-- header with `static` dropped from two functions -- without which GCC refuses +-- the module interface outright ("exposes TU-local entity 'union '", +-- a hard error no -W flag reaches). It is scoped to the package's own include +-- path, so compat.godot-cpp still compiles upstream's copy untouched. See the +-- package README. +-- +-- CN tags carry an `-m` suffix: the gitcode mirror repo is shared with the -- compat.godot-cpp archives, which already hold the bare-version tags. -- -- Three platforms, one OS-neutral tarball: godot-cpp is portable C++ and @@ -64,6 +74,13 @@ package = { xpm = { linux = { + ["10.0.0-rc1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v10.0.0-rc1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v10.0.0-rc1-m/godot-cpp-m-10.0.0-rc1.tar.gz", + }, + sha256 = "895975f32456d821b1297dbd5298ea3e9b925b00557a187927f3dacf2f035220", + }, ["4.5.0"] = { url = { GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", @@ -73,6 +90,13 @@ package = { }, }, macosx = { + ["10.0.0-rc1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v10.0.0-rc1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v10.0.0-rc1-m/godot-cpp-m-10.0.0-rc1.tar.gz", + }, + sha256 = "895975f32456d821b1297dbd5298ea3e9b925b00557a187927f3dacf2f035220", + }, ["4.5.0"] = { url = { GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", @@ -82,6 +106,13 @@ package = { }, }, windows = { + ["10.0.0-rc1"] = { + url = { + GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v10.0.0-rc1.tar.gz", + CN = "https://gitcode.com/mcpp-res/godot-cpp/releases/download/v10.0.0-rc1-m/godot-cpp-m-10.0.0-rc1.tar.gz", + }, + sha256 = "895975f32456d821b1297dbd5298ea3e9b925b00557a187927f3dacf2f035220", + }, ["4.5.0"] = { url = { GLOBAL = "https://github.com/mcpplibs/godot-cpp-m/archive/refs/tags/v4.5.0.tar.gz", diff --git a/tests/examples/godot-cpp-module-v10/mcpp.toml b/tests/examples/godot-cpp-module-v10/mcpp.toml new file mode 100644 index 0000000..9a8d314 --- /dev/null +++ b/tests/examples/godot-cpp-module-v10/mcpp.toml @@ -0,0 +1,20 @@ +# godotengine.godot-cpp-m 10.0.0-rc1 (Godot 4.6) module-layer test project. +# +# Separate member from godot-cpp-module for the same reason compat has two: +# the versions are different bindings of different engine releases, and each +# has to be built and asserted on its own. +# +# Overrides the workspace-root redirect: root declares `compat`, this member +# needs `godotengine`. A member-level [indices] REPLACES the inherited table, +# so the module package's own compat.godot-cpp dependency resolves from the +# GLOBAL published index -- that descriptor is pre-merge-validated by +# tests/examples/godot-cpp-v10. +[indices] +godotengine = { path = "../../.." } + +[package] +name = "godot-cpp-module-v10-tests" +version = "0.1.0" + +[dependencies] +godotengine.godot-cpp-m = "10.0.0-rc1" diff --git a/tests/examples/godot-cpp-module-v10/tests/godot_cpp_module_v10.cpp b/tests/examples/godot-cpp-module-v10/tests/godot_cpp_module_v10.cpp new file mode 100644 index 0000000..a1012e2 --- /dev/null +++ b/tests/examples/godot-cpp-module-v10/tests/godot_cpp_module_v10.cpp @@ -0,0 +1,76 @@ +// Behavioral test for the godotengine.godot-cpp-m 10.0.0-rc1 module package (Godot 4.6): godot-cpp's +// API reached with `import godot_cpp;` and no #include at all. +// +// The numbers are deliberately the same ones tests/examples/godot-cpp asserts +// through headers -- that member is the header spelling of this one, so a +// divergence here means the module re-export changed behaviour. +// +// Both halves can fail: +// * Vector2::length(), Basis::orthonormalized(), Color::to_rgba32() and +// AABB::get_volume() are DEFINED in compat.godot-cpp's src/variant/*.cpp, +// so they only resolve if that dependency really compiled and linked. +// * Node, Node2D, Node::PROCESS_MODE_*, godot::OK and Variant::OBJECT exist +// only in the pre-generated gen/ tree. +// +// Not covered here, by nature: anything routed through the +// gdextension_interface_* function pointers (String, Array, class +// registration) needs a Godot process that has loaded the extension. The +// GDCLASS/macro shape is covered by the package's own test suite. + +import std; +import godot_cpp; + +namespace { + +bool close(double a, double b) { + return std::fabs(a - b) < 1e-5; +} + +} // namespace + +int main() { + using namespace godot; + + const bool vec2_ok = close(Vector2(3, 4).length(), 5.0) && + close(Vector2(3, 4).normalized().length(), 1.0); + + const Vector3 cross = Vector3(1, 0, 0).cross(Vector3(0, 1, 0)); + const bool vec3_ok = cross == Vector3(0, 0, 1) && + close(Vector3(2, 3, 6).length(), 7.0); + + const bool basis_ok = close(Basis().orthonormalized().determinant(), 1.0); + + const bool color_ok = Color(1.0f, 0.0f, 0.0f, 1.0f).to_rgba32() == 0xff0000ffu; + + const AABB box(Vector3(0, 0, 0), Vector3(2, 3, 4)); + const AABB other(Vector3(1, 1, 1), Vector3(4, 4, 4)); + const bool aabb_ok = close(box.get_volume(), 24.0) && + box.intersects(other) && + close(box.intersection(other).get_volume(), 6.0); + + // These bindings are Godot 4.6. The 4.5 member checks GODOT_VERSION_MAJOR + // / _MINOR, but those are MACROS from version.hpp and a module cannot + // export macros -- so the version is asserted through types instead: + // EditorDock and AHashMap exist in 4.6 and not in 4.5. + const bool version_ok = sizeof(EditorDock) > 0 && + sizeof(AHashMap) > 0; + + const bool gen_ok = sizeof(Node) > 0 && + sizeof(Node2D) > 0 && + Node::PROCESS_MODE_INHERIT == 0 && + Node::PROCESS_MODE_DISABLED == 4 && + godot::OK == 0 && + godot::ERR_FILE_NOT_FOUND == 7 && + godot::SIDE_LEFT == 0 && + Variant::OBJECT != Variant::NIL; + + const bool math_ok = Math::is_equal_approx(1.0f, 1.0f) && + !Math::is_zero_approx(1.0f) && + close(Math::lerp(0.0, 10.0, 0.25), 2.5); + + const bool ok = version_ok && vec2_ok && vec3_ok && basis_ok && color_ok && + aabb_ok && gen_ok && math_ok; + std::println("version={} vec2={} vec3={} basis={} color={} aabb={} gen={} math={}", + version_ok, vec2_ok, vec3_ok, basis_ok, color_ok, aabb_ok, gen_ok, math_ok); + return ok ? 0 : 1; +}