Skip to content

Commit

Permalink
Cross-compile arm64 targets for Linux and macOS
Browse files Browse the repository at this point in the history
For compiling the native launcher portion of Jaunch on Linux,
this requires the gcc-aarch64-linux-gnu package to be installed,
for the aarch64-linux-gnu-gcc command line tool.

For the configurator portion of Jaunch, Gradle should take care
of bootstrapping the necessary compilation tools.

I had a heck of a time figuring out the correct Gradle ceremony,
even with the help of Internet searches and ChatGPT, so I posted
an answer on StackOverflow about it to hopefully help others:

    https://stackoverflow.com/a/77964683/1207769
  • Loading branch information
ctrueden committed Feb 8, 2024
1 parent 7302c93 commit 12aeba5
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 49 deletions.
30 changes: 28 additions & 2 deletions bin/compile-configurator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,35 @@ echo "gitHash -> $gitHash"
mv src/commonMain/kotlin/version.kt src/commonMain/kotlin/version.kt.original
sed -e "s/{{{VERSION}}}/$version/" -e "s/{{{GIT-HASH}}}/$gitHash/" src/commonMain/kotlin/version.kt.original > src/commonMain/kotlin/version.kt

# Call the appropriate Gradle targets for the current platform.
set +e
./gradlew --no-daemon linkReleaseExecutable
result=$?
platform=$(uname)
case "$platform" in
Linux)
(set -x; ./gradlew --no-daemon linkReleaseExecutableLinuxX64 linkReleaseExecutableLinuxArm64)
result=$?
;;
Darwin)
(set -x; ./gradlew --no-daemon linkReleaseExecutableMacosX64 linkReleaseExecutableMacosArm64)
result=$?
if [ "$result" -eq 0 ]
then
# Merge the macOS binaries into a Universal2 fat binary.
outDir=build/bin/macosUniversal/releaseExecutable
mkdir -p "$outDir"
(set -x; lipo -create -output "$outDir/jaunch.kexe" build/bin/macosArm64/releaseExecutable/jaunch.kexe build/bin/macosX64/jaunch.kexe)
result=$?
fi
;;
MINGW*|MSYS*)
(set -x; ./gradlew --no-daemon linkReleaseExecutableWindows)
result=$?
;;
*)
echo -e "\033[1;31m[ERROR] Unsupported platform: $platform\033[0m"
result=1
esac

# Replace the modified version.kt with its original content.
mv -f src/commonMain/kotlin/version.kt.original src/commonMain/kotlin/version.kt
exit $result
21 changes: 16 additions & 5 deletions bin/compile-launcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ test -d "$jdkdir" || jdkdir=$(test ! -x /usr/libexec/java_home || /usr/libexec/j
mkdir -p build

compile() {
(set -x; gcc \
compiler=$1
command -v "$compiler" >/dev/null 2>&1 || {
echo "[WARNING] Skipping invocation: $@"
return
}
shift
(set -x; "$compiler" \
-I"$jdkdir/include" \
-I"$jdkdir/include/linux" \
-I"$jdkdir/include/darwin" \
Expand All @@ -23,10 +29,15 @@ compile() {
}

case "$(uname)" in
Linux)
compile gcc -o build/launcher-linux-x64 &&
compile aarch64-linux-gnu-gcc -o build/launcher-linux-arm64
;;
Darwin)
compile -o build/launcher-x86_64 -target x86_64-apple-macos10.12 &&
compile -o build/launcher-arm64 -target arm64-apple-macos11 &&
(set -x; lipo -create -output build/launcher build/launcher-x86_64 build/launcher-arm64)
compile gcc -o build/launcher-macos-arm64 -target arm64-apple-macos11 &&
compile gcc -o build/launcher-macos-x64 -target x86_64-apple-macos10.12 &&
(set -x; lipo -create -output build/launcher build/launcher-macos-x64 build/launcher-macos-arm64)
;;
*) compile -o build/launcher ;;
MINGW*|MSYS*) compile -o build/launcher-windows-x64.exe ;;
*) compile gcc -o build/launcher ;;
esac
47 changes: 18 additions & 29 deletions bin/dist.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,42 +15,31 @@ copyBinary() {
if [ ! -f "$srcPath" ]; then return; fi
mkdir -p "$destDir"
(set -x; cp "$srcPath" "$destDir/$destName")
if [ "$makeExec" ]; then chmod +x "$destDir/$destName"; fi
if [ "$makeExec" ]; then (set -x; chmod +x "$destDir/$destName"); fi
}

# Clear out previous dist directory.
rm -rf dist

os=$(uname -s)
case "$os" in
Linux) os=linux ;;
Darwin) os=macos ;;
MINGW*|MSYS*) os=windows ;;
esac
arch=$(uname -m)
case "$arch" in
x86_64|amd64) arch=x64 ;;
arm64) arch=aarch64 ;;
esac
suffix="$os-$arch"

# Copy native launcher executable.
posixLauncherBinaryPath=build/launcher
posixLauncherBinaryType=$(file -b "$posixLauncherBinaryPath" 2>/dev/null)
case "$posixLauncherBinaryType" in
ELF*) copyBinary "$posixLauncherBinaryPath" dist "$appName-$suffix" true ;;
Mach-O*) copyBinary "$posixLauncherBinaryPath" dist/Contents/MacOS "$appName-$suffix" true ;;
esac
copyBinary build/launcher.exe dist "$appName-$suffix".exe
# Copy native launcher executables.
for launcherBinary in build/launcher*
do
targetFilename="$appName${launcherBinary#build/launcher}"
binaryType=$(file -b "$launcherBinary" 2>/dev/null)
case "$binaryType" in
ELF*) copyBinary "$launcherBinary" dist "$targetFilename" true ;;
Mach-O*) copyBinary "$launcherBinary" dist/Contents/MacOS "$targetFilename" true ;;
*) copyBinary "$launcherBinary" dist "$targetFilename" ;;
esac
done

# Copy jaunch configurator executables.
posixJaunchBinaryPath=build/bin/posix/releaseExecutable/jaunch.kexe
posixJaunchBinaryType=$(file -b "$posixJaunchBinaryPath" 2>/dev/null)
case "$posixJaunchBinaryType" in
ELF*) copyBinary "$posixJaunchBinaryPath" dist/jaunch "jaunch-$suffix" ;;
Mach-O*) copyBinary "$posixJaunchBinaryPath" dist/Contents/MacOS "jaunch-$suffix" ;;
esac
copyBinary build/bin/windows/releaseExecutable/jaunch.exe dist/jaunch "jaunch-$suffix.exe"
copyBinary build/bin/linuxArm64/releaseExecutable/jaunch.kexe dist/jaunch jaunch-linux-arm64 true
copyBinary build/bin/linuxX64/releaseExecutable/jaunch.kexe dist/jaunch jaunch-linux-x64 true
copyBinary build/bin/macosArm64/releaseExecutable/jaunch.kexe dist/Contents/MacOS jaunch-macos-arm64 true
copyBinary build/bin/macosX64/releaseExecutable/jaunch.kexe dist/Contents/MacOS jaunch-macos-x64 true
copyBinary build/bin/macosUniversal/releaseExecutable/jaunch.kexe dist/Contents/MacOS jaunch-macos true
copyBinary build/bin/windows/releaseExecutable/jaunch.exe dist/jaunch jaunch-windows-x64.exe

# Copy TOML configuration files.
if [ ! -f dist/jaunch/jaunch.toml ]
Expand Down
61 changes: 49 additions & 12 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,59 @@ repositories {
}

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 isArm64 = System.getProperty("os.arch") == "aarch64"
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" && isArm64 -> macosArm64("posix")
hostOs == "Mac OS X" && !isArm64 -> macosX64("posix")
hostOs == "Linux" && isArm64 -> linuxArm64("posix")
hostOs == "Linux" && !isArm64 -> linuxX64("posix")
isMingwX64 -> mingwX64("windows")
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.")
}

nativeTarget.apply {
binaries {
executable {
entryPoint = "main"
// 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())
}
@Suppress("UNUSED_PARAMETER")
val macosArm64Main by getting {
dependsOn(posixMain)
}
@Suppress("UNUSED_PARAMETER")
val macosX64Main by getting {
dependsOn(posixMain)
}
@Suppress("UNUSED_PARAMETER")
val linuxArm64Main by getting {
dependsOn(posixMain)
}
@Suppress("UNUSED_PARAMETER")
val linuxX64Main by getting {
dependsOn(posixMain)
}
}

// Build the binaries for all activated native targets.
nativeTargets.forEach {
it.apply {
binaries {
executable {
entryPoint = "main"
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
kotlin.code.style=official
kotlin.mpp.applyDefaultHierarchyTemplate=false
#kotlin.native.cacheKind.linuxX64=none
kotlin.native.ignoreDisabledTargets=true
org.gradle.jvmargs=-Xmx1g -XX:MaxMetaspaceSize=512m
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/Jaunch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ private fun help(executable: String?, programName: String, supportedOptions: Jau

printlnErr("Usage: $exeName [<Java options>.. --] [<main arguments>..]")
printlnErr()
printlnErr("$programName launcher (Jaunch v$JAUNCH_VERSION / build $JAUNCH_BUILD)")
printlnErr("$programName launcher (Jaunch v$JAUNCH_VERSION / $JAUNCH_BUILD / $BUILD_TARGET)")
printlnErr("Java options are passed to the Java Runtime,")
printlnErr("main arguments to the launched program ($programName).")
printlnErr()
Expand Down
2 changes: 2 additions & 0 deletions src/commonMain/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import kotlin.experimental.ExperimentalNativeApi

expect val BUILD_TARGET: String

expect fun execute(command: String): List<String>?

expect fun getcwd(): String
Expand Down
1 change: 1 addition & 0 deletions src/linuxArm64Main/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
actual val BUILD_TARGET = "linuxArm64"
1 change: 1 addition & 0 deletions src/linuxX64Main/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
actual val BUILD_TARGET = "linuxX64"
1 change: 1 addition & 0 deletions src/macosArm64Main/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
actual val BUILD_TARGET = "macosArm64"
1 change: 1 addition & 0 deletions src/macosX64Main/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
actual val BUILD_TARGET = "macosX64"
2 changes: 2 additions & 0 deletions src/windowsMain/kotlin/platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import platform.posix.*
import platform.posix.getenv as pGetEnv
import platform.windows.*

actual val BUILD_TARGET = "mingwX64"

@OptIn(ExperimentalForeignApi::class)
actual fun execute(command: String): List<String>? {
// Source: https://stackoverflow.com/a/69385366/1207769
Expand Down

0 comments on commit 12aeba5

Please sign in to comment.