mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-11-04 10:13:59 +00:00
feat(graalvm): enable GraalVM native image support and update packaging configurations
This commit is contained in:
parent
62c1fe7902
commit
aa83708499
58
.github/workflows/spotube-release-binary.yml
vendored
58
.github/workflows/spotube-release-binary.yml
vendored
@ -27,8 +27,8 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
COMPOSE_WEBVIEW_REPO: kdroidFilter/ComposeNativeWebview
|
||||
COMPOSE_WEBVIEW_COMMIT: 5aeb268a2c37ff4dbfb76924573344a7c4f763ce
|
||||
COMPOSE_WEBVIEW_REPO: KRTirtho/ComposeNativeWebview
|
||||
COMPOSE_WEBVIEW_COMMIT: cbd36f88927298a28a2d64c6acf7ec3b2def6f9d
|
||||
GRADLE_PLUGIN_REPO: team-spotube/gradle-plugin
|
||||
VLCJ_BUNDLER_REPO: team-spotube/vlcj-bundler-gradle-plugin
|
||||
|
||||
@ -181,7 +181,7 @@ jobs:
|
||||
- name: Install packaging tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y rpm fuse
|
||||
sudo apt-get install -y rpm fuse build-essential zlib1g-dev patchelf
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
@ -198,13 +198,19 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Cache GraalVM toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/nucleus/graalvm
|
||||
key: graalvm-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Package Linux
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Linux artifacts
|
||||
run: |
|
||||
DEB_FILE=$(find composeApp/build -name "*.deb" -type f | head -1)
|
||||
RPM_FILE=$(find composeApp/build -name "*.rpm" -type f | head -1)
|
||||
DEB_FILE=$(find composeApp/build/compose/binaries/main/graalvm-deb -maxdepth 1 -name "*.deb" -type f | head -1)
|
||||
RPM_FILE=$(find composeApp/build/compose/binaries/main/graalvm-rpm -maxdepth 1 -name "*.rpm" -type f | head -1)
|
||||
cp "$DEB_FILE" Spotube-linux-x86_64.deb
|
||||
cp "$RPM_FILE" Spotube-linux-x86_64.rpm
|
||||
|
||||
@ -242,7 +248,7 @@ jobs:
|
||||
- name: Install packaging tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y rpm fuse
|
||||
sudo apt-get install -y rpm fuse build-essential zlib1g-dev patchelf
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
@ -259,13 +265,19 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Cache GraalVM toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/nucleus/graalvm
|
||||
key: graalvm-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Package Linux
|
||||
run: ./gradlew :composeApp:packageReleaseDeb :composeApp:packageReleaseRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Linux artifacts
|
||||
run: |
|
||||
DEB_FILE=$(find composeApp/build -name "*.deb" -type f | head -1)
|
||||
RPM_FILE=$(find composeApp/build -name "*.rpm" -type f | head -1)
|
||||
DEB_FILE=$(find composeApp/build/compose/binaries/main/graalvm-deb -maxdepth 1 -name "*.deb" -type f | head -1)
|
||||
RPM_FILE=$(find composeApp/build/compose/binaries/main/graalvm-rpm -maxdepth 1 -name "*.rpm" -type f | head -1)
|
||||
cp "$DEB_FILE" Spotube-linux-aarch64.deb
|
||||
cp "$RPM_FILE" Spotube-linux-aarch64.rpm
|
||||
|
||||
@ -312,13 +324,25 @@ jobs:
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
# native-image links with MSVC
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Cache GraalVM toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/nucleus/graalvm
|
||||
key: graalvm-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Package EXE
|
||||
run: ./gradlew :composeApp:packageReleaseExe -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
run: ./gradlew :composeApp:packageGraalvmExe -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename Windows artifacts
|
||||
shell: pwsh
|
||||
run: |
|
||||
$EXE_FILE = Get-ChildItem -Path composeApp/build -Filter "*.exe" -Recurse | Select-Object -First 1
|
||||
# Top level only: subfolders hold the unpacked app, and __* are electron-builder helpers
|
||||
$EXE_FILE = Get-ChildItem -Path composeApp/build/compose/binaries/main/graalvm-exe -Filter "*.exe" |
|
||||
Where-Object { $_.Name -notlike '__*' } | Select-Object -First 1
|
||||
Copy-Item $EXE_FILE.FullName Spotube-windows-x86_64-setup.exe
|
||||
|
||||
- name: Upload EXE
|
||||
@ -361,12 +385,18 @@ jobs:
|
||||
- name: Make gradlew executable
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Cache GraalVM toolchain
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/nucleus/graalvm
|
||||
key: graalvm-${{ runner.os }}-${{ runner.arch }}
|
||||
|
||||
- name: Package DMG
|
||||
run: ./gradlew :composeApp:packageReleaseDmg -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
run: ./gradlew :composeApp:packageGraalvmDmg -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
|
||||
|
||||
- name: Rename macOS artifact
|
||||
run: |
|
||||
DMG_FILE=$(find composeApp/build -name "*.dmg" -type f | head -1)
|
||||
DMG_FILE=$(find composeApp/build/compose/binaries/main/graalvm-dmg -maxdepth 1 -name "*.dmg" -type f | head -1)
|
||||
cp "$DMG_FILE" Spotube-macos-universal.dmg
|
||||
|
||||
- name: Upload DMG
|
||||
|
||||
11
AGENTS.md
11
AGENTS.md
@ -22,6 +22,15 @@
|
||||
- Kotlin: `2.3.0`, JVM target: `11` (compile/target compatibility in both `composeApp/build.gradle.kts` and `plugin_interfaces/build.gradle.kts`).
|
||||
- **compose-webview (desktop) dispatcher hijack:** `dev.nucleusframework:composewebview` transitively pulls `nucleus.decorated-window-tao`, which registers a `TaoMainDispatcherFactory` (`loadPriority = 100`) via `META-INF/services` that hijacks `Dispatchers.Main` away from the Swing EDT. This breaks lifecycle's `enforceMainThreadIfNeeded` (window fails with "Method addObserver must be called on the main thread"). The app overrides it with `SwingMainDispatcherFactory` (`composeApp/src/jvmMain/.../core/coroutines/`, `loadPriority = Int.MAX_VALUE`) which pins `Dispatchers.Main` back to the Swing EDT. Keep that factory + its `META-INF/services/kotlinx.coroutines.internal.MainDispatcherFactory` resource; the long-term fix is in the webview (make the Tao dispatcher opt-in or lower its priority).
|
||||
|
||||
## GraalVM native image (desktop)
|
||||
- Enabled via `graalvm { }` inside `nucleus.application` in `composeApp/build.gradle.kts`. Nucleus downloads GraalVM CE itself (or uses `$GRAALVM_HOME`). Linux needs `gcc` + `patchelf`.
|
||||
- Build: `:composeApp:createGraalvmNativeDistributable` (output `composeApp/build/compose/binaries/main/graalvm-app/`), installers via `packageGraalvm{Deb,Rpm,AppImage,Dmg,Msi,Exe}`. A build takes ~10 min and uses all cores and ~15 GB RAM.
|
||||
- Hand-maintained + agent-recorded metadata lives in `composeApp/graalvm/reachability-metadata.json`. After adding features that use reflection/JNI/JNA (vlcj, Zipline, plugins), run `:composeApp:runWithNativeAgent`, exercise the feature, then close the window normally (the merge only happens on a clean exit).
|
||||
- `uk.co.caprica.vlcj.binding.lib.LibVlc` must stay registered (`allDeclaredMethods` + `jniAccessible`): JNA direct mapping finds its `native` methods by reflection.
|
||||
- The `*UberJarForCurrentOS` task excludes other OSes'/CPUs' native libs and stale CEF binaries, because native-image embeds every registered resource into the binary. On Linux, `copyGraalvmJvmSo` is disabled and `copyGraalvmAwtSoLibs` prefers native-image's own libs (10 KB `libjvm.so`/`libjava.so` shims instead of the JDK's 28 MB `libjvm.so`).
|
||||
- `isProductionBuild` (top of `composeApp/build.gradle.kts`) is true for `package*`/`create*`/`notarize*`/`nativeImageCompile` tasks, or with `-Pproduction=true|false`. It gates the Rust release profile (Gobley `jvmVariant`) so `:composeApp:run` keeps fast debug Rust builds. Put new slow, ship-only optimizations behind it.
|
||||
- Full user-facing build docs are in `README.md` ("Building from source").
|
||||
|
||||
## Codegen and plugin packaging
|
||||
- JS plugin bundles: `:js_plugin_example:packageDevelopmentPlugin` and `:js_plugin_example:packageProductionPlugin`. Output is `.smplug` files in `js_plugin_example/build/distributions/`.
|
||||
- Zipline plugin entrypoint: `mainFunction = "org.spotube.js_plugin_example.main"` in `js_plugin_example/build.gradle.kts`. Plugin metadata from `js_plugin_example/plugin.json`.
|
||||
@ -53,7 +62,7 @@
|
||||
- `prepare-deps` job checks out & publishes to `mavenLocal`:
|
||||
- `kdroidFilter/ComposeNativeWebview` (compose-webview)
|
||||
- `team-spotube/gradle-plugin` (spotubeGradle + vlcjBundler plugins)
|
||||
- Build jobs per platform (Android, Linux, Windows, macOS), all `needs: prepare-deps`.
|
||||
- Build jobs per platform (Android, Linux, Windows, macOS), all `needs: prepare-deps`. Desktop jobs ship GraalVM native installers (`packageGraalvm{Deb,Rpm,Exe,Dmg}`, output in `composeApp/build/compose/binaries/main/graalvm-<format>/`), not jpackage/JVM ones.
|
||||
- **Stable**: reads `versionName` from `composeApp/build.gradle.kts`, tag = `v{version}`, draft release.
|
||||
- **Nightly**: builds with `-PversionName=nightly`, tag = `nightly` (updates existing), prerelease.
|
||||
- **Android signing**: decodes `secrets.KEYSTORE` (base64) → `composeApp/upload-keystore.jks`, writes signing config into `local.properties` from secrets.
|
||||
|
||||
99
README.md
99
README.md
@ -47,7 +47,104 @@ Grab the latest nightly builds of Spotube [from the Offical Website](https://spo
|
||||
|
||||
<a href="https://github.com/team-spotube/spotube/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/KRTirtho/spotube/spotube-release-binary.yml?+label=Build%20Status"></a>
|
||||
|
||||
You can compile Spotube's source code by [following these instructions](CONTRIBUTION.md#your-first-code-contribution).
|
||||
Spotube is a Kotlin Multiplatform + Compose Multiplatform app. The desktop app is built with the [Nucleus](https://nucleusframework.dev) Gradle plugin and released as a **GraalVM native image**: an ahead-of-time compiled binary that needs no JVM installed.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **JDK 21** to run Gradle. The Gradle daemon JDK and the GraalVM toolchain are provisioned automatically.
|
||||
- **Rust** (`rustup` + `cargo`). The `composeApp` Rust library is built through [Gobley](https://gobley.dev).
|
||||
- **Android SDK**, only for Android builds.
|
||||
- **Unpublished dependencies.** Three dependencies aren't on Maven Central yet. Clone each one and run `./gradlew publishToMavenLocal` in it:
|
||||
- [kdroidFilter/ComposeNativeWebview](https://github.com/kdroidFilter/ComposeNativeWebview), at the commit pinned in [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml) (`COMPOSE_WEBVIEW_COMMIT`)
|
||||
- [team-spotube/gradle-plugin](https://github.com/team-spotube/gradle-plugin)
|
||||
- [team-spotube/vlcj-bundler-gradle-plugin](https://github.com/team-spotube/vlcj-bundler-gradle-plugin)
|
||||
- **Linux runtime:** `webkit2gtk-4.1` for the embedded webview.
|
||||
|
||||
Always use the Gradle wrapper (`./gradlew`, or `gradlew.bat` on Windows).
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
./gradlew :composeApp:run # desktop (JVM), fastest edit/run loop
|
||||
./gradlew :composeApp:assembleStableDebug # Android debug APK (or assembleNightlyDebug)
|
||||
```
|
||||
|
||||
Development builds stay fast. The release-only optimizations (the Rust release profile with LTO and symbol stripping, the native image, native-library pruning) are only applied when you build something to ship. See [Production vs development builds](#production-vs-development-builds).
|
||||
|
||||
### Building the desktop app (GraalVM native image)
|
||||
|
||||
#### Native toolchain
|
||||
|
||||
[Nucleus downloads and caches GraalVM CE](https://nucleusframework.dev/en/docs/performance/graalvm/) under `~/.gradle/nucleus/graalvm` on the first native build. Set `GRAALVM_HOME` to use your own GraalVM instead. Each OS also needs a native C toolchain:
|
||||
|
||||
| OS | Requirements |
|
||||
|---|---|
|
||||
| Linux | `gcc`, `zlib` headers, `patchelf`; plus `rpm` for `.rpm` packages. Debian/Ubuntu: `sudo apt-get install build-essential zlib1g-dev patchelf rpm`. Fedora: `sudo dnf install gcc zlib-devel patchelf rpm-build` |
|
||||
| macOS | Xcode Command Line Tools (`xcode-select --install`) |
|
||||
| Windows | Visual Studio Build Tools with the "Desktop development with C++" workload. Build from an "x64 Native Tools" prompt if `native-image` can't find MSVC. |
|
||||
|
||||
A native build is a whole-program compile. It takes **about 10 minutes**, uses **every CPU core**, and peaks at roughly **15 GB of RAM** (it gets slower, not broken, with less). That only happens for the native tasks below, never for `run`.
|
||||
|
||||
#### Build and run
|
||||
|
||||
```bash
|
||||
# App folder: binary + its native libs, in composeApp/build/compose/binaries/main/graalvm-app/
|
||||
./gradlew :composeApp:createGraalvmNativeDistributable
|
||||
|
||||
# Run that folder's binary directly
|
||||
./composeApp/build/compose/binaries/main/graalvm-app/org.spotube.spotube/spotube
|
||||
|
||||
# Or build and launch in one step
|
||||
./gradlew :composeApp:runGraalvmNativeDistributable
|
||||
```
|
||||
|
||||
Keep the `spotube` binary next to the `.so`/`.dll`/`lib/` files it was built with; it loads them from its own folder.
|
||||
|
||||
#### Installers
|
||||
|
||||
Each OS can only build its own installers:
|
||||
|
||||
| OS | Command | Output |
|
||||
|---|---|---|
|
||||
| Linux | `./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm` (also `packageGraalvmAppImage`) | `composeApp/build/compose/binaries/main/graalvm-{deb,rpm}/` |
|
||||
| Windows | `./gradlew :composeApp:packageGraalvmExe` (or `packageGraalvmMsi`) | `composeApp/build/compose/binaries/main/graalvm-exe/` |
|
||||
| macOS | `./gradlew :composeApp:packageGraalvmDmg` | `composeApp/build/compose/binaries/main/graalvm-dmg/` |
|
||||
|
||||
Add `-Pnightly=true` for the nightly channel (separate icons and name).
|
||||
|
||||
#### Reflection metadata (when a feature breaks only in the native build)
|
||||
|
||||
A native image is a closed world: anything the app reaches through reflection, JNI or JNA (vlcj, the Zipline/QuickJS plugin engine, NewPipe, ktor, datastore, …) must be declared at build time. Nucleus generates most of this automatically. The rest is in [`composeApp/graalvm/reachability-metadata.json`](composeApp/graalvm/reachability-metadata.json).
|
||||
|
||||
If something works in `:composeApp:run` but throws `ClassNotFoundException`, `NoSuchMethodException`, `MissingReflectionRegistrationError` or similar in the native binary, record what's missing:
|
||||
|
||||
```bash
|
||||
./gradlew :composeApp:runWithNativeAgent
|
||||
```
|
||||
|
||||
The app starts on the JVM with GraalVM's tracing agent attached. Use the broken feature (and anything near it), then **close the window normally**: the recorded entries are merged into `composeApp/graalvm/` only on a clean exit, not with Ctrl+C. Commit the updated JSON and rebuild the native image.
|
||||
|
||||
`./gradlew :composeApp:runGraalvmNative` gives a faster debugging loop: a quick-build (`-Ob`) native image that reports exactly which registration is missing.
|
||||
|
||||
Keep the hand-written `uk.co.caprica.vlcj.binding.lib.LibVlc` entry. JNA's direct mapping looks up its `native` methods through reflection, and without the entry, playback fails to initialize.
|
||||
|
||||
### Production vs development builds
|
||||
|
||||
Production mode is switched on automatically when you ask Gradle for a shippable artifact: any `package*`, `create*`, `notarize*` or `nativeImageCompile` task. You can force it either way with `-Pproduction=true` or `-Pproduction=false`.
|
||||
|
||||
| | Development (`run`, debug builds) | Production (installers, native image) |
|
||||
|---|---|---|
|
||||
| Rust library (desktop) | `debug` profile, fast incremental compile | `release` profile with LTO, single codegen unit, stripped (31 MB → 1.8 MB) |
|
||||
| Desktop app | JVM, hot-reloadable | GraalVM native image, `-Os` (size-optimized) |
|
||||
| Native libraries | everything on the classpath | only the host OS/CPU's natives; other OSes' and CPUs' libraries are pruned from the uber JAR the image is compiled from |
|
||||
| Skia (Skiko) | extracted from the JAR | loaded from `lib/` next to the binary, not embedded a second time |
|
||||
| JDK runtime libs (Linux) | n/a | native-image's ~10 KB `libjvm.so`/`libjava.so` shims instead of the JDK's (28 MB `libjvm.so`) |
|
||||
|
||||
Android picks the Rust profile from its build type (`debug` or `release`) on its own.
|
||||
|
||||
### Releases
|
||||
|
||||
Releases are built by [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml), run manually (`workflow_dispatch`) as `stable` or `nightly`. Each desktop platform (Linux x86_64 and aarch64, Windows, macOS) builds its native installers on its own runner. Android builds a signed APK.
|
||||
|
||||
## 👥 The Spotube team
|
||||
|
||||
|
||||
@ -15,3 +15,8 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
[lib]
|
||||
crate-type = ["cdylib", "staticlib"]
|
||||
path = "src/commonMain/rust/lib.rs"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
*/
|
||||
|
||||
import gobley.gradle.GobleyHost
|
||||
import gobley.gradle.Variant
|
||||
import gobley.gradle.cargo.dsl.jvm
|
||||
import dev.nucleusframework.desktop.application.dsl.NativeImageOptimization
|
||||
import dev.nucleusframework.desktop.application.dsl.TargetFormat
|
||||
import org.jetbrains.compose.reload.gradle.ComposeHotRun
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
@ -50,6 +52,17 @@ val desktopIconDir = if (isNightly) "icons/nightly" else "icons/stable"
|
||||
val nightlyBuildNumber = providers.gradleProperty("buildNumber").orNull?.toIntOrNull()
|
||||
?: System.getenv("GITHUB_RUN_NUMBER")?.toIntOrNull()
|
||||
?: buildNumber
|
||||
// Production = shipping artifacts (installers, distributables, native images). Size/speed
|
||||
// optimizations that slow builds down (Rust release + LTO) only apply then, never to `run`/dev.
|
||||
// Override with -Pproduction=true|false.
|
||||
val isProductionBuild = providers.gradleProperty("production").map { it.toBoolean() }.getOrElse(
|
||||
gradle.startParameter.taskNames.any { task ->
|
||||
task.substringAfterLast(':').let { name ->
|
||||
name.startsWith("package") || name.startsWith("create") ||
|
||||
name.startsWith("notarize") || name == "nativeImageCompile"
|
||||
}
|
||||
}
|
||||
)
|
||||
val targetName = "Spotube"
|
||||
val homePageUrl = "https://spotube.org"
|
||||
val repositoryUrl = "https://github.com/team-spotube/spotube"
|
||||
@ -431,6 +444,84 @@ nucleus.application {
|
||||
buildTypes.release.proguard {
|
||||
configurationFiles.from(project.file("proguard-rules.pro"))
|
||||
}
|
||||
|
||||
// AOT-compiled native binary (no bundled JVM). Build with `packageGraalvmNative`, try it with
|
||||
// `runGraalvmNative`. Nucleus downloads GraalVM CE on first use (or uses $GRAALVM_HOME).
|
||||
// Reflection/JNI metadata the automatic passes miss (vlcj, Zipline, NewPipe/Rhino, uniffi)
|
||||
// lives in `graalvm/` and is (re)collected by exercising the app under `runWithNativeAgent`.
|
||||
graalvm {
|
||||
isEnabled = true
|
||||
imageName = "spotube"
|
||||
nativeImageConfigBaseDir.set(project.layout.projectDirectory.dir("graalvm"))
|
||||
// ProGuard is not used for native images; -Os is the size equivalent
|
||||
optimization = NativeImageOptimization.SIZE
|
||||
// Same ceiling as the JVM distribution's -Xmx
|
||||
maxHeapSize = "384m"
|
||||
// Skiko is loaded from lib/ next to the binary (Nucleus copies it there); don't embed
|
||||
// a second ~30 MB copy as a resource
|
||||
buildArgs.add("-H:+UnlockExperimentalVMOptions")
|
||||
buildArgs.add("-H:ExcludeResources=.*skiko-[^/]*\\.(so|dylib|dll)")
|
||||
}
|
||||
}
|
||||
|
||||
// The uber JAR is what the GraalVM build compiles from, and native-image embeds every registered
|
||||
// resource in the binary. Libraries ship natives for every OS/CPU, so keep only the host's
|
||||
// (the uber JAR is per-OS/arch already; it isn't used by `run`).
|
||||
tasks.withType<org.gradle.jvm.tasks.Jar>().matching { it.name.endsWith("UberJarForCurrentOS") }.configureEach {
|
||||
// Everything the exclude closure uses is local: the configuration cache can't serialize
|
||||
// references to the build script object.
|
||||
val hostOs = System.getProperty("os.name").lowercase()
|
||||
val foreignExtensions = when {
|
||||
hostOs.contains("win") -> setOf("so", "dylib", "jnilib", "a")
|
||||
hostOs.contains("mac") -> setOf("so", "dll", "a")
|
||||
else -> setOf("dll", "dylib", "jnilib", "a")
|
||||
}
|
||||
val nativeExtensions = setOf("so", "dll", "dylib", "jnilib", "a")
|
||||
// CPU names as libraries spell them in their native folders (linux-x86-64, jni/amd64, darwin-aarch64, …)
|
||||
val cpuDirToken = Regex(
|
||||
"(?:^|[-_])(x86[-_]64|amd64|x64|aarch64|arm64|armv6|armv7|armel|arm|x86|i[36]86|ppc64le|ppc64|ppc" +
|
||||
"|s390x|riscv64|loongarch64|mips64el|sparcv9|sparc)(?=$|[-_])"
|
||||
)
|
||||
val hostCpuTokens = when (System.getProperty("os.arch").lowercase()) {
|
||||
"aarch64", "arm64" -> setOf("aarch64", "arm64")
|
||||
else -> setOf("x86-64", "x86_64", "amd64", "x64")
|
||||
}
|
||||
// OS flavours we never ship desktop builds for (sqlite-jdbc and JNA bundle these)
|
||||
val foreignOsDirToken = Regex("android|musl|freebsd|openbsd|dragonflybsd|aix|sunos")
|
||||
exclude { element ->
|
||||
val segments = element.path.lowercase().split('/')
|
||||
val extension = segments.last().substringAfterLast('.', "")
|
||||
!element.isDirectory && extension in nativeExtensions && (
|
||||
extension in foreignExtensions ||
|
||||
segments.dropLast(1).any { dir ->
|
||||
foreignOsDirToken.containsMatchIn(dir) ||
|
||||
cpuDirToken.findAll(dir).any { it.groupValues[1] !in hostCpuTokens }
|
||||
}
|
||||
)
|
||||
}
|
||||
// The locally built compose-webview SNAPSHOT carries stale CEF binaries (~1.3 GB unstripped
|
||||
// libcef.so) that no code loads (Linux uses WebKitGTK).
|
||||
exclude(
|
||||
"nucleus/native/*/libcef.*",
|
||||
"nucleus/native/*/libcompose_cef_*",
|
||||
"nucleus/native/*/cef_subprocess*",
|
||||
"nucleus/native/*/*.pak",
|
||||
"nucleus/native/*/icudtl.dat",
|
||||
"nucleus/native/*/v8_context_snapshot.bin",
|
||||
)
|
||||
}
|
||||
|
||||
// Linux: native-image emits 13 KB libjvm.so/libjava.so shims (the image itself provides the JVM
|
||||
// symbols), but Nucleus copies the JDK's real ones next to the binary — libjvm.so alone is 28 MB.
|
||||
// Ship native-image's own outputs instead: skip the libjvm copy, and in the AWT copy prefer
|
||||
// nativeCompile's version of any library native-image produced over the JDK's.
|
||||
tasks.matching { it.name == "copyGraalvmJvmSo" }.configureEach { enabled = false }
|
||||
tasks.withType<Copy>().matching { it.name == "copyGraalvmAwtSoLibs" }.configureEach {
|
||||
val nativeCompileDir = layout.buildDirectory.dir("compose/tmp/main/graalvm/nativeCompile").get().asFile
|
||||
from(nativeCompileDir) { include("*.so") }
|
||||
eachFile {
|
||||
if (file.parentFile != nativeCompileDir && nativeCompileDir.resolve(name).exists()) exclude()
|
||||
}
|
||||
}
|
||||
|
||||
// Issue with compose plugin
|
||||
@ -446,6 +537,8 @@ tasks.withType<ComposeHotRun>().configureEach {
|
||||
}
|
||||
|
||||
cargo {
|
||||
// Gobley defaults desktop to the debug profile, which ships an unstripped ~31 MB library
|
||||
if (isProductionBuild) jvmVariant = Variant.Release
|
||||
builds.jvm {
|
||||
embedRustLibrary = (rustTarget == GobleyHost.current.rustTarget)
|
||||
}
|
||||
|
||||
1332
composeApp/graalvm/reachability-metadata.json
Normal file
1332
composeApp/graalvm/reachability-metadata.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user