Platform-Specific Implementation Internal Documentation
Overview
godot-cpp implements platform-specific build configurations through modular Python scripts in the tools/ directory. Each platform has unique requirements for compilers, linkers, SDKs, and runtime libraries.
Platform Architecture
Platform Detection (godotcpp.py)
↓
Platform Tool Module (platform.py)
↓
Common Compiler Flags (common_compiler_flags.py)
↓
Platform-Specific Configuration
├── Compiler Selection
├── Architecture Flags
├── SDK/Toolchain Setup
└── Runtime Library Config
Windows Platform
Compiler Selection (MSVC vs MinGW)
Windows supports two distinct toolchains:
# tools/windows.py:90-106
def generate(env):
if not env["use_mingw"] and msvc.exists(env):
# MSVC Configuration
env["is_msvc"] = True
msvc.generate(env)
env.Tool("msvc")
env.Tool("mslib")
env.Tool("mslink")
# MSVC-specific defines
env.Append(CPPDEFINES=[
"TYPED_METHOD_BIND", # Required for method binding
"NOMINMAX" # Prevent Windows.h min/max macros
])
env.Append(CCFLAGS=["/utf-8"]) # UTF-8 source files
env.Append(LINKFLAGS=["/WX"]) # Linker warnings as errors
MSVC Runtime Library Configuration
# tools/windows.py:121-128
if env["debug_crt"]:
# Debug CRT with DLL runtime (thread_local requires DLL runtime)
env.AppendUnique(CCFLAGS=["/MDd"])
else:
if env["use_static_cpp"]:
env.AppendUnique(CCFLAGS=["/MT"]) # Static runtime
else:
env.AppendUnique(CCFLAGS=["/MD"]) # DLL runtime
MinGW Cross-Compilation
# tools/windows.py:162-189
# Architecture-specific prefixes
if env["arch"] == "x86_64":
prefix += "x86_64"
elif env["arch"] == "arm64":
prefix += "aarch64"
elif env["arch"] == "arm32":
prefix += "armv7"
elif env["arch"] == "x86_32":
prefix += "i686"
# Compiler selection (GCC or LLVM)
if env["use_llvm"]:
env["CXX"] = prefix + "-w64-mingw32-clang++"
env["CC"] = prefix + "-w64-mingw32-clang"
env["AR"] = prefix + "-w64-mingw32-llvm-ar"
else:
env["CXX"] = prefix + "-w64-mingw32-g++"
env["CC"] = prefix + "-w64-mingw32-gcc"
env["AR"] = prefix + "-w64-mingw32-gcc-ar"
# Static linking for portability
if env["use_static_cpp"]:
env.Append(LINKFLAGS=[
"-static",
"-static-libgcc",
"-static-libstdc++"
])
MSVC Output Silencing
# tools/windows.py:10-73
def silence_msvc(env):
"""Sophisticated output filtering for MSVC"""
# Captures cl.exe and link.exe output
# Filters compilation noise while preserving errors/warnings
def spawn_capture(sh, escape, cmd, args, env):
if args[0] not in ["cl", "link"]:
return old_spawn(sh, escape, cmd, args, env)
# Redirect to temp file
tmp_stdout, tmp_stdout_name = tempfile.mkstemp()
args.append(f">{tmp_stdout_name}")
ret = old_spawn(sh, escape, cmd, args, env)
# Process output, filter noise, keep errors
# Regex patterns identify actual errors vs normal output
Linux Platform
Compiler Configuration
# tools/linux.py:15-22
def generate(env):
if env["use_llvm"]:
clang.generate(env)
clangxx.generate(env)
elif env.use_hot_reload:
# Critical for proper extension unloading
env.Append(CXXFLAGS=["-fno-gnu-unique"])
# Position-independent code for shared libraries
env.Append(CCFLAGS=["-fPIC", "-Wwrite-strings"])
# RPATH for finding shared libraries relative to binary
env.Append(LINKFLAGS=["-Wl,-R,'$$ORIGIN'"])
Architecture-Specific Flags
# tools/linux.py:26-39
if env["arch"] == "x86_64":
env.Append(CCFLAGS=["-m64", "-march=x86-64"])
env.Append(LINKFLAGS=["-m64", "-march=x86-64"])
elif env["arch"] == "x86_32":
env.Append(CCFLAGS=["-m32", "-march=i686"])
env.Append(LINKFLAGS=["-m32", "-march=i686"])
elif env["arch"] == "arm64":
env.Append(CCFLAGS=["-march=armv8-a"])
env.Append(LINKFLAGS=["-march=armv8-a"])
elif env["arch"] == "rv64":
# RISC-V 64-bit with general extensions and compressed instructions
env.Append(CCFLAGS=["-march=rv64gc"])
env.Append(LINKFLAGS=["-march=rv64gc"])
Static Linking for Portability
# tools/linux.py:41-43
if env["use_static_cpp"]:
# Link C++ runtime statically for distribution
env.Append(LINKFLAGS=[
"-static-libgcc",
"-static-libstdc++"
])
env.Append(CPPDEFINES=["LINUX_ENABLED", "UNIX_ENABLED"])
macOS Platform
Universal Binary Support
# tools/macos.py:51-56
if env["arch"] == "universal":
# Build fat binary for both Intel and Apple Silicon
env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"])
env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"])
else:
env.Append(LINKFLAGS=["-arch", env["arch"]])
env.Append(CCFLAGS=["-arch", env["arch"]])
SDK and Deployment Target
# tools/macos.py:58-64
if env["macos_deployment_target"] != "default":
# Minimum macOS version for compatibility
env.Append(CCFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
env.Append(LINKFLAGS=["-mmacosx-version-min=" + env["macos_deployment_target"]])
if env["macos_sdk_path"]:
# Custom SDK path (useful for CI/CD)
env.Append(CCFLAGS=["-isysroot", env["macos_sdk_path"]])
env.Append(LINKFLAGS=["-isysroot", env["macos_sdk_path"]])
OSXCross Support
# tools/macos.py:32-48
# Cross-compilation from Linux using OSXCross
if has_osxcross():
root = os.environ.get("OSXCROSS_ROOT", "")
if env["arch"] == "arm64":
basecmd = root + "/target/bin/arm64-apple-" + env["osxcross_sdk"] + "-"
else:
basecmd = root + "/target/bin/x86_64-apple-" + env["osxcross_sdk"] + "-"
env["CC"] = basecmd + "clang"
env["CXX"] = basecmd + "clang++"
env["AR"] = basecmd + "ar"
env["RANLIB"] = basecmd + "ranlib"
# Add OSXCross to PATH for linking
binpath = os.path.join(root, "target", "bin")
env.PrependENVPath("PATH", binpath)
Android Platform
NDK Toolchain Setup
# tools/android.py:30-35
def get_android_ndk_root(env):
# Prioritize ANDROID_HOME with specific NDK version
if env["ANDROID_HOME"]:
return env["ANDROID_HOME"] + "/ndk/" + env["ndk_version"]
else:
# Fallback to ANDROID_NDK_ROOT environment variable
return os.environ.get("ANDROID_NDK_ROOT")
Architecture Configuration
# tools/android.py:77-102
arch_info_table = {
"arm32": {
"march": "armv7-a",
"target": "armv7a-linux-androideabi",
"compiler_path": "armv7a-linux-androideabi",
"ccflags": ["-mfpu=neon"], # NEON SIMD instructions
},
"arm64": {
"march": "armv8-a",
"target": "aarch64-linux-android",
"compiler_path": "aarch64-linux-android",
"ccflags": [],
},
"x86_32": {
"march": "i686",
"target": "i686-linux-android",
"compiler_path": "i686-linux-android",
"ccflags": ["-mstackrealign"], # Stack alignment for compatibility
},
"x86_64": {
"march": "x86-64",
"target": "x86_64-linux-android",
"compiler_path": "x86_64-linux-android",
"ccflags": [],
}
}
Toolchain Configuration
# tools/android.py:105-119
# LLVM-based toolchain (required for modern Android)
env["CC"] = toolchain + "/bin/clang"
env["CXX"] = toolchain + "/bin/clang++"
env["LINK"] = toolchain + "/bin/clang++"
env["AR"] = toolchain + "/bin/llvm-ar"
env["AS"] = toolchain + "/bin/llvm-as"
env["STRIP"] = toolchain + "/bin/llvm-strip"
env["RANLIB"] = toolchain + "/bin/llvm-ranlib"
# Target configuration with API level
env.Append(CCFLAGS=[
"--target=" + arch_info["target"] + env["android_api_level"],
"-march=" + arch_info["march"],
"-fPIC" # Position-independent code required
])
API Level Validation
# tools/android.py:50-53
if int(env["android_api_level"]) < 24:
print("WARNING: minimum supported Android target api is 24.")
env["android_api_level"] = "24"
iOS Platform
Simulator vs Device Configuration
# tools/ios.py:36-43
if env["ios_simulator"]:
sdk_name = "iphonesimulator"
env.Append(CCFLAGS=["-mios-simulator-version-min=" + env["ios_min_version"]])
env.Append(LINKFLAGS=["-mios-simulator-version-min=" + env["ios_min_version"]])
else:
sdk_name = "iphoneos"
env.Append(CCFLAGS=["-miphoneos-version-min=" + env["ios_min_version"]])
env.Append(LINKFLAGS=["-miphoneos-version-min=" + env["ios_min_version"]])
Universal Binary for iOS
# tools/ios.py:86-92
if env["arch"] == "universal":
if env["ios_simulator"]:
# Simulator: both x86_64 (Intel Mac) and arm64 (Apple Silicon Mac)
env.Append(LINKFLAGS=["-arch", "x86_64", "-arch", "arm64"])
env.Append(CCFLAGS=["-arch", "x86_64", "-arch", "arm64"])
else:
# Device: arm64 only (iPhone/iPad)
env.Append(LINKFLAGS=["-arch", "arm64"])
env.Append(CCFLAGS=["-arch", "arm64"])
SDK Path Detection
# tools/ios.py:46-54
if env["IOS_SDK_PATH"] == "":
try:
# Use xcrun to find SDK
env["IOS_SDK_PATH"] = subprocess.check_output(
["xcrun", "--sdk", sdk_name, "--show-sdk-path"]
).strip().decode("utf-8")
except (subprocess.CalledProcessError, OSError):
raise ValueError(f"Failed to find SDK path for {sdk_name}")
# Apply SDK settings
env.Append(CCFLAGS=["-isysroot", env["IOS_SDK_PATH"]])
env.Append(LINKFLAGS=["-isysroot", env["IOS_SDK_PATH"]])
Web Platform
Emscripten Configuration
# tools/web.py:14-19
# Emscripten toolchain
env["CC"] = "emcc"
env["CXX"] = "em++"
env["AR"] = "emar"
env["RANLIB"] = "emranlib"
# tools/web.py:20-23
# Handle long command lines on Windows
env["ARCOM_POSIX"] = env["ARCOM"].replace("$TARGET", "$TARGET.posix")
.replace("$SOURCES", "$SOURCES.posix")
env["ARCOM"] = "${TEMPFILE(ARCOM_POSIX)}"
WebAssembly Configuration
# tools/web.py:33-34
# Output WebAssembly module
env["SHLIBSUFFIX"] = ".wasm"
# tools/web.py:36-39
# Thread support via SharedArrayBuffer
if env["threads"]:
env.Append(CCFLAGS=["-sUSE_PTHREADS=1"])
env.Append(LINKFLAGS=["-sUSE_PTHREADS=1"])
# tools/web.py:41-43
# Build as side module (dynamic library)
env.Append(CCFLAGS=["-sSIDE_MODULE=1"])
env.Append(LINKFLAGS=["-sSIDE_MODULE=1"])
WebAssembly Features
# tools/web.py:45-47
# Enable BigInt for 64-bit integer support
# Must match Godot's build configuration
env.Append(LINKFLAGS=["-sWASM_BIGINT"])
# tools/web.py:49-51
# Force WebAssembly-native exception handling
env.Append(CCFLAGS=["-sSUPPORT_LONGJMP='wasm'"])
env.Append(LINKFLAGS=["-sSUPPORT_LONGJMP='wasm'"])
Common Compiler Flags
C++ Standard and Exceptions
# tools/common_compiler_flags.py:33-47
# C++17 required
if env.get("is_msvc", False):
env.Append(CXXFLAGS=["/std:c++17"])
else:
env.Append(CXXFLAGS=["-std=c++17"])
# Disable exceptions for size/performance
if env["disable_exceptions"]:
if env.get("is_msvc", False):
env.Append(CPPDEFINES=[("_HAS_EXCEPTIONS", 0)])
else:
env.Append(CXXFLAGS=["-fno-exceptions"])
Optimization Levels
# tools/common_compiler_flags.py:113-123
if env["optimize"] == "speed":
env.Append(CCFLAGS=["-O3"])
elif env["optimize"] == "speed_trace":
# Better debugging than -O3
env.Append(CCFLAGS=["-O2"])
elif env["optimize"] == "size":
env.Append(CCFLAGS=["-Os"])
elif env["optimize"] == "debug":
env.Append(CCFLAGS=["-Og"])
elif env["optimize"] == "none":
env.Append(CCFLAGS=["-O0"])
Link-Time Optimization
# tools/common_compiler_flags.py:125-133
if env["lto"] == "thin":
# ThinLTO (LLVM only)
if not env["use_llvm"]:
print("ThinLTO requires LLVM")
env.Exit(255)
env.Append(CCFLAGS=["-flto=thin"])
env.Append(LINKFLAGS=["-flto=thin"])
elif env["lto"] == "full":
# Full LTO
env.Append(CCFLAGS=["-flto"])
env.Append(LINKFLAGS=["-flto"])
Debug Symbols
# tools/common_compiler_flags.py:92-105
if env["debug_symbols"]:
# DWARF-4 for better compatibility
env.Append(CCFLAGS=["-gdwarf-4"])
if using_emcc(env):
# Emscripten needs -g3 for full debug info
env.AppendUnique(CCFLAGS=["-g3"])
env.AppendUnique(LINKFLAGS=["-gdwarf-4", "-g3"])
elif env.dev_build:
env.Append(CCFLAGS=["-g3"]) # Maximum debug info
else:
env.Append(CCFLAGS=["-g2"]) # Standard debug info
Symbol Visibility
# tools/common_compiler_flags.py:49-55
if env["symbols_visibility"] == "visible":
env.Append(CCFLAGS=["-fvisibility=default"])
env.Append(LINKFLAGS=["-fvisibility=default"])
elif env["symbols_visibility"] == "hidden":
# Hide symbols by default (reduces binary size)
env.Append(CCFLAGS=["-fvisibility=hidden"])
env.Append(LINKFLAGS=["-fvisibility=hidden"])
Cross-Compilation
Host vs Target Detection
# Platform detection logic
def detect_cross_compilation(env):
host_platform = platform.system().lower()
target_platform = env["platform"]
is_cross_compile = host_platform != target_platform
if is_cross_compile:
print(f"Cross-compiling from {host_platform} to {target_platform}")
setup_cross_toolchain(env, target_platform)
Toolchain Path Management
# Common pattern for cross-compilation toolchains
def setup_cross_toolchain(env, target):
toolchain_root = get_toolchain_root(target)
# Prepend toolchain to PATH
env.PrependENVPath("PATH", os.path.join(toolchain_root, "bin"))
# Set compiler prefix
prefix = get_target_triple(target)
env["CC"] = prefix + "-gcc"
env["CXX"] = prefix + "-g++"
env["AR"] = prefix + "-ar"
env["RANLIB"] = prefix + "-ranlib"
SDK Management
# SDK location patterns
sdk_paths = {
"android": "$ANDROID_HOME/ndk/$version",
"ios": "xcrun --sdk $sdk --show-sdk-path",
"macos": "$OSXCROSS_ROOT/target/SDK",
"windows": "$MINGW_PREFIX",
}
# Sysroot configuration
env.Append(CCFLAGS=["-isysroot", sdk_path])
env.Append(LINKFLAGS=["-isysroot", sdk_path])
Platform-Specific Defines
Each platform sets specific preprocessor defines:
platform_defines = {
"windows": ["WINDOWS_ENABLED", "TYPED_METHOD_BIND", "NOMINMAX"],
"linux": ["LINUX_ENABLED", "UNIX_ENABLED"],
"macos": ["MACOS_ENABLED", "UNIX_ENABLED"],
"android": ["ANDROID_ENABLED", "UNIX_ENABLED"],
"ios": ["IOS_ENABLED", "UNIX_ENABLED"],
"web": ["WEB_ENABLED", "UNIX_ENABLED"],
}
Platform-Specific Libraries
Runtime Library Linking
# Windows: Static vs Dynamic CRT
if static:
msvc_flags = ["/MT"] # Static runtime
mingw_flags = ["-static", "-static-libgcc", "-static-libstdc++"]
else:
msvc_flags = ["/MD"] # Dynamic runtime
mingw_flags = [] # Dynamic by default
# Linux/macOS: Static libstdc++ for portability
if static:
unix_flags = ["-static-libgcc", "-static-libstdc++"]
Platform Library Suffixes
library_suffixes = {
"windows": {
"static": ".lib" if msvc else ".a",
"shared": ".dll",
"import": ".lib" if msvc else ".a"
},
"linux": {
"static": ".a",
"shared": ".so"
},
"macos": {
"static": ".a",
"shared": ".dylib"
},
"android": {
"static": ".a",
"shared": ".so"
},
"ios": {
"static": ".a",
"shared": ".dylib" # Frameworks use .framework
},
"web": {
"static": ".a",
"shared": ".wasm"
}
}
Build Output Organization
# Platform-specific output paths
def get_output_path(env):
# Binary naming: libgodot-cpp.{platform}.{target}.{arch}{suffix}
platform = env["platform"]
target = env["target"]
arch = env["arch"]
suffix = f".{platform}.{target}"
if env.dev_build:
suffix += ".dev"
if env["precision"] == "double":
suffix += ".double"
suffix += f".{arch}"
if env.get("ios_simulator"):
suffix += ".simulator"
return f"bin/libgodot-cpp{suffix}{env['LIBSUFFIX']}"
Summary
The platform-specific implementation in godot-cpp demonstrates sophisticated cross-platform build management:
- Compiler Abstraction: Unified interface across MSVC, GCC, Clang, Emscripten
- Architecture Support: x86, x64, ARM, ARM64, RISC-V, WebAssembly
- SDK Integration: Automatic detection and configuration of platform SDKs
- Cross-Compilation: Full support for building from any host to any target
- Runtime Libraries: Flexible static/dynamic linking configurations
- Platform Features: Thread support, SIMD instructions, debug symbols
- Optimization: Platform-specific optimization defaults and LTO support
Each platform implementation carefully balances performance, compatibility, and ease of use while maintaining consistent behavior across all supported targets.
Supported Platforms: 6 (Windows, Linux, macOS, Android, iOS, Web) Supported Architectures: 9+ per platform Compiler Support: MSVC, GCC, Clang, MinGW, Emscripten Cross-compilation: Full matrix of host/target combinations