644 lines
14 KiB
Markdown
644 lines
14 KiB
Markdown
# Common Build Errors & Solutions
|
|
|
|
## Table of Contents
|
|
- [Compose Version Conflicts](#compose-version-conflicts)
|
|
- [secp256k1 JNI Errors](#secp256k1-jni-errors)
|
|
- [Source Set Dependency Issues](#source-set-dependency-issues)
|
|
- [Proguard/R8 Issues](#proguardr8-issues)
|
|
- [Desktop Packaging Errors](#desktop-packaging-errors)
|
|
- [Kotlin Compilation Errors](#kotlin-compilation-errors)
|
|
- [Dependency Resolution Failures](#dependency-resolution-failures)
|
|
- [JVM/JDK Version Issues](#jvmjdk-version-issues)
|
|
|
|
---
|
|
|
|
## Compose Version Conflicts
|
|
|
|
### Error 1: Compose Runtime Mismatch
|
|
|
|
```
|
|
java.lang.IllegalStateException: Version mismatch: Compose runtime is 1.10.0 but compiler is 1.9.0
|
|
```
|
|
|
|
**Cause:** Compose Compiler plugin version doesn't match Compose Runtime
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In gradle/libs.versions.toml
|
|
composeMultiplatform = "1.9.3" // Must align with Kotlin version
|
|
kotlin = "2.3.0"
|
|
|
|
// Check compatibility matrix:
|
|
// https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html
|
|
```
|
|
|
|
**Verification:**
|
|
```bash
|
|
./gradlew :commons:dependencies | grep compose
|
|
```
|
|
|
|
### Error 2: AndroidX Compose BOM Conflict
|
|
|
|
```
|
|
Duplicate class androidx.compose.ui.platform.AndroidCompositionLocalMap found in modules...
|
|
```
|
|
|
|
**Cause:** Both Compose Multiplatform and AndroidX Compose BOM providing same classes
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In commons/build.gradle.kts (KMP module)
|
|
// Use Compose Multiplatform, NOT AndroidX BOM
|
|
dependencies {
|
|
implementation(compose.ui) // ✅ Compose Multiplatform
|
|
implementation(compose.material3)
|
|
|
|
// Don't use in KMP modules:
|
|
// implementation(libs.androidx.compose.bom) // ❌ Android-only
|
|
}
|
|
|
|
// In amethyst/build.gradle.kts (Android-only module)
|
|
// Can use AndroidX BOM
|
|
dependencies {
|
|
val composeBom = platform(libs.androidx.compose.bom)
|
|
implementation(composeBom)
|
|
implementation(libs.androidx.ui)
|
|
}
|
|
```
|
|
|
|
### Error 3: Material3 WindowSizeClass Not Found
|
|
|
|
```
|
|
Unresolved reference: WindowSizeClass
|
|
```
|
|
|
|
**Cause:** Using Android's WindowSizeClass in shared KMP code
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// Don't use in commonMain or jvmAndroid:
|
|
// import androidx.compose.material3.windowsizeclass.WindowSizeClass // ❌
|
|
|
|
// Use in androidMain only, or create expect/actual:
|
|
// commonMain
|
|
expect class WindowSizeClassAdapter
|
|
|
|
// androidMain
|
|
actual typealias WindowSizeClassAdapter = androidx.compose.material3.windowsizeclass.WindowSizeClass
|
|
|
|
// jvmMain (desktop)
|
|
actual class WindowSizeClassAdapter { /* Custom impl */ }
|
|
```
|
|
|
|
---
|
|
|
|
## secp256k1 JNI Errors
|
|
|
|
### Error 1: JNI Library Not Found (Desktop)
|
|
|
|
```
|
|
java.lang.UnsatisfiedLinkError: no secp256k1jni in java.library.path
|
|
```
|
|
|
|
**Cause:** Desktop using wrong secp256k1 variant (Android JNI instead of JVM JNI)
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In quartz/build.gradle.kts
|
|
sourceSets {
|
|
jvmMain {
|
|
dependencies {
|
|
// ✅ Correct - JVM variant
|
|
implementation(libs.secp256k1.kmp.jni.jvm)
|
|
|
|
// ❌ Wrong - Android variant
|
|
// implementation(libs.secp256k1.kmp.jni.android)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification:**
|
|
```bash
|
|
./gradlew :quartz:dependencies --configuration jvmRuntimeClasspath | grep secp256k1
|
|
# Should show: secp256k1-kmp-jni-jvm, NOT jni-android
|
|
```
|
|
|
|
### Error 2: Version Mismatch Between Variants
|
|
|
|
```
|
|
java.lang.NoSuchMethodError: fr.acinq.secp256k1.Secp256k1.sign
|
|
```
|
|
|
|
**Cause:** Common, Android, and JVM variants have different versions
|
|
|
|
**Solution:**
|
|
```toml
|
|
# In gradle/libs.versions.toml
|
|
# All three MUST use same version
|
|
secp256k1KmpJniAndroid = "0.22.0"
|
|
|
|
[libraries]
|
|
secp256k1-kmp-common = { ..., version.ref = "secp256k1KmpJniAndroid" }
|
|
secp256k1-kmp-jni-android = { ..., version.ref = "secp256k1KmpJniAndroid" }
|
|
secp256k1-kmp-jni-jvm = { ..., version.ref = "secp256k1KmpJniAndroid" }
|
|
```
|
|
|
|
### Error 3: Android JNI Not Loaded
|
|
|
|
```
|
|
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader couldn't find "libsecp256k1jni.so"
|
|
```
|
|
|
|
**Cause:** Proguard stripping JNI classes
|
|
|
|
**Solution:**
|
|
```proguard
|
|
# In quartz/proguard-rules.pro
|
|
-keep class fr.acinq.secp256k1.** { *; }
|
|
```
|
|
|
|
---
|
|
|
|
## Source Set Dependency Issues
|
|
|
|
### Error 1: jvmAndroid Defined After androidMain
|
|
|
|
```
|
|
Could not get unknown property 'jvmAndroid' for source set container
|
|
```
|
|
|
|
**Cause:** Source sets must be defined in dependency order
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// ✅ Correct order
|
|
sourceSets {
|
|
commonMain { }
|
|
|
|
// Define jvmAndroid BEFORE androidMain and jvmMain
|
|
val jvmAndroid = create("jvmAndroid") {
|
|
dependsOn(commonMain.get())
|
|
}
|
|
|
|
androidMain {
|
|
dependsOn(jvmAndroid) // Now jvmAndroid exists
|
|
}
|
|
|
|
jvmMain {
|
|
dependsOn(jvmAndroid)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error 2: Dependency in Wrong Source Set
|
|
|
|
```
|
|
Unresolved reference: ObjectMapper (Jackson)
|
|
```
|
|
|
|
**Cause:** JVM-only library in commonMain
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
sourceSets {
|
|
commonMain {
|
|
// ❌ Jackson is JVM-only, can't use here
|
|
// implementation(libs.jackson.module.kotlin)
|
|
}
|
|
|
|
val jvmAndroid = create("jvmAndroid") {
|
|
dependsOn(commonMain.get())
|
|
// ✅ Jackson in jvmAndroid (shared JVM code)
|
|
api(libs.jackson.module.kotlin)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error 3: Platform-Specific Code in Shared Source Set
|
|
|
|
```
|
|
java.lang.NoClassDefFoundError: android.content.Context
|
|
```
|
|
|
|
**Cause:** Android-specific API in jvmAndroid or commonMain
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// Use expect/actual pattern
|
|
|
|
// commonMain
|
|
expect class PlatformContext
|
|
|
|
// androidMain
|
|
actual typealias PlatformContext = android.content.Context
|
|
|
|
// jvmMain
|
|
actual class PlatformContext {
|
|
// Custom desktop implementation
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Proguard/R8 Issues
|
|
|
|
### Error 1: Native Library Classes Stripped
|
|
|
|
```
|
|
java.lang.NoClassDefFoundError: com.goterl.lazysodium.Sodium
|
|
```
|
|
|
|
**Cause:** R8/Proguard removing JNA/LibSodium classes
|
|
|
|
**Solution:**
|
|
```proguard
|
|
# In quartz/proguard-rules.pro
|
|
-keep class com.goterl.lazysodium.** { *; }
|
|
-keep class com.sun.jna.** { *; }
|
|
-keep class fr.acinq.secp256k1.** { *; }
|
|
```
|
|
|
|
### Error 2: Reflection-Based Libraries Broken
|
|
|
|
```
|
|
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of ...
|
|
```
|
|
|
|
**Cause:** Jackson uses reflection, R8 strips class metadata
|
|
|
|
**Solution:**
|
|
```proguard
|
|
# Preserve reflection metadata
|
|
-keepattributes *Annotation*
|
|
-keepattributes Signature
|
|
-keepattributes InnerClasses
|
|
|
|
# Keep all Quartz event classes
|
|
-keep class com.vitorpamplona.quartz.** { *; }
|
|
```
|
|
|
|
### Error 3: Enum Values Missing
|
|
|
|
```
|
|
java.lang.IllegalArgumentException: No enum constant ...
|
|
```
|
|
|
|
**Cause:** R8 obfuscating enum names
|
|
|
|
**Solution:**
|
|
```proguard
|
|
# Keep all enums
|
|
-keep enum ** { *; }
|
|
-keepnames class ** { *; }
|
|
```
|
|
|
|
---
|
|
|
|
## Desktop Packaging Errors
|
|
|
|
### Error 1: Icon Not Found
|
|
|
|
```
|
|
FAILURE: Build failed with an exception.
|
|
* What went wrong: Cannot find icon file: src/jvmMain/resources/icon.icns
|
|
```
|
|
|
|
**Cause:** Icon file missing or wrong path
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In desktopApp/build.gradle.kts
|
|
nativeDistributions {
|
|
macOS {
|
|
// Ensure file exists at this path
|
|
iconFile.set(project.file("src/jvmMain/resources/icon.icns"))
|
|
}
|
|
|
|
// Check file exists:
|
|
// ls -la desktopApp/src/jvmMain/resources/
|
|
}
|
|
```
|
|
|
|
**Icon Requirements:**
|
|
- macOS: `.icns` (512x512, 256x256, 128x128, 32x32)
|
|
- Windows: `.ico` (256x256, 128x128, 64x64, 32x32, 16x16)
|
|
- Linux: `.png` (512x512 recommended)
|
|
|
|
### Error 2: Main Class Not Found
|
|
|
|
```
|
|
Error: Could not find or load main class com.vitorpamplona.amethyst.desktop.MainKt
|
|
```
|
|
|
|
**Cause:** Wrong mainClass path or Main.kt doesn't have main()
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In desktopApp/build.gradle.kts
|
|
compose.desktop {
|
|
application {
|
|
mainClass = "com.vitorpamplona.amethyst.desktop.MainKt"
|
|
// ^^^^
|
|
// Kotlin compiler adds "Kt" suffix
|
|
}
|
|
}
|
|
|
|
// In src/jvmMain/kotlin/com/vitorpamplona/amethyst/desktop/Main.kt
|
|
fun main() = application {
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Error 3: Native Library Missing in Package
|
|
|
|
```
|
|
java.lang.UnsatisfiedLinkError: no secp256k1jni in java.library.path
|
|
```
|
|
|
|
**Cause:** Native libraries not bundled in distribution
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// Native libs are automatically included via dependencies
|
|
// Verify secp256k1-kmp-jni-jvm is in dependencies:
|
|
dependencies {
|
|
implementation(libs.secp256k1.kmp.jni.jvm) // ✅ Includes native libs
|
|
}
|
|
|
|
// Test packaged app:
|
|
./gradlew :desktopApp:createDistributable
|
|
# Run from: desktopApp/build/compose/binaries/main/app/
|
|
```
|
|
|
|
---
|
|
|
|
## Kotlin Compilation Errors
|
|
|
|
### Error 1: Expect/Actual Mismatch
|
|
|
|
```
|
|
'actual' declaration has no corresponding expected declaration
|
|
```
|
|
|
|
**Cause:** Signature mismatch or missing expect
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// commonMain - expect declaration
|
|
expect class CryptoProvider {
|
|
fun sign(message: ByteArray, privateKey: ByteArray): ByteArray
|
|
}
|
|
|
|
// androidMain & jvmMain - actual must match EXACTLY
|
|
actual class CryptoProvider {
|
|
actual fun sign(message: ByteArray, privateKey: ByteArray): ByteArray {
|
|
// Implementation
|
|
}
|
|
}
|
|
|
|
// Common mistakes:
|
|
// - Different parameter names ❌
|
|
// - Different return types ❌
|
|
// - Missing 'actual' modifier ❌
|
|
```
|
|
|
|
### Error 2: Target JVM Version Mismatch
|
|
|
|
```
|
|
Compilation failed: module was compiled with an incompatible version of Kotlin
|
|
```
|
|
|
|
**Cause:** Different JVM targets across modules
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// Ensure ALL modules use same JVM target
|
|
|
|
// In quartz/build.gradle.kts
|
|
kotlin {
|
|
jvm {
|
|
compilerOptions {
|
|
jvmTarget.set(JvmTarget.JVM_21) // ✅ Java 21
|
|
}
|
|
}
|
|
}
|
|
|
|
// In android {} block
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_21
|
|
targetCompatibility = JavaVersion.VERSION_21
|
|
}
|
|
```
|
|
|
|
### Error 3: Compose Compiler Plugin Missing
|
|
|
|
```
|
|
This declaration needs opt-in. Please use @OptIn(ComposeApi::class) or @Composable
|
|
```
|
|
|
|
**Cause:** Compose compiler plugin not applied
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In build.gradle.kts
|
|
plugins {
|
|
alias(libs.plugins.jetbrainsComposeCompiler) // ✅ Add this
|
|
alias(libs.plugins.composeMultiplatform)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Dependency Resolution Failures
|
|
|
|
### Error 1: Repository Not Found
|
|
|
|
```
|
|
Could not find com.github.vitorpamplona.compose-richtext:richtext-ui:f92ef49c9d
|
|
```
|
|
|
|
**Cause:** Jitpack or custom Maven repository not configured
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In settings.gradle
|
|
dependencyResolutionManagement {
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
maven { url = "https://jitpack.io" } // ✅ Add Jitpack
|
|
}
|
|
}
|
|
```
|
|
|
|
### Error 2: Gradle Version Too Old
|
|
|
|
```
|
|
Version catalogs are not supported in this version of Gradle
|
|
```
|
|
|
|
**Cause:** Gradle < 7.0
|
|
|
|
**Solution:**
|
|
```properties
|
|
# In gradle/wrapper/gradle-wrapper.properties
|
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
|
```
|
|
|
|
Then: `./gradlew wrapper --gradle-version=8.9`
|
|
|
|
### Error 3: Dependency Variant Not Found
|
|
|
|
```
|
|
No matching variant of fr.acinq.secp256k1:secp256k1-kmp-jni-android:0.22.0 was found
|
|
```
|
|
|
|
**Cause:** Wrong dependency configuration for target
|
|
|
|
**Solution:**
|
|
```kotlin
|
|
// In androidMain (Android library module)
|
|
dependencies {
|
|
// For AAR packaging
|
|
implementation("net.java.dev.jna:jna:5.18.1@aar") // ✅ Specify @aar
|
|
|
|
// secp256k1 works without @aar (auto-detects)
|
|
api(libs.secp256k1.kmp.jni.android)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## JVM/JDK Version Issues
|
|
|
|
### Error 1: Unsupported Class File Version
|
|
|
|
```
|
|
Unsupported class file major version 65
|
|
```
|
|
|
|
**Cause:** Compiled with Java 21, running with older Java
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Check Java version
|
|
java -version # Should show 21
|
|
|
|
# Set JAVA_HOME if needed
|
|
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home
|
|
|
|
# Or in gradle.properties
|
|
org.gradle.java.home=/path/to/jdk-21
|
|
```
|
|
|
|
### Error 2: JVM Toolchain Not Found
|
|
|
|
```
|
|
No matching toolchain found for requested JvmVersion
|
|
```
|
|
|
|
**Cause:** Java 21 not installed or not detected
|
|
|
|
**Solution:**
|
|
```bash
|
|
# macOS (Homebrew)
|
|
brew install openjdk@21
|
|
|
|
# Ubuntu
|
|
sudo apt install openjdk-21-jdk
|
|
|
|
# Set JAVA_HOME
|
|
export JAVA_HOME=$(/usr/libexec/java_home -v 21) # macOS
|
|
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk # Linux
|
|
|
|
# Verify
|
|
./gradlew -version
|
|
```
|
|
|
|
### Error 3: Gradle Daemon Using Wrong Java
|
|
|
|
```
|
|
Daemon will be stopped at the end of the build because JVM version has changed
|
|
```
|
|
|
|
**Cause:** Daemon started with different Java version
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Stop all daemons
|
|
./gradlew --stop
|
|
|
|
# Start with correct JAVA_HOME
|
|
export JAVA_HOME=/path/to/jdk-21
|
|
./gradlew build
|
|
|
|
# Or set in gradle.properties permanently
|
|
org.gradle.java.home=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home
|
|
```
|
|
|
|
---
|
|
|
|
## General Troubleshooting Steps
|
|
|
|
### Step 1: Clean Build
|
|
```bash
|
|
./gradlew clean
|
|
./gradlew --stop # Stop daemon
|
|
./gradlew build
|
|
```
|
|
|
|
### Step 2: Check Dependencies
|
|
```bash
|
|
./gradlew :moduleName:dependencies
|
|
./gradlew dependencyInsight --dependency libraryName
|
|
```
|
|
|
|
### Step 3: Enable Debug Logging
|
|
```bash
|
|
./gradlew build --info # Info logging
|
|
./gradlew build --debug # Debug logging (verbose)
|
|
./gradlew build --stacktrace
|
|
```
|
|
|
|
### Step 4: Invalidate Caches
|
|
```bash
|
|
# Clear Gradle cache
|
|
rm -rf ~/.gradle/caches/
|
|
|
|
# Clear build outputs
|
|
./gradlew clean
|
|
|
|
# Clear Gradle wrapper cache
|
|
rm -rf ~/.gradle/wrapper/
|
|
```
|
|
|
|
### Step 5: Build Scan
|
|
```bash
|
|
./gradlew build --scan
|
|
# Opens interactive diagnostics in browser
|
|
```
|
|
|
|
## Quick Reference: Error Keywords → Solution
|
|
|
|
| Error Keyword | Likely Cause | Quick Fix |
|
|
|---------------|--------------|-----------|
|
|
| `UnsatisfiedLinkError` | Wrong JNI variant | Check secp256k1/JNA variants by platform |
|
|
| `IllegalStateException` (Compose) | Version mismatch | Align Compose Multiplatform + Kotlin versions |
|
|
| `NoClassDefFoundError` | Proguard stripping | Add `-keep` rule for class |
|
|
| `Unresolved reference` | Wrong source set | Move to appropriate source set (jvmAndroid) |
|
|
| `Duplicate class` | BOM conflict | Remove AndroidX BOM from KMP modules |
|
|
| `Version mismatch` | Plugin/runtime version mismatch | Update libs.versions.toml |
|
|
| `No matching variant` | Repository or packaging issue | Add repository or @aar suffix |
|
|
| `Could not find` (dependency) | Missing repository | Add maven/jitpack to repositories |
|
|
| `Unsupported class file` | Java version mismatch | Update JAVA_HOME to Java 21 |
|
|
|
|
---
|
|
|
|
## Getting Help
|
|
|
|
1. **Check Build Scan**: `./gradlew build --scan` for detailed diagnostics
|
|
2. **Gradle Forums**: https://discuss.gradle.org/
|
|
3. **Kotlin Slack**: #multiplatform channel
|
|
4. **Stack Overflow**: Tags `gradle`, `kotlin-multiplatform`, `compose-multiplatform`
|