Nx Targets & Cross-Platform Builds
How we manage mobile, web, and native builds using the Nx DAG.
Platrium is a massive polyglot monorepo. We have Go for the backend, Rust for the core SDK, Kotlin for Android, Swift for iOS, and WASM for the Web.
To prevent this from devolving into a chaotic nightmare of Makefiles and Bash scripts, we rely entirely on the Nx Build System.
How We Use Nx Internally
Nx uses a Directed Acyclic Graph (DAG) to understand exactly how our projects relate to one another.
Instead of writing imperative scripts ("first build X, then build Y"), we declaratively define dependencies in our project.json files.
For example, look at how the Android SDK target is set up. It doesn't just run Gradle; it dictates a strict chain of events:
- The SDK depends on the API: The SDK cannot compile until the
apiproject successfully compiles the TypeSpec into OpenAPI YAML and generates the Rust client. - The FFI Build: The
sdkproject usescargo ndkto cross-compile the Rust code into Android.sobinaries. - UniFFI Generation: It then runs
uniffi-bindgento generate the Kotlin.ktfiles. - Gradle Packaging: Finally, it invokes
gradle assembleReleaseto package the Kotlin code and the C++ binaries into a clean Android.aarlibrary!
Because Nx tracks all of this, running nx build android-app will automatically trigger the entire chain all the way down to the TypeSpec compiler if needed, while aggressively caching anything that hasn't changed.
Adding New Targets
Adding a new target (like Windows Native, macOS Desktop, or a new mobile framework) is incredibly straightforward.
You simply define a new target in the project.json of the relevant project (usually sdk) using the nx:run-commands executor.
// Example of adding a new target in sdk/project.json
{
"targets": {
"generate-ffi-macos": {
"executor": "nx:run-commands",
"options": {
"commands": [
"cargo build --target aarch64-apple-darwin --release",
"cargo run --manifest-path uniffi/Cargo.toml -- generate --library ./target/aarch64-apple-darwin/release/libplatrium_sdk.dylib --language swift --out-dir ./_ffi/macos"
],
"parallel": false
},
"dependsOn": [
"^build" // This tells Nx to build the API generators first!
]
}
}
}By adding dependsOn: ["^build"], you ensure that the new target perfectly integrates into the existing DAG. You never have to worry about race conditions or building against outdated APIs!