Skip to content

feat: add compat.godot-cpp 4.5.0 - #143

Merged
Sunrisepeak merged 2 commits into
mainfrom
feat/compat-godot-cpp
Aug 3, 2026
Merged

feat: add compat.godot-cpp 4.5.0#143
Sunrisepeak merged 2 commits into
mainfrom
feat/compat-godot-cpp

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

What

Adds compat.godot-cpp 4.5.0 (upstream tag godot-4.5-stable) — the C++ bindings for Godot's GDExtension API, consumable as an ordinary mcpp source package: #include <godot_cpp/...>, no SCons, no CMake, no Python.

Shape: upstream codegen frozen into the mirror archive

godot-cpp's tag archives are half a source tree. The other half — ~1000 engine classes and every builtin Variant type, i.e. gen/include/ and gen/src/ — is emitted by upstream's own binding_generator.py from gdextension/extension_api.json at build time, and ships in no upstream archive or release:

upstream archive:  include/ 67 .hpp    src/ 32 .cpp
after generation:  +1010 .hpp          +990 .cpp     (18 MB)

Three ways to get it, and why this one:

option verdict
install() hook runs python on the consumer side rejected — makes Python a hard dependency of every build on every platform, against what this index does elsewhere (compat.ffmpeg's config snapshot, the opencv package's frozen gen/)
ship gen/ through generated_files rejected — 18 MB of text in a descriptor
run it once offline, publish the result taken

So the download is a repack, in the same spirit as the asio symlink repack: upstream's tree byte-for-byte, plus gen/.

tools/godot-cpp/repack.sh is the archive's only source and its verifier — it re-extracts the upstream archive, diff -rs it against the generated tree and refuses to publish if any upstream file differs, then packs deterministically (--sort=name, fixed mtime, gzip -n). Two independent runs hash identically.

Mirrors

region url
GLOBAL github.com/xlings-res/godot-cpp/releases/download/4.5.0/godot-cpp-4.5.0.tar.gz
CN gitcode.com/mcpp-res/godot-cpp/releases/download/4.5.0/godot-cpp-4.5.0.tar.gz

Both re-downloaded and hashed: b0c36e77f02c4181352cdd7547b209b93a833be1ad6197f8c650d92987221a00, identical to the local build. Upstream godot-4.5-stable archive is ac78539c0042554c494ea419549d2de88758d448721aeb0e5d41129aa87e339c; both are recorded in the mirror repo's README together with the reproduction recipe.

One OS-neutral archive for all three platforms — godot-cpp is portable C++ with no per-platform source selection; the platform split lives in Godot itself, behind the gdextension_interface.h C ABI.

Defines / features

GDEXTENSION is upstream's PUBLIC compile definition (cmake puts it on the target's INTERFACE), so library and consumer TUs must agree — it rides on default = { implies = { "gdextension" } }, the same shape as CURL_STATICLIB in compat.curl.

Deliberately not features:

  • DEBUG_ENABLED / DEV_ENABLED — extra checks, off in upstream's release default.
  • HOT_RELOAD_ENABLED — changes the Wrapped layout; that is ABI, not a switch.
  • REAL_T_IS_DOUBLE — needs a gen/ tree generated with precision=double, which is not in this archive. It could only ever be a separate version/package, never a feature over these bytes.

Shared reason for the first two: each re-keys the store into a second full 1022-TU build of the same library.

Test member

tests/examples/godot-cpp/ (inherits the workspace-root compat redirect). Both halves are asserted, and either can fail:

  • linkingVector2::length(), Basis::orthonormalized(), Color::to_rgba32(), AABB::get_volume() are declared in headers but defined in src/variant/*.cpp, so a pass proves the 1022 TUs really compiled and linked in.
  • generated bindings<godot_cpp/classes/node.hpp>, Node::PROCESS_MODE_*, godot::OK, godot::ERR_FILE_NOT_FOUND, Variant::OBJECT exist only under gen/.

Out of scope by nature: anything routed through the gdextension_interface_* function pointers (String, Array, class registration) needs a Godot process that has loaded the extension. The pure-math half does not, so that is where the assertions live.

Verification

Cold run with the CI-pinned mcpp (MCPP_VERSION = 2026.8.3.3):

$ mcpp test -p godot-cpp
   Compiling compat.godot-cpp v4.5.0
     Running bin/godot_cpp
vec2=1 vec3=1 basis=1 color=1 aabb=1 gen=1
godot_cpp ... ok (0.12s)
 test result ok. 1 passed; 0 failed; finished in 106.54s (build 55.60s + run 0.02s)

Local lint mirrors validate.yml (syntax, required fields, no leading v, mirror table, package name, cross-package refs, mcpp xpkg parse) — all clean, and the CN url returns 200.

Separately, all 1022 TUs were built once with gcc 13 at -std=c++23 and every object linked eagerly against the test's main to confirm no unresolved symbols under whole-object dependency linking (no -ldl/-lpthread needed). ~16 CPU-minutes, so ~4–5 min on a 4-core runner.

Note src/core/object.cpp and gen/src/classes/object.cpp share a basename — covered by mcpp's obj-path disambiguation (mcpp#233/#240), well below this index's client floor, and verified by the passing link.

Follow-up (separate PR)

The C++23 module layer stays out of this index: it will be the external Form-A package mcpplibs/godot-cpp-m, depending on compat.godot-cpp and exposing import godot_cpp;, registered here as pkgs/g/godotengine.godot-cpp.lua.

That has to come after this one merges and publish-artifact republishes: a module test member can declare only one [indices] redirect (godotengine), so its transitive compat.godot-cpp resolves from the published index — the same constraint tests/examples/ffmpeg-module documents.

Design notes: .agents/docs/2026-08-04-add-godot-cpp-plan.md.

godot-cpp's tag archives are only half a source tree: the ~1000 GDExtension
engine classes and every builtin Variant type (gen/include, gen/src) are
emitted by upstream's own binding_generator.py at build time, and ship in no
upstream archive or release. Running that generator on the consumer side
would make Python a hard dependency of every build on every platform, so it
runs once offline instead and the result is published as an immutable mirror
archive: upstream's tree byte-for-byte plus gen/.

tools/godot-cpp/repack.sh is the only source of that archive and verifies it
-- it diffs the regenerated tree against a fresh extraction of the upstream
archive and refuses to publish if any upstream file differs, then packs
deterministically (sorted, fixed mtime, gzip -n). Two runs hash identically.

  GLOBAL github.com/xlings-res/godot-cpp    4.5.0
  CN     gitcode.com/mcpp-res/godot-cpp     4.5.0
  sha256 b0c36e77f02c4181352cdd7547b209b93a833be1ad6197f8c650d92987221a00

GDEXTENSION is upstream's PUBLIC compile definition, so it rides on a default
feature and reaches consumer TUs. DEBUG_ENABLED / DEV_ENABLED /
HOT_RELOAD_ENABLED are deliberately not features (each re-keys the store into
a second full 1022-TU build, and HOT_RELOAD_ENABLED changes the Wrapped
layout); REAL_T_IS_DOUBLE cannot be one at all, since it needs a gen/ tree
generated with precision=double.

The test member asserts on both halves and can fail on either: Vector2::
length(), Basis::orthonormalized(), Color::to_rgba32() and AABB::get_volume()
are defined in src/variant/*.cpp, so they only resolve if the library really
compiled and linked, while Node, Node::PROCESS_MODE_*, godot::OK and
Variant::OBJECT exist only in gen/. Everything a running Godot process would
be needed for (String, Array, class registration) is out of scope.

Verified locally with the CI-pinned mcpp 2026.8.3.3:
`mcpp test -p godot-cpp` -> test result ok. 1 passed; 0 failed (106.54s).
A GDExtension is a shared library, so compat.godot-cpp's objects are
almost always linked into one -- and without position-independent code
that link fails outright:

  ld: obj/vector4i.o: relocation R_X86_64_32 against `.rodata` can not
      be used when making a shared object; recompile with -fPIC

which leaves the package unable to do the one thing it exists for.
Upstream's own SCons and CMake builds pass -fPIC for the same reason.
Windows does not need it: the PE toolchain has no such distinction and
clang-cl would only report the flag as unused.

Caught by building a real GDExtension (kind = "shared") against the
package, not by inspection. With it, that shared library links and
exports its entry symbol, and the workspace member still passes.
@Sunrisepeak
Sunrisepeak merged commit fc8ba4e into main Aug 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants