Build System Architecture
The build system is centred on a convention plugin living in common/build-logic/. This is an included build whose compiled output is available on the classpath of every module build script that declares plugins { id("base-conventions") }.
Config Data Classes
All plugin, Godot, iOS, and build settings are loaded once into typed immutable data classes. Project build scripts access them through Project extension functions - the same call pattern used throughout the Kotlin ecosystem:
| Extension function | Data class | Source file |
|---|---|---|
loadPluginConfig() |
PluginConfig |
common/config/plugin.properties |
loadGodotConfig() |
GodotConfig |
common/config/godot.properties |
loadIosConfig() |
IosConfig |
ios/config/ios.properties + ios/config/spm_dependencies.json |
loadBuildConfig() |
BuildConfig |
common/config/build.properties + all four *-build.properties |
Usage in any module build script:
plugins { id("base-conventions") }
val pluginConfig = loadPluginConfig()
val godotConfig = loadGodotConfig()
val iosConfig = loadIosConfig()
println(pluginConfig.pluginName) // "AdmobPlugin"
println(godotConfig.godotAarUrl) // full GitHub download URL
println(iosConfig.frameworks) // List<String> - already parsed
base-conventions Convention Plugin
Applying id("base-conventions") in a module build script:
- Loads all four config data classes.
- Bridges every scalar config value onto
project.extra(for compatibility withapply(from = …)scripts that cannot reference build-logic types directly). - Sets shared directory-layout extras (
pluginDir,repositoryRootDir,archiveDir,demoDir). - Applies the per-module user-defined extra properties and extra Gradle scripts from
BuildConfig, scoped byproject.path- so:androidonly receivesBuildConfig.androidExtraProperties/androidExtraGradle,:iosonly receives the iOS equivalents, and so on.
IosConfig List Fields
The frameworks, embeddedFrameworks, and linkerFlags fields on IosConfig are List<String>. The comma-separated values in ios/config/ios.properties are split and trimmed at load time, so consumers never need to parse delimiters:
val iosConfig = loadIosConfig()
iosConfig.frameworks // ["Foundation.framework", "Network.framework"]
iosConfig.embeddedFrameworks // [] when empty
iosConfig.linkerFlags // ["-ObjC"]
IosConfig SPM Dependencies
IosConfig also exposes a spmDependencies: List<SpmDependency> field, decoded at load time from ios/config/spm_dependencies.json. Each SpmDependency entry carries three fields:
| Field | Type | Description |
|---|---|---|
url |
String |
Git repository URL of the Swift package |
version |
String |
Minimum version requirement |
products |
List<String> |
SPM product names to link against |
val iosConfig = loadIosConfig()
iosConfig.spmDependencies // [SpmDependency(url="https://...", version="1.2.3", products=["ProductA"])]
base-conventions bridges this list onto project.extra["iosSpmDependencies"] so it is accessible from any task lambda that cannot reference IosConfig by type directly.
The addon-build.gradle.kts generateGDScript and generateSharedGDScript tasks expose the list via the @spmDependencies@ token. Each dependency is rendered as a GDScript dictionary literal using StringName key syntax (&"key"), and multiple entries are joined with , - without outer brackets, because they are supplied by the surrounding GDScript constant:
# Template source:
const SPM_DEPENDENCIES: Array = [ @spmDependencies@ ]
# After token replacement (two dependencies):
const SPM_DEPENDENCIES: Array = [ {&"url": "https://github.com/owner/repo", &"version": "1.2.3", &"products": ["ProductA", "ProductB"]}, {&"url": "https://github.com/other/pkg", &"version": "2.0.0", &"products": ["ProductC"]} ]