Unverified Commit 907d7fa0 authored by Thomas Vos's avatar Thomas Vos Committed by GitHub

Add linuxArm64 target (#1051)

This PR adds support for the linuxArm64 target. It can be cross compiled
from a Linux x64 host.

The `multistrap` package is used for downloading some required arm64
dependencies.

# Testing

GitHub run: https://github.com/Thomas-Vos/skiko/actions/runs/15261348943

Also using it in a compose app.
parent b4f463b9
......@@ -164,7 +164,10 @@ jobs:
sudo apt-get install gcc-9 g++-9 -y
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9
sudo update-alternatives --config gcc
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip xvfb -y
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip multistrap xvfb -y
sudo apt-get install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu -y
sudo update-alternatives --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc /usr/bin/aarch64-linux-gnu-gcc-9 60 --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ /usr/bin/aarch64-linux-gnu-g++-9
sudo update-alternatives --config aarch64-linux-gnu-gcc
sudo Xvfb :0 -screen 0 1280x720x24 &
- shell: bash
......@@ -173,6 +176,12 @@ jobs:
export DISPLAY=:0
./gradlew --no-daemon --stacktrace --info -Pskiko.native.enabled=true -Pkotlin.native.cacheKind.linuxX64=none -Pskiko.test.onci=true :skiko:linuxX64Test
# Linux arm64 tests are executed on a separate runner
- shell: bash
name: 'Compile Linux arm64 tests'
run: |
./gradlew --no-daemon --stacktrace --info -Pskiko.native.enabled=true -Pskiko.test.onci=true :skiko:linkDebugTestLinuxArm64
- shell: bash
name: 'Compile and run AWT tests'
run: |
......@@ -197,6 +206,79 @@ jobs:
path: ./skiko/build/reports/tests
retention-days: 5
- uses: actions/upload-artifact@v4
name: 'Save linuxArm64 test binary as artifact'
with:
name: test-binary-linuxArm64
path: ./skiko/build/bin/linuxArm64
if-no-files-found: error
retention-days: 5
linux-arm:
needs: linux
runs-on: ubuntu-22.04-arm
steps:
- uses: actions/checkout@v3
name: 'Check out code'
- uses: actions/setup-java@v3
name: 'Set up JDK 11'
with:
distribution: 'adopt'
java-version: '11'
cache: 'gradle'
- uses: actions/download-artifact@v4
with:
name: test-binary-linuxArm64
path: ./skiko/build/bin/linuxArm64
- shell: bash
name: 'Set up Linux build environment'
run: |
# TODO: simplify installation of compilers.
sudo apt-get update -y
sudo apt-get install build-essential software-properties-common -y
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
sudo apt-get update -y
sudo apt-get install build-essential software-properties-common -y
sudo apt-get update
sudo apt-get install clang-11 -y
sudo apt-get remove g++ -y
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-11 100
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-11 100
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip xvfb -y
sudo Xvfb :0 -screen 0 1280x720x24 &
- shell: bash
name: 'Run Linux arm64 tests'
working-directory: ./skiko
run: |
export DISPLAY=:0
chmod +x ./build/bin/linuxArm64/debugTest/test.kexe
./build/bin/linuxArm64/debugTest/test.kexe
- shell: bash
name: 'Compile and run AWT tests'
run: |
export DISPLAY=:0
./gradlew --no-daemon --stacktrace --info -Pskiko.test.onci=true :skiko:awtTest
timeout-minutes: 25
- shell: bash
name: 'Publish to Maven Local, check AWT sample'
run: |
./gradlew --no-daemon --stacktrace --info :skiko:publishToMavenLocal
./gradlew --no-daemon --stacktrace --info :SkiaAwtSample:installDist
- uses: actions/upload-artifact@v4
name: 'Save test results as artifact'
if: always()
with:
name: test-reports-linux-arm
path: ./skiko/build/reports/tests
retention-days: 5
windows:
runs-on: windows-2022
steps:
......
......@@ -37,7 +37,10 @@ jobs:
sudo apt-get install gcc-9 g++-9 -y
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9
sudo update-alternatives --config gcc
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip xvfb -y
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip multistrap xvfb -y
sudo apt-get install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu -y
sudo update-alternatives --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc /usr/bin/aarch64-linux-gnu-gcc-9 60 --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ /usr/bin/aarch64-linux-gnu-g++-9
sudo update-alternatives --config aarch64-linux-gnu-gcc
sudo Xvfb :0 -screen 0 1280x720x24 &
- name: 'Build Dokka documentation'
......
......@@ -4,7 +4,7 @@
* `macOs` Install Xcode Command Line Tools
* `Linux` Install these tools:
```
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip libx11-dev
sudo apt-get install ninja-build fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev zip multistrap libx11-dev
```
* `Windows`
1. Download [Visual Studio Build Tools 2019](https://learn.microsoft.com/en-us/visualstudio/releases/2019/history) (search "BuildTools" on the page).
......
......@@ -142,6 +142,7 @@ kotlin {
}
if (supportNativeLinux) {
skikoProjectContext.configureNativeTarget(OS.Linux, Arch.X64, linuxX64())
skikoProjectContext.configureNativeTarget(OS.Linux, Arch.Arm64, linuxArm64())
}
if (supportNativeIosArm64) {
skikoProjectContext.configureNativeTarget(OS.IOS, Arch.Arm64, iosArm64())
......@@ -292,6 +293,12 @@ kotlin {
val linuxX64Test by getting {
dependsOn(linuxTest)
}
val linuxArm64Main by getting {
dependsOn(linuxMain)
}
val linuxArm64Test by getting {
dependsOn(linuxTest)
}
}
if (supportAnyNativeIos || supportNativeMac) {
val darwinMain by creating {
......
......@@ -35,11 +35,16 @@ val OS.isCompatibleWithHost: Boolean
OS.Android -> true
}
fun compilerForTarget(os: OS, arch: Arch): String =
fun compilerForTarget(os: OS, arch: Arch, isJvm: Boolean = false): String =
when (os) {
// TODO: Use clang++ for all Linux targets
OS.Linux -> when (arch) {
Arch.X64 -> "g++"
Arch.Arm64 -> "clang++"
Arch.Arm64 -> if (isJvm) {
"clang++"
} else {
if (hostArch == Arch.Arm64) "g++" else "aarch64-linux-gnu-g++"
}
Arch.Wasm -> "Unexpected combination: $os & $arch"
}
OS.Android -> "clang++"
......@@ -48,8 +53,8 @@ fun compilerForTarget(os: OS, arch: Arch): String =
OS.Wasm -> "emcc"
}
fun linkerForTarget(os: OS, arch: Arch): String =
if (os.isWindows) "lld-link.exe" else compilerForTarget(os, arch)
fun linkerForTarget(os: OS, arch: Arch, isJvm: Boolean = false): String =
if (os.isWindows) "lld-link.exe" else compilerForTarget(os, arch, isJvm)
val OS.dynamicLibExt: String
get() = when (this) {
......
......@@ -66,7 +66,7 @@ fun SkikoProjectContext.createCompileJvmBindingsTask(
includeHeadersNonRecursive(projectDir.resolve("src/jvmMain/cpp/include"))
includeHeadersNonRecursive(projectDir.resolve("src/commonMain/cpp/common/include"))
compiler.set(compilerForTarget(targetOs, targetArch))
compiler.set(compilerForTarget(targetOs, targetArch, isJvm = true))
val osFlags: Array<String>
when (targetOs) {
......@@ -237,7 +237,7 @@ fun SkikoProjectContext.createLinkJvmBindings(
buildSuffix.set("jvm")
buildTargetArch.set(targetArch)
buildVariant.set(buildType)
linker.set(linkerForTarget(targetOs, targetArch))
linker.set(linkerForTarget(targetOs, targetArch, isJvm = true))
when (targetOs) {
OS.MacOS -> {
......
......@@ -7,6 +7,7 @@ import SkiaBuildType
import SkikoProjectContext
import WriteCInteropDefFile
import compilerForTarget
import hostArch
import isCompatibleWithHost
import joinToTitleCamelCase
import listOfFrameworks
......@@ -51,13 +52,21 @@ fun SkikoProjectContext.compileNativeBridgesTask(
): TaskProvider<CompileSkikoCppTask> = with (this.project) {
val skiaNativeDir = registerOrGetSkiaDirProvider(os, arch, isUikitSim = isUikitSim)
val setupMultistrapTask = if (os == OS.Linux && arch == Arch.Arm64 && hostArch != Arch.Arm64) {
setupMultistrapTask(os, arch, isUikitSim = isUikitSim)
} else {
null
}
val actionName = "compileNativeBridges".withSuffix(isUikitSim = isUikitSim)
return project.registerSkikoTask<CompileSkikoCppTask>(actionName, os, arch) {
dependsOn(skiaNativeDir)
val unpackedSkia = skiaNativeDir.get()
compiler.set(compilerForTarget(os, arch))
setupMultistrapTask?.let { dependsOn(it) }
compiler.set(compilerForTarget(os, arch, isJvm = false))
buildTargetOS.set(os)
if (isUikitSim) {
buildSuffix.set("sim")
......@@ -126,8 +135,9 @@ fun SkikoProjectContext.compileNativeBridgesTask(
))
}
OS.Linux -> {
flags.set(listOf(
flags.set(listOfNotNull(
*buildType.clangFlags,
"-fPIC",
"-fno-rtti",
"-fno-exceptions",
"-fvisibility=hidden",
......@@ -239,8 +249,9 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
configureCinterop("uikit", os, arch, target, targetString, tvosFrameworks)
mutableListOfLinkerOptions(tvosFrameworks)
}
OS.Linux -> mutableListOfLinkerOptions(
"-L/usr/lib/x86_64-linux-gnu",
OS.Linux -> {
val options = mutableListOf(
"-L/usr/lib/${if (arch == Arch.Arm64) "aarch64" else "x86_64"}-linux-gnu",
"-lfontconfig",
"-lGL",
// TODO: an ugly hack, Linux linker searches only unresolved symbols.
......@@ -250,6 +261,12 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
"$skiaBinDir/libskunicode_icu.a",
"$skiaBinDir/libskia.a"
)
if (arch == Arch.Arm64 && hostArch != Arch.Arm64) {
options.add(0, "-L$buildDir/multistrap-arm64/usr/lib")
options.add(1, "-L$buildDir/multistrap-arm64/usr/lib/aarch64-linux-gnu")
}
mutableListOfLinkerOptions(options)
}
else -> mutableListOf()
}
if (skiko.includeTestHelpers) {
......@@ -308,6 +325,24 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin
}
}
/**
* This installs dependencies needed for cross compiling Skiko on Linux.
* https://youtrack.jetbrains.com/issue/KT-36871
*/
fun SkikoProjectContext.setupMultistrapTask(
os: OS, arch: Arch, isUikitSim: Boolean
): TaskProvider<Exec> = with (this.project) {
require(os == OS.Linux)
require(arch == Arch.Arm64)
val actionName = "setupMultistrap".withSuffix(isUikitSim = isUikitSim)
return project.registerSkikoTask<Exec>(actionName, os, arch) {
workingDir(projectDir)
commandLine("multistrap", "-f", "multistrap-config-${arch.id}")
}
}
fun KotlinMultiplatformExtension.configureIOSTestsWithMetal(project: Project) {
val metalTestTargets = listOf("iosX64", "iosSimulatorArm64")
metalTestTargets.forEach { target: String ->
......
......@@ -41,7 +41,7 @@ fun SkikoProjectContext.createWasmLinkTasks(): LinkWasmTasks = with(this.project
dependsOn(skiaWasmDir)
compiler.set(compilerForTarget(OS.Wasm, Arch.Wasm))
compiler.set(compilerForTarget(OS.Wasm, Arch.Wasm, isJvm = false))
buildTargetOS.set(osArch.first)
buildTargetArch.set(osArch.second)
buildVariant.set(buildType)
......@@ -73,7 +73,7 @@ fun SkikoProjectContext.createWasmLinkTasks(): LinkWasmTasks = with(this.project
dependsOn(skiaWasmDir)
val unpackedSkia = skiaWasmDir.get()
linker.set(linkerForTarget(OS.Wasm, Arch.Wasm))
linker.set(linkerForTarget(OS.Wasm, Arch.Wasm, isJvm = false))
buildTargetOS.set(osArch.first)
buildTargetArch.set(osArch.second)
buildVariant.set(buildType)
......
......@@ -19,6 +19,7 @@ val skikoArtifactIds: List<String> =
SkikoArtifacts.jsArtifactId,
SkikoArtifacts.wasmArtifactId,
SkikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.Linux, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.Arm64),
SkikoArtifacts.nativeArtifactIdFor(OS.MacOS, Arch.X64),
SkikoArtifacts.nativeArtifactIdFor(OS.IOS, Arch.X64),
......
[General]
arch=arm64
directory=build/multistrap-arm64
cleanup=true
noauth=false
unpack=true
cleanup=true
aptsources=Ubuntu
bootstrap=Ubuntu
[Ubuntu]
packages=fontconfig libfontconfig1-dev libglu1-mesa-dev
source=https://ports.ubuntu.com/
keyring=ubuntu-keyring
suite=jammy
......@@ -2,8 +2,10 @@ package org.jetbrains.skia
import org.jetbrains.skia.impl.interopScope
import org.jetbrains.skia.impl.use
import org.jetbrains.skiko.Arch
import org.jetbrains.skiko.KotlinBackend
import org.jetbrains.skiko.OS
import org.jetbrains.skiko.hostArch
import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.kotlinBackend
import org.jetbrains.skiko.tests.TestGlContext
......@@ -113,6 +115,10 @@ class SurfaceTest {
// TODO implement for other platforms and render targets
return
}
if (hostArch == Arch.Arm64) {
// TODO implement and test EGL on arm64
return
}
val pixels = TestGlContext.run {
DirectContext.makeGL().useContext { ctx ->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment