fix(modules): graft to the directory Godot expects, and fetch submodules #13

Merged
picklemustard merged 1 commit from dev into main 2026-09-20 16:07:18 +00:00

A build with godot_voxel enabled failed in the generated registration file:

modules/register_module_types.gen.cpp:155:9: error:
'initialize_godot_voxel_module' was not declared in this scope;
did you mean 'initialize_voxel_module'?

Godot generates modules/register_module_types.gen.cpp from the directory name
under modules/: for modules/

it includes /register_types.h and calls
initialize__module. Zylann/godot_voxel is named for its repository but
declares initialize_voxel_module, so its catalog row has to graft it to
modules/voxel. The row's path was empty, so it went to modules/godot_voxel and
the engine compiled every source the module owns before failing on a symbol
that never mentions the directory that caused it.

The machinery for this was already right -- workspaceSpec derives the SCons
toggle from the DestPath basename, so a correct path fixes the destination and
module_

_enabled=yes together. Nothing recorded or checked which directory
a module expects, which is what made a one-field mistake cost a full compile.

Four changes:

  • internal/gdmodule reads the entry point a register_types.h declares. The
    word boundary matters: uninitialize_x_module contains initialize_x_module.

  • graft() checks the header against the directory it just exported into, and
    refuses with the path to set. It stays quiet about anything it cannot judge
    -- no header, a macro-declared entry point, a destination the build already
    rejects elsewhere -- so it can only ever fail a build that was going to fail.

  • Auto-populate reads the same header inside the crawl's existing mirror
    borrow and returns suggested_path, which the admin page offers to apply. No
    second lock and no extra fetch.

  • Registry.Export now fills gitlinks. git archive omits them, a mirror clone
    does not fetch them, and the build container has no network, so a module
    using submodules arrived incomplete and silently. Submodule URLs come from
    upstream rather than from an administrator, so they are followed within
    bounds: an allowlist of remote schemes, three levels of nesting, 64
    submodules across the whole graft, safeJoin on every path, and a repository
    repeated within one chain refused as a cycle. A gitlink pins an exact SHA,
    so a warm mirror fetches nothing at all.

docs/architecture.md claimed submodules "work for free" under shelling out to
git. They do not, and it now says what actually happens.

Operationally, the live godot_voxel row still needs its path set to
modules/voxel; this stops the next such row from costing a build to find.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

A build with godot_voxel enabled failed in the generated registration file: modules/register_module_types.gen.cpp:155:9: error: 'initialize_godot_voxel_module' was not declared in this scope; did you mean 'initialize_voxel_module'? Godot generates modules/register_module_types.gen.cpp from the directory name under modules/: for modules/<dir> it includes <dir>/register_types.h and calls initialize_<dir>_module. Zylann/godot_voxel is named for its repository but declares initialize_voxel_module, so its catalog row has to graft it to modules/voxel. The row's path was empty, so it went to modules/godot_voxel and the engine compiled every source the module owns before failing on a symbol that never mentions the directory that caused it. The machinery for this was already right -- workspaceSpec derives the SCons toggle from the DestPath basename, so a correct path fixes the destination and module_<dir>_enabled=yes together. Nothing recorded or checked which directory a module expects, which is what made a one-field mistake cost a full compile. Four changes: - internal/gdmodule reads the entry point a register_types.h declares. The word boundary matters: uninitialize_x_module contains initialize_x_module. - graft() checks the header against the directory it just exported into, and refuses with the path to set. It stays quiet about anything it cannot judge -- no header, a macro-declared entry point, a destination the build already rejects elsewhere -- so it can only ever fail a build that was going to fail. - Auto-populate reads the same header inside the crawl's existing mirror borrow and returns suggested_path, which the admin page offers to apply. No second lock and no extra fetch. - Registry.Export now fills gitlinks. git archive omits them, a mirror clone does not fetch them, and the build container has no network, so a module using submodules arrived incomplete and silently. Submodule URLs come from upstream rather than from an administrator, so they are followed within bounds: an allowlist of remote schemes, three levels of nesting, 64 submodules across the whole graft, safeJoin on every path, and a repository repeated within one chain refused as a cycle. A gitlink pins an exact SHA, so a warm mirror fetches nothing at all. docs/architecture.md claimed submodules "work for free" under shelling out to git. They do not, and it now says what actually happens. Operationally, the live godot_voxel row still needs its path set to modules/voxel; this stops the next such row from costing a build to find. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(modules): graft to the directory Godot expects, and fetch submodules
All checks were successful
CI / Dedupe (push) Successful in 1s
CI / Go (push) Successful in 3m38s
CI / Frontend (push) Successful in 29s
CI / Badges (push) Has been skipped
CI / Publish images (push) Has been skipped
CI / Dedupe (pull_request) Successful in 1s
CI / Go (pull_request) Successful in 1m53s
CI / Frontend (pull_request) Successful in 20s
CI / Badges (pull_request) Has been skipped
CI / Publish images (pull_request) Has been skipped
213d3c258e
A build with godot_voxel enabled failed in the generated registration file:

    modules/register_module_types.gen.cpp:155:9: error:
    'initialize_godot_voxel_module' was not declared in this scope;
    did you mean 'initialize_voxel_module'?

Godot generates modules/register_module_types.gen.cpp from the directory name
under modules/: for modules/<dir> it includes <dir>/register_types.h and calls
initialize_<dir>_module. Zylann/godot_voxel is named for its repository but
declares initialize_voxel_module, so its catalog row has to graft it to
modules/voxel. The row's path was empty, so it went to modules/godot_voxel and
the engine compiled every source the module owns before failing on a symbol
that never mentions the directory that caused it.

The machinery for this was already right -- workspaceSpec derives the SCons
toggle from the DestPath basename, so a correct path fixes the destination and
module_<dir>_enabled=yes together. Nothing recorded or checked which directory
a module expects, which is what made a one-field mistake cost a full compile.

Four changes:

- internal/gdmodule reads the entry point a register_types.h declares. The
  word boundary matters: uninitialize_x_module contains initialize_x_module.

- graft() checks the header against the directory it just exported into, and
  refuses with the path to set. It stays quiet about anything it cannot judge
  -- no header, a macro-declared entry point, a destination the build already
  rejects elsewhere -- so it can only ever fail a build that was going to fail.

- Auto-populate reads the same header inside the crawl's existing mirror
  borrow and returns suggested_path, which the admin page offers to apply. No
  second lock and no extra fetch.

- Registry.Export now fills gitlinks. git archive omits them, a mirror clone
  does not fetch them, and the build container has no network, so a module
  using submodules arrived incomplete and silently. Submodule URLs come from
  upstream rather than from an administrator, so they are followed within
  bounds: an allowlist of remote schemes, three levels of nesting, 64
  submodules across the whole graft, safeJoin on every path, and a repository
  repeated within one chain refused as a cycle. A gitlink pins an exact SHA,
  so a warm mirror fetches nothing at all.

docs/architecture.md claimed submodules "work for free" under shelling out to
git. They do not, and it now says what actually happens.

Operationally, the live godot_voxel row still needs its path set to
modules/voxel; this stops the next such row from costing a build to find.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
StudioTeal/GodotBinaryBuilder!13
No description provided.