This page describes how Godot's C++ module system works: how modules are detected, configured, built, and registered at startup. It covers the SCons-side machinery (detection, dependency checking, code generation) and the conventions a module directory must follow.
For information about the overall SCons build entry point and general build options, see Build Configuration. For platform-specific detection scripts, see Platform Detection. For the C# scripting module specifically, see C# Scripting.
Godot's module system is a mechanism for compiling optional C++ subsystems into the engine. Each module lives in its own directory, provides a fixed set of files that the build system recognises, and is compiled into a separate static library that is linked into the final binary. Modules can declare dependencies on other modules, and custom modules outside the main modules/ directory are supported via the custom_modules build option.
The entire module pipeline runs at SCons configuration time and produces:
libmodule_<name>.a static libraries (one per enabled module).libmodules.a library containing a generated register_module_types.gen.cpp that calls each module's init/deinit functions.modules_enabled.gen.h listing MODULE_<NAME>_ENABLED preprocessor defines.Module detection runs early in SConstruct, before platform configuration is finalised.
Search paths are assembled in order:
modules/ (always searched, non-recursively).custom_modules build option (comma-separated; searched recursively by default, controlled by custom_modules_recursive).The function detect_modules(search_path, recursive) in methods.py walks the given directory and calls is_module(path) on every subdirectory.
is_module(path) returns True only when the directory contains all three of these files:
| Required file | Purpose |
|---|---|
register_types.h | Declares initialize_<name>_module / uninitialize_<name>_module |
SCsub | SCons build script for the module |
config.py | Python script providing build options and enable/disable logic |
The return value of detect_modules is an OrderedDict mapping module name → directory path. Relative paths indicate built-in modules; absolute paths indicate custom modules.
Diagram: Module Detection Code Flow
Sources: SConstruct436-462 methods.py240-310
A valid module directory must contain, at minimum:
modules/my_module/
config.py # Build configuration and enable/disable logic
SCsub # SCons build script
register_types.h # Declares initialize/uninitialize functions
register_types.cpp # Implements initialize/uninitialize functions
Additional files and subdirectories (source files, thirdparty libs, tests/, editor/) are common but not required by the detection mechanism.
After detection, SConstruct imports each module's config.py to read its default enabled state and any additional build options it exposes.
config.py may define any of the following Python functions:
| Function | Arguments | Return | Required |
|---|---|---|---|
is_enabled() | — | bool | No (defaults to True) |
get_opts(platform) | str | list of SCons Variables | No |
can_build(env, platform) | env, str | bool | No (called from some modules' SCsubs) |
is_enabled(): Whether the module is enabled by default when modules_enabled_by_default=yes. If absent, the module defaults to enabled.get_opts(platform): Returns additional platform-specific SCons Variables entries (e.g. paths to SDKs). These are added globally to the option set.can_build(env, platform): Used by some modules (typically called from the module's SCsub or dependency check) to test whether system prerequisites are met.For each detected module, a BoolVariable named module_<name>_enabled is registered. Users can override any module's state on the command line (e.g., module_gdscript_enabled=no).
The global option modules_enabled_by_default (default yes) controls whether newly detected modules are on or off unless individually overridden.
Modules can declare dependencies on other modules using two helper methods that are mixed into the SCons env object:
| Method | Description |
|---|---|
env.module_add_dependencies(module, dependencies, optional=False) | Registers required or optional inter-module dependencies |
env.module_check_dependencies(module) | Checks all required dependencies are enabled; disables the module with a warning if they are not |
These are typically called from a module's can_build logic or from its config.py. Required dependencies that fail cause the dependent module to be added to env.disabled_modules and emit a warning.
After all modules are configured, sort_module_list(env) topologically sorts env.module_list so that dependencies are always linked before dependents.
Diagram: Module Dependency Resolution
Sources: methods.py325-379
The file modules/SCsub is the SCons entry point for building all modules. It runs after all module options have been resolved.
Steps performed:
env into env_modules and append the GODOT_MODULE preprocessor define so module code can detect its build context.modules/modules_enabled.gen.h — a header of #define MODULE_<NAME>_ENABLED entries for every active module.modules/register_module_types.gen.cpp — the registration glue code.env.module_list, call SConscript("<path>/SCsub"). The module's SCsub populates env.modules_sources. If non-empty, a libmodule_<name>.a is built and prepended to LIBS.tests=yes, collect *.h files from each module's tests/ subdirectory, then generate modules/modules_tests.gen.h.libmodules.a containing only register_module_types.gen.cpp. This library is prepended last so all individual libmodule_* libraries appear to its left in the linker command.Diagram: modules/SCsub Build Flow
Sources: modules/SCsub1-63
modules/modules_builders.py contains three builder functions called by SCons during the build:
modules/modules_builders.py1-57
modules_enabled_builderGenerates modules/modules_enabled.gen.h. For each active module name, writes:
This header is included throughout the engine to conditionally compile module-dependent code.
register_module_types_builderGenerates modules/register_module_types.gen.cpp. Produces:
#include line for each module's register_types.h.void initialize_modules(ModuleInitializationLevel p_level) — calls initialize_<name>_module(p_level) for each module, guarded by #ifdef MODULE_<NAME>_ENABLED.void uninitialize_modules(ModuleInitializationLevel p_level) — calls uninitialize_<name>_module(p_level) in reverse order, similarly guarded.modules_tests_builderGenerates modules/modules_tests.gen.h (only when tests=yes). Includes all tests/*.h headers found across active modules.
Diagram: Generated File Relationships
Sources: modules/modules_builders.py1-57 modules/SCsub18-63
A module's SCsub receives the env and env_modules variables via SCons's Import. The primary responsibility is populating env.modules_sources.
The env_modules environment carries the GODOT_MODULE define and any module-specific modifications made by the parent. The module library name is determined by the parent modules/SCsub (libmodule_<name>.a), not by the module's own SCsub.
Sources: modules/SCsub33-42
Custom modules outside the modules/ directory are supported via the custom_modules build option.
scons custom_modules=/path/to/my_modules
scons custom_modules=/path/to/dir1,/path/to/dir2
Behaviour differences from built-in modules:
custom_modules_recursive=yes).CPPPATH, enabling headers to be referenced as #include "summator/summator.h" regardless of where the modules directory lives on disk.OrderedDict update order ensures last write wins).is_engine(path) checks for a version.py containing short_name = "godot" and skips it.methods.py313-322 SConstruct439-462
| Option | Default | Description |
|---|---|---|
modules_enabled_by_default | yes | If no, all modules default to disabled unless explicitly enabled |
module_<name>_enabled | per-module is_enabled() | Enable or disable a specific module |
custom_modules | "" | Comma-separated list of directories to search for custom modules |
custom_modules_recursive | yes | Search custom_modules paths recursively |
Sources: SConstruct273-275 SConstruct464-492
Diagram: Full Module Pipeline from SCons to Binary
Sources: SConstruct435-492 modules/SCsub1-63 modules/modules_builders.py1-57 methods.py240-379
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.