Default Kotlin native project gradle configuration looks like:
plugins {
kotlin("multiplatform") version "1.4.10"
}
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
binaries {
executable {
entryPoint = "main"
}
}
}
sourceSets {
val nativeMain by getting
val nativeTest by getting
}
}
Such configuration can produce binaries only for single target. How can be the configuration adjusted so that single build will produce 3 binary for all mentioned targets: Windows, Linux and MacOS?
You can just set a number of targets, and then running the assemble
task will produce binaries for all available on your host machine. It is important because one cannot create a binary for macOS anywhere but on macOS host, Windows target also has some complex preparations(see this issue). Also, there could be some problem with source sets - if their contents are identical, maybe it worth connecting them as in the example below:
kotlin {
macosX64("nativeMac")
linuxX64("nativeLin")
mingwX64("nativeWin")
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
binaries {
executable {
entryPoint = "main"
}
}
}
sourceSets {
val nativeMacMain by getting //let's assume that you work on Mac and so put all the code here
val nativeLinMain by getting {
dependsOn(nativeMacMain)
}
}
}
Here is a build.gradle.kts
that configures Gradle to build a Kotlin Multiplatform project with arm64 and x86-64 targets on Linux, arm64 and x86-64 targets on macOS, and a single x86-64 (mingw) target on Windows.
plugins {
val kotlinVersion = "1.9.21"
kotlin("multiplatform") version kotlinVersion
}
group = "org.example"
version = "1.0.0-SNAPSHOT"
repositories {
mavenCentral()
}
kotlin {
// Initialize all target platforms.
// Incompatible targets will be automatically skipped with a warning;
// we suppress such warnings by adding a line to gradle.properties:
// kotlin.native.ignoreDisabledTargets=true
val linuxArm64 = linuxArm64()
val linuxX64 = linuxX64()
val macosArm64 = macosArm64()
val macosX64 = macosX64()
val windows = mingwX64("windows")
// Configure which native targets to build, based on current platform.
val hostOs = System.getProperty("os.name")
val nativeTargets = when {
hostOs == "Linux" -> listOf(linuxArm64, linuxX64)
hostOs == "Mac OS X" -> listOf(macosArm64, macosX64)
hostOs.startsWith("Windows") -> listOf(windows)
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
// Define dependencies between source sets.
// We declare an intermediate source set called posix, which
// the Linux and macOS sources extend, but Windows does not.
// For further details, see:
// https://kotlinlang.org/docs/multiplatform-advanced-project-structure.html#declaring-custom-source-sets
sourceSets {
val posixMain by creating {
dependsOn(commonMain.get())
}
val macosArm64Main by getting {
dependsOn(posixMain)
}
val macosX64Main by getting {
dependsOn(posixMain)
}
val linuxArm64Main by getting {
dependsOn(posixMain)
}
val linuxX64Main by getting {
dependsOn(posixMain)
}
}
// Build the binaries for all activated native targets.
nativeTargets.forEach {
it.apply {
binaries {
executable {
entryPoint = "main"
}
}
}
}
}
It assumes the following directory structure for sources:
src
├── commonMain
│ ├── kotlin
├── linuxArm64Main
│ └── kotlin
├── linuxX64Main
│ └── kotlin
├── macosArm64Main
│ └── kotlin
├── macosX64Main
│ └── kotlin
├── posixMain
│ └── kotlin
└── windowsMain
└── kotlin
where src/posixMain/kotlin
is an area to put intermediate sources consumed by both Linux and macOS targets; I find is useful since Kotlin Native's platform.posix
library functions largely identically across those two platforms. (In my experience, platform.posix
is also very close when used with mingw, but there are more differences that can be problematic, so I like to implement my Windows-specific code using the platform.windows
library instead.)
I don't have an answer for cross-compiling all platforms at once, since as Artyom Degtyarev mentioned, there are extra challenges with that. But you can cross-compile CPU architecture using a structure like the above, building both arm64 and x86-64 for Linux and macOS, which allows to target more platforms using a CI pipeline such as GitHub Actions with an x86-64-only matrix configuration, and then assemble the final binaries into a single output artifact at the end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With