Cross-Platform Mobile C++ Development with CMake: A Unified Build System for iOS, Android, Windows, and macOS


Introduction
Have you ever dreamed of writing C++ code once and deploying it seamlessly across iOS, Android, Windows, and macOS? I recently embarked on this journey while developing a mobile hardware ray tracing renderer. The goal was ambitious: create a framework that rivals the convenience of modern web development tools, but for C++ applications targeting all major platforms.
The good news? In 2025, this is not only possible but surprisingly achievable. I've open-sourced the result as Sparkle: A Cross-Platform Hardware Ray Tracing Renderer.
Why Now?
The C++ ecosystem has evolved significantly:
- Modern C++ - The standard library is richer than ever, and tools like vcpkg have revolutionized package management
- Universal Clang Support - All platforms now support Clang, with Android and macOS using it as their default compiler
- Mature CMake - While still quirky, CMake has become the de facto standard for cross-platform C++ builds
- Platform Evolution - Each platform has developed mature toolchains that still embrace C++
- Graphics API Convergence - Vulkan's cross-platform design and the convergence of graphics APIs make unified rendering possible
- Python Everywhere - Python's ubiquity provides a "free" cross-platform scripting solution for complex build automation
- AI Assistance - Modern AI tools can help navigate documentation, automate tedious tasks, and debug complex build issues
The Magic: One Command, Any Platform
Here's what the final build system looks like:
# Build for Windows (on Windows)
python3 build.py --framework glfw --config Release
# Build and run on macOS
python3 build.py --framework glfw --run
# Generate Visual Studio project (Windows)
python3 build.py --framework glfw --generate_only
# Build Android APK and deploy to connected device
python3 build.py --framework android --run
# Build for iOS with hardware ray tracing
python3 build.py --framework ios --config Release --run --pipeline gpu
The result? Identical rendering output across all platforms:
Design Philosophy
Core Principles
- Platform-Agnostic by Default - Use standard library and cross-platform libraries wherever possible
- Isolate Platform-Specific Code - Keep platform dependencies contained and minimal
- Minimize Macros - Reserve them only for compiler/platform-specific needs
- Source Dependencies Over Binaries - Increases build time but dramatically reduces maintenance overhead
- Python Over CMake/C++ - Use Python for complex build logic instead of CMake scripting
- Modern Language Features - Embrace the latest C++ standards and compiler features
- Compiler as Guardian - Enable all warnings, treat warnings as errors, enforce code style
- Unified Toolchain - Standardize on Clang across platforms while maintaining native toolchain compatibility
- Configuration Over Convention - Make features toggleable for easier debugging and experimentation
Strategic Trade-offs
- Latest Everything - Support only the newest toolchains, SDKs, and hardware to leverage cutting-edge features
- Focused Feature Set - Implement representative, cross-platform solutions rather than comprehensive feature coverage
- Delayed Optimization - Prioritize stability and maintainability over premature optimization
Architecture Overview
Module Structure
sparkle/
├── libraries/ # Platform-independent modules
│ ├── core/ # Logging, math, threading, profiling
│ ├── io/ # Resource loading and file systems
│ ├── scene/ # Scene graph and nodes
│ ├── renderer/ # Rendering pipeline
│ ├── rhi/ # Graphics API abstraction
│ └── application/ # Main loop and initialization
├── frameworks/ # Platform-specific implementations
│ ├── glfw/ # Desktop (Windows/macOS)
│ ├── android/ # Android-specific code
│ ├── apple/ # Shared Apple platform code
│ ├── macos/ # macOS native support
│ └── ios/ # iOS native support
├── shaders/ # Cross-platform shaders
├── resources/ # Assets and configurations
├── build_system/ # Build scripts and platform resources
└── thirdparty/ # Dependencies
CMake Architecture
The build system uses three strategically designed CMake files:
- Main CMake - Manages global parameters, creates core libraries, handles platform-specific configurations
- Dependencies CMake - Consolidates all third-party dependencies into a single static library
- Shader CMake - Manages shader compilation (likely to be replaced with Python/C++ in the future)
Key Dependencies
- GLFW - Mature cross-platform windowing for desktop platforms
- Eigen - Powerful math library (overkill for graphics, but incredibly robust)
- Dear ImGui - Lightweight, flexible immediate-mode GUI
- spdlog - Fast, feature-rich logging
- cpptrace - Cross-platform stack traces for debugging
Platform Implementation Details
Windows: The Reference Implementation
Windows development is straightforward thanks to GLFW and Microsoft's legendary backward compatibility. The interesting part is Clang on Windows support:
- Install LLVM toolchain via Visual Studio Installer (clang-cl)
- Develop without touching Visual Studio IDE
- Unified compiler behavior across all platforms
- Note: VS ships Clang 16 while official Clang is at 18+
macOS: Native Metal Support
Sparkle provides two macOS implementations:
- GLFW - Works out of the box with MoltenVK for Vulkan translation
- Native - Required for Metal ray tracing support
The native implementation bridges C++ and Apple's frameworks:
// Key components connected via Storyboard
- AppDelegate → Application lifecycle
- MetalView → Rendering context and swapchain
- ViewController → Window management and events
- Storyboard → Wires everything together
iOS: Mobile Challenges
iOS support required several adaptations:
- Build System - Using ios-cmake for proper toolchain configuration
- Code Signing - Automatic signing for local development (no paid developer account required)
- Touch Events - Custom UIKit integration for touch handling
- ImGui Backend - Custom implementation for iOS touch and rendering
- File System - Sandbox-aware file handling using NSBundle and app directories
Android: GameActivity Integration
Android's approach uses GameActivity for C++ integration:
- Create a Kotlin/Java class extending GameActivity
- Load the C++ library from this class
- Implement GameActivity hooks in C++
- Configure AndroidManifest.xml to use your GameActivity
- Link everything through gradle's externalNativeBuild
The Build System Magic
The Python-based build system provides consistent commands across all platforms:
Features
- Platform Selection - Target any supported platform
- Build Configurations - Debug/Release modes
- Instant Run - Build and deploy in one command
- Mobile Deployment - Direct USB deployment to devices
- IDE Integration - Generate native project files
- LSP Support - Export compile_commands.json for clangd
- Advanced Options - Shader debugging, ASAN, profiling
Automatic Dependency Management
The build system handles most dependencies automatically:
- Git Submodules - Checked and updated on each build
- CMake FetchContent - Downloaded during configuration
- Vulkan SDK - Silent installation to project directory
- Binary Dependencies - Via system package manager or vcpkg fallback
- macOS Tools - LLVM and CMake via Homebrew
Manual installation is still required for:
- Visual Studio
- Xcode
- Android Studio
- Platform-specific SDKs
Tested Platforms
Platform | OS | CPU | GPU |
Windows | Windows 11 | Ryzen 5975WX | GeForce RTX 4080 |
Android | Android 13 | Snapdragon 8 Gen2 | Adreno 740 |
macOS | macOS 15.5 | Apple M3 Pro | Apple M3 Pro |
iOS | iOS 18.5 | Apple A18 | Apple A18 |
Conclusion
Building a truly cross-platform C++ application framework is challenging but achievable. The key is embracing modern tools, making strategic trade-offs, and maintaining a clear separation between platform-agnostic and platform-specific code.
Sparkle demonstrates that with careful design, you can achieve the "write once, run everywhere" dream for C++ applications, even with advanced features like hardware ray tracing.
The project is actively developed and welcomes contributions. Whether you're building a game engine, a rendering system, or any cross-platform C++ application, I hope Sparkle's approach provides valuable insights for your journey.
Check out the full source code and documentation at github.com/tqjxlm/Sparkle.
Have you tackled cross-platform C++ development? What challenges did you face? Share your experiences in the comments below!
Subscribe to my newsletter
Read articles from tqjxlm directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
