finish skills
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
# Build Commands Reference
|
||||
|
||||
## Table of Contents
|
||||
- [Core Build Tasks](#core-build-tasks)
|
||||
- [Module-Specific Builds](#module-specific-builds)
|
||||
- [Desktop Tasks](#desktop-tasks)
|
||||
- [Android Tasks](#android-tasks)
|
||||
- [Testing](#testing)
|
||||
- [Analysis & Diagnostics](#analysis--diagnostics)
|
||||
- [Performance Optimization](#performance-optimization)
|
||||
|
||||
## Core Build Tasks
|
||||
|
||||
### Full Project Build
|
||||
```bash
|
||||
./gradlew build # Build all modules
|
||||
./gradlew clean build # Clean build
|
||||
./gradlew assemble # Build without tests
|
||||
```
|
||||
|
||||
### Incremental Builds
|
||||
```bash
|
||||
./gradlew :quartz:build # Build only quartz module
|
||||
./gradlew :commons:build # Build only commons module
|
||||
./gradlew :desktopApp:build # Build only desktop app
|
||||
```
|
||||
|
||||
## Module-Specific Builds
|
||||
|
||||
### Quartz (KMP Library)
|
||||
```bash
|
||||
./gradlew :quartz:build # All targets
|
||||
./gradlew :quartz:compileKotlinJvm # JVM target only
|
||||
./gradlew :quartz:compileDebugKotlinAndroid # Android target only
|
||||
./gradlew :quartz:linkDebugFrameworkIosArm64 # iOS framework
|
||||
./gradlew :quartz:publishToMavenLocal # Publish locally
|
||||
```
|
||||
|
||||
### Commons (Shared UI)
|
||||
```bash
|
||||
./gradlew :commons:build # All targets
|
||||
./gradlew :commons:compileKotlinJvm # Desktop target
|
||||
./gradlew :commons:compileDebugKotlinAndroid # Android target
|
||||
```
|
||||
|
||||
## Desktop Tasks
|
||||
|
||||
### Run Desktop App
|
||||
```bash
|
||||
./gradlew :desktopApp:run # Run desktop app
|
||||
./gradlew :desktopApp:runDistributable # Run packaged version
|
||||
```
|
||||
|
||||
### Package Desktop App
|
||||
```bash
|
||||
./gradlew :desktopApp:createDistributable # Create runnable package
|
||||
./gradlew :desktopApp:packageDmg # macOS DMG
|
||||
./gradlew :desktopApp:packageMsi # Windows MSI
|
||||
./gradlew :desktopApp:packageDeb # Linux DEB
|
||||
```
|
||||
|
||||
### Distribution Location
|
||||
- macOS: `desktopApp/build/compose/binaries/main/dmg/`
|
||||
- Windows: `desktopApp/build/compose/binaries/main/msi/`
|
||||
- Linux: `desktopApp/build/compose/binaries/main/deb/`
|
||||
|
||||
## Android Tasks
|
||||
|
||||
### Compile & Assemble
|
||||
```bash
|
||||
./gradlew :amethyst:assembleDebug # Debug APK
|
||||
./gradlew :amethyst:assembleRelease # Release APK
|
||||
./gradlew :amethyst:bundleRelease # Release AAB
|
||||
```
|
||||
|
||||
### Install & Run
|
||||
```bash
|
||||
./gradlew :amethyst:installDebug # Install debug on device
|
||||
adb shell am start -n com.vitorpamplona.amethyst/.MainActivity
|
||||
```
|
||||
|
||||
### Proguard/R8
|
||||
```bash
|
||||
./gradlew :quartz:minifyReleaseWithR8 # Test R8 minification
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
./gradlew test # All unit tests
|
||||
./gradlew :quartz:jvmTest # JVM unit tests
|
||||
./gradlew :quartz:testDebugUnitTest # Android unit tests
|
||||
./gradlew :commons:test # Commons tests
|
||||
```
|
||||
|
||||
### Android Instrumented Tests
|
||||
```bash
|
||||
./gradlew :quartz:connectedAndroidTest # Requires device/emulator
|
||||
```
|
||||
|
||||
### Test Reports
|
||||
```bash
|
||||
# Reports location: <module>/build/reports/tests/
|
||||
open quartz/build/reports/tests/jvmTest/index.html
|
||||
```
|
||||
|
||||
## Analysis & Diagnostics
|
||||
|
||||
### Dependency Analysis
|
||||
```bash
|
||||
./gradlew dependencies # All dependencies
|
||||
./gradlew :quartz:dependencies # Quartz dependencies
|
||||
./gradlew dependencyInsight --dependency okhttp # Specific dependency
|
||||
```
|
||||
|
||||
### Build Scan
|
||||
```bash
|
||||
./gradlew build --scan # Upload to scans.gradle.com
|
||||
```
|
||||
|
||||
### Performance Profiling
|
||||
```bash
|
||||
./gradlew build --profile # Generate profile report
|
||||
# Report: build/reports/profile/profile-<timestamp>.html
|
||||
```
|
||||
|
||||
### Task Dependencies
|
||||
```bash
|
||||
./gradlew :desktopApp:run --dry-run # Show task graph
|
||||
./gradlew :desktopApp:dependencies --scan # Visualize dependencies
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Configuration Cache
|
||||
```bash
|
||||
./gradlew build --configuration-cache # Enable config cache
|
||||
./gradlew build --configuration-cache-problems=warn
|
||||
```
|
||||
|
||||
### Build Cache
|
||||
```bash
|
||||
./gradlew build --build-cache # Enable build cache
|
||||
./gradlew cleanBuildCache # Clear build cache
|
||||
```
|
||||
|
||||
### Parallel Execution
|
||||
```bash
|
||||
./gradlew build --parallel --max-workers=8 # Parallel with 8 workers
|
||||
```
|
||||
|
||||
### Daemon Management
|
||||
```bash
|
||||
./gradlew --stop # Stop Gradle daemon
|
||||
./gradlew --status # Daemon status
|
||||
```
|
||||
|
||||
### Incremental Compilation
|
||||
```bash
|
||||
# Already enabled by default in Kotlin, but can verify:
|
||||
./gradlew :quartz:compileKotlinJvm --info | grep "Incremental"
|
||||
```
|
||||
|
||||
## gradle.properties Optimizations
|
||||
|
||||
Add to `gradle.properties` for faster builds:
|
||||
|
||||
```properties
|
||||
# Daemon
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g
|
||||
|
||||
# Parallel
|
||||
org.gradle.parallel=true
|
||||
org.gradle.workers.max=8
|
||||
|
||||
# Caching
|
||||
org.gradle.caching=true
|
||||
org.gradle.configuration-cache=true
|
||||
|
||||
# Kotlin
|
||||
kotlin.incremental=true
|
||||
kotlin.daemon.jvmargs=-Xmx2g
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Full Desktop Build & Run
|
||||
```bash
|
||||
./gradlew :desktopApp:clean :desktopApp:run
|
||||
```
|
||||
|
||||
### Quick Desktop Iteration
|
||||
```bash
|
||||
# No clean - incremental compilation
|
||||
./gradlew :desktopApp:run
|
||||
```
|
||||
|
||||
### Android Release Build
|
||||
```bash
|
||||
./gradlew :amethyst:clean :amethyst:bundleRelease
|
||||
```
|
||||
|
||||
### Test All KMP Targets
|
||||
```bash
|
||||
./gradlew :quartz:test :quartz:testDebugUnitTest
|
||||
```
|
||||
|
||||
### Publish Quartz Locally for Testing
|
||||
```bash
|
||||
./gradlew :quartz:publishToMavenLocal
|
||||
# Then update version in consumer project to test
|
||||
```
|
||||
@@ -0,0 +1,643 @@
|
||||
# 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`
|
||||
@@ -0,0 +1,266 @@
|
||||
# Module Dependency Graph
|
||||
|
||||
## Visual Hierarchy
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Root Project │
|
||||
│ (Amethyst) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────────┼────────────────┬────────────┐
|
||||
│ │ │ │
|
||||
▼ ▼ ▼ ▼
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────────┐
|
||||
│ :amethyst │ │ :desktopApp │ │ :benchmark │ │:ammolite │
|
||||
│ (Android) │ │ (JVM) │ │ (Android) │ │ (Support)│
|
||||
└─────────────┘ └─────────────┘ └─────────────┘ └──────────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
└────────────────┼────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ :commons │
|
||||
│ (KMP UI) │
|
||||
│ │
|
||||
│ jvmAndroid │
|
||||
│ / \ │
|
||||
│ jvm android│
|
||||
└─────────────┘
|
||||
│
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ :quartz │
|
||||
│(KMP Library)│
|
||||
│ │
|
||||
│ commonMain │
|
||||
│ │ │
|
||||
│ jvmAndroid │
|
||||
│ / | \ │
|
||||
│jvm and ios │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Module Details
|
||||
|
||||
### :quartz (KMP Nostr Library)
|
||||
**Type:** Kotlin Multiplatform Library
|
||||
**Targets:** JVM, Android, iOS (iosX64, iosArm64, iosSimulatorArm64)
|
||||
**Dependencies:**
|
||||
- External: secp256k1, jackson, okhttp, kotlinx.coroutines, kotlinx.collections.immutable
|
||||
- Source sets: commonMain → jvmAndroid → {androidMain, jvmMain}, iosMain
|
||||
|
||||
**Role:** Core Nostr protocol implementation, shared across all platforms
|
||||
|
||||
### :commons (Shared UI Components)
|
||||
**Type:** Kotlin Multiplatform Library
|
||||
**Targets:** JVM, Android
|
||||
**Dependencies:**
|
||||
- Module: `:quartz`
|
||||
- External: Compose Multiplatform, Material3, kotlinx.collections.immutable
|
||||
- Source sets: commonMain → jvmAndroid → {androidMain, jvmMain}
|
||||
|
||||
**Role:** Shared Compose UI components for Desktop and Android
|
||||
|
||||
### :desktopApp (Desktop Application)
|
||||
**Type:** JVM Application
|
||||
**Targets:** JVM (Desktop)
|
||||
**Dependencies:**
|
||||
- Modules: `:commons`, `:quartz`
|
||||
- External: Compose Desktop, kotlinx.coroutines.swing
|
||||
|
||||
**Role:** Desktop-specific navigation, layouts, and entry point
|
||||
|
||||
### :amethyst (Android Application)
|
||||
**Type:** Android Application
|
||||
**Targets:** Android
|
||||
**Dependencies:**
|
||||
- Modules: `:commons`, `:quartz`, `:ammolite`
|
||||
- External: Android SDK, AndroidX, Firebase, Tor
|
||||
|
||||
**Role:** Android-specific navigation, layouts, and entry point
|
||||
|
||||
### :benchmark (Android Benchmark)
|
||||
**Type:** Android Library
|
||||
**Targets:** Android
|
||||
**Dependencies:**
|
||||
- Modules: `:commons`, `:quartz`
|
||||
- External: AndroidX Benchmark
|
||||
|
||||
**Role:** Performance benchmarking for Android builds
|
||||
|
||||
### :ammolite (Support Module)
|
||||
**Type:** Android Library
|
||||
**Targets:** Android
|
||||
**Dependencies:** Android-specific utilities
|
||||
|
||||
**Role:** Android support utilities for amethyst
|
||||
|
||||
## Dependency Flow Patterns
|
||||
|
||||
### Desktop Build Chain
|
||||
```
|
||||
:desktopApp → :commons (jvmMain) → :quartz (jvmMain)
|
||||
↓
|
||||
jvmAndroid
|
||||
↓
|
||||
commonMain
|
||||
```
|
||||
|
||||
### Android Build Chain
|
||||
```
|
||||
:amethyst → :commons (androidMain) → :quartz (androidMain)
|
||||
↓ ↓
|
||||
:ammolite jvmAndroid
|
||||
↓
|
||||
commonMain
|
||||
```
|
||||
|
||||
## Source Set Dependencies
|
||||
|
||||
### :quartz Source Sets
|
||||
```
|
||||
commonMain (base)
|
||||
├─ jvmAndroid (shared JVM code)
|
||||
│ ├─ androidMain (Android platform)
|
||||
│ └─ jvmMain (Desktop platform)
|
||||
└─ iosMain (iOS platform)
|
||||
├─ iosX64Main
|
||||
├─ iosArm64Main
|
||||
└─ iosSimulatorArm64Main
|
||||
```
|
||||
|
||||
**Key Dependencies per Source Set:**
|
||||
- **commonMain**: secp256k1-kmp, kotlinx.coroutines, collection, immutable collections
|
||||
- **jvmAndroid**: jackson, okhttp, url-detector, rfc3986
|
||||
- **androidMain**: secp256k1-kmp-jni-android, lazysodium-android, jna (aar)
|
||||
- **jvmMain**: secp256k1-kmp-jni-jvm, lazysodium-java, jna (jar)
|
||||
|
||||
### :commons Source Sets
|
||||
```
|
||||
commonMain (base UI)
|
||||
└─ jvmAndroid (shared JVM UI)
|
||||
├─ androidMain (Android UI utilities)
|
||||
└─ jvmMain (Desktop UI utilities)
|
||||
```
|
||||
|
||||
**Key Dependencies per Source Set:**
|
||||
- **commonMain**: Compose Multiplatform, Material3, :quartz
|
||||
- **jvmAndroid**: url-detector
|
||||
- **androidMain**: AndroidX Compose tooling
|
||||
- **jvmMain**: Compose Desktop
|
||||
|
||||
## Critical Dependency Patterns
|
||||
|
||||
### 1. secp256k1 Variants
|
||||
```kotlin
|
||||
// commonMain - API only
|
||||
api(libs.secp256k1.kmp.common)
|
||||
|
||||
// androidMain - JNI Android
|
||||
api(libs.secp256k1.kmp.jni.android)
|
||||
|
||||
// jvmMain - JNI JVM
|
||||
implementation(libs.secp256k1.kmp.jni.jvm)
|
||||
```
|
||||
**Why:** Different JNI bindings for Android vs Desktop JVM
|
||||
|
||||
### 2. JNA Variants (for LibSodium)
|
||||
```kotlin
|
||||
// androidMain
|
||||
implementation("com.goterl:lazysodium-android:5.2.0@aar")
|
||||
implementation("net.java.dev.jna:jna:5.18.1@aar")
|
||||
|
||||
// jvmMain
|
||||
implementation(libs.lazysodium.java)
|
||||
implementation(libs.jna) // JAR variant
|
||||
```
|
||||
**Why:** Android needs AAR packaging, JVM needs JAR
|
||||
|
||||
### 3. Compose Alignment
|
||||
```kotlin
|
||||
// commons/build.gradle.kts
|
||||
implementation(compose.ui) // Compose Multiplatform BOM
|
||||
implementation(compose.material3)
|
||||
|
||||
// Version catalog alignment
|
||||
composeMultiplatform = "1.9.3"
|
||||
composeBom = "2025.12.01" // AndroidX Compose
|
||||
```
|
||||
**Why:** Two Compose ecosystems (Multiplatform + AndroidX) must align
|
||||
|
||||
## Dependency Configuration Types
|
||||
|
||||
### API vs Implementation
|
||||
|
||||
**Use `api` when:**
|
||||
- Dependency types appear in module's public API
|
||||
- Used in expect/actual declarations visible to consumers
|
||||
- Example: `secp256k1-kmp-common` in quartz (public types)
|
||||
|
||||
**Use `implementation` when:**
|
||||
- Internal implementation detail
|
||||
- Not exposed to module consumers
|
||||
- Example: `okhttp` in quartz (internal network client)
|
||||
|
||||
### Example from quartz
|
||||
```kotlin
|
||||
// Public API - exposed to consumers
|
||||
api(libs.secp256k1.kmp.common)
|
||||
api(libs.jackson.module.kotlin) // Event serialization public
|
||||
|
||||
// Internal implementation
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
```
|
||||
|
||||
## Transitive Dependency Impact
|
||||
|
||||
### When :desktopApp depends on :commons
|
||||
- Gets `:quartz` transitively (via :commons)
|
||||
- Gets `secp256k1-kmp-jvm` transitively (via :quartz jvmMain)
|
||||
- Does NOT get Android-specific dependencies (scoped to androidMain)
|
||||
|
||||
### When :amethyst depends on :commons
|
||||
- Gets `:quartz` transitively (via :commons)
|
||||
- Gets `secp256k1-kmp-jni-android` transitively (via :quartz androidMain)
|
||||
- Does NOT get JVM/Desktop-specific dependencies (scoped to jvmMain)
|
||||
|
||||
## Verifying Dependencies
|
||||
|
||||
### Check Module Dependencies
|
||||
```bash
|
||||
./gradlew :desktopApp:dependencies
|
||||
./gradlew :amethyst:dependencies
|
||||
```
|
||||
|
||||
### Check Specific Library
|
||||
```bash
|
||||
./gradlew dependencyInsight --dependency secp256k1
|
||||
./gradlew dependencyInsight --dependency compose-ui
|
||||
```
|
||||
|
||||
### Visualize with Build Scan
|
||||
```bash
|
||||
./gradlew :desktopApp:dependencies --scan
|
||||
# Opens interactive dependency graph in browser
|
||||
```
|
||||
|
||||
## Common Dependency Issues
|
||||
|
||||
### Issue 1: Wrong secp256k1 Variant in Desktop
|
||||
**Symptom:** `UnsatisfiedLinkError: no secp256k1jni in java.library.path`
|
||||
**Cause:** Desktop using Android JNI variant
|
||||
**Fix:** Ensure jvmMain uses `secp256k1-kmp-jni-jvm`
|
||||
|
||||
### Issue 2: Compose Version Mismatch
|
||||
**Symptom:** `IllegalStateException: Version mismatch`
|
||||
**Cause:** Compose Multiplatform plugin vs runtime version mismatch
|
||||
**Fix:** Align `composeMultiplatform` version in libs.versions.toml with Kotlin plugin
|
||||
|
||||
### Issue 3: Duplicate JNA Classes
|
||||
**Symptom:** `DuplicateClassException: com.sun.jna.Native`
|
||||
**Cause:** Both JAR and AAR JNA variants in classpath
|
||||
**Fix:** Use AAR (@aar) in androidMain, JAR in jvmMain (never in shared source sets)
|
||||
@@ -0,0 +1,422 @@
|
||||
# Version Catalog Guide
|
||||
|
||||
## Overview
|
||||
|
||||
AmethystMultiplatform uses Gradle's version catalog (`gradle/libs.versions.toml`) to centralize dependency management. This ensures version consistency across all modules and simplifies updates.
|
||||
|
||||
## Structure
|
||||
|
||||
### sections
|
||||
```toml
|
||||
[versions] # Version numbers (referenced by libraries and plugins)
|
||||
[libraries] # Library dependencies
|
||||
[plugins] # Gradle plugins
|
||||
```
|
||||
|
||||
## Version References
|
||||
|
||||
### Defining Versions
|
||||
```toml
|
||||
[versions]
|
||||
kotlin = "2.3.0"
|
||||
composeMultiplatform = "1.9.3"
|
||||
okhttp = "5.3.2"
|
||||
```
|
||||
|
||||
### Special Patterns
|
||||
|
||||
#### Android SDK Versions
|
||||
```toml
|
||||
android-compileSdk = "36"
|
||||
android-minSdk = "26"
|
||||
android-targetSdk = "36"
|
||||
```
|
||||
**Access in build.gradle.kts:**
|
||||
```kotlin
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
```
|
||||
|
||||
#### Version Suffixes (Git Commits)
|
||||
```toml
|
||||
androidKotlinGeohash = "b481c6a64e" # Jitpack commit hash
|
||||
markdown = "f92ef49c9d"
|
||||
```
|
||||
**Why:** For GitHub dependencies via Jitpack that don't have semantic versions
|
||||
|
||||
## Library Declarations
|
||||
|
||||
### Basic Pattern
|
||||
```toml
|
||||
[libraries]
|
||||
library-name = { group = "...", name = "...", version.ref = "..." }
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### Version Reference
|
||||
```toml
|
||||
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||
```
|
||||
|
||||
#### Module Reference (for multi-artifact libs)
|
||||
```toml
|
||||
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "androidxCamera" }
|
||||
```
|
||||
|
||||
#### Without Group (shorthand)
|
||||
```toml
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
```
|
||||
**Note:** Inherits version from BOM (compose-bom)
|
||||
|
||||
### BOMs (Bill of Materials)
|
||||
|
||||
#### AndroidX Compose BOM
|
||||
```toml
|
||||
[versions]
|
||||
composeBom = "2025.12.01"
|
||||
|
||||
[libraries]
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
```
|
||||
|
||||
**Usage in build.gradle.kts:**
|
||||
```kotlin
|
||||
val composeBom = platform(libs.androidx.compose.bom)
|
||||
implementation(composeBom)
|
||||
implementation(libs.androidx.ui) // Version from BOM
|
||||
implementation(libs.androidx.material3) // Version from BOM
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- All AndroidX Compose artifacts use compatible versions
|
||||
- Update single BOM version, not individual libraries
|
||||
- Prevents version conflicts
|
||||
|
||||
### Platform-Specific Variants
|
||||
|
||||
#### secp256k1 (KMP crypto library)
|
||||
```toml
|
||||
secp256k1KmpJniAndroid = "0.22.0"
|
||||
|
||||
[libraries]
|
||||
secp256k1-kmp-common = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-android = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-android", version.ref = "secp256k1KmpJniAndroid" }
|
||||
secp256k1-kmp-jni-jvm = { group = "fr.acinq.secp256k1", name = "secp256k1-kmp-jni-jvm", version.ref = "secp256k1KmpJniAndroid" }
|
||||
```
|
||||
|
||||
**Critical:** All three variants MUST share the same version
|
||||
|
||||
#### JNA (for LibSodium)
|
||||
```toml
|
||||
jna = "5.18.1"
|
||||
|
||||
[libraries]
|
||||
jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
|
||||
```
|
||||
|
||||
**Usage in build.gradle.kts:**
|
||||
```kotlin
|
||||
// androidMain - AAR packaging
|
||||
implementation("net.java.dev.jna:jna:5.18.1@aar")
|
||||
|
||||
// jvmMain - JAR packaging
|
||||
implementation(libs.jna)
|
||||
```
|
||||
|
||||
**Why:** Android needs AAR, JVM needs JAR (different artifact types)
|
||||
|
||||
## Plugin Declarations
|
||||
|
||||
### Basic Pattern
|
||||
```toml
|
||||
[plugins]
|
||||
plugin-id = { id = "...", version.ref = "..." }
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
#### Kotlin Plugins
|
||||
```toml
|
||||
[versions]
|
||||
kotlin = "2.3.0"
|
||||
|
||||
[plugins]
|
||||
jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
jetbrainsKotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
serialization = { id = 'org.jetbrains.kotlin.plugin.serialization', version.ref = 'kotlinxSerializationPlugin' }
|
||||
```
|
||||
|
||||
**Critical:** All Kotlin plugins MUST use the same Kotlin version
|
||||
|
||||
#### Android Gradle Plugin
|
||||
```toml
|
||||
[versions]
|
||||
agp = "8.13.2"
|
||||
|
||||
[plugins]
|
||||
androidApplication = { id = "com.android.application", version.ref = "agp" }
|
||||
androidLibrary = { id = "com.android.library", version.ref = "agp" }
|
||||
androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
|
||||
```
|
||||
|
||||
#### Compose Multiplatform
|
||||
```toml
|
||||
[versions]
|
||||
composeMultiplatform = "1.9.3"
|
||||
|
||||
[plugins]
|
||||
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
|
||||
```
|
||||
|
||||
### Plugin Application
|
||||
|
||||
```kotlin
|
||||
// In build.gradle.kts
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.composeMultiplatform)
|
||||
alias(libs.plugins.jetbrainsComposeCompiler)
|
||||
}
|
||||
```
|
||||
|
||||
## Usage in Build Files
|
||||
|
||||
### Accessing Versions
|
||||
```kotlin
|
||||
// Direct version access
|
||||
val kotlinVersion = libs.versions.kotlin.get()
|
||||
val minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
```
|
||||
|
||||
### Accessing Libraries
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
api(libs.secp256k1.kmp.common)
|
||||
implementation(libs.okhttp)
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing Plugins
|
||||
```kotlin
|
||||
plugins {
|
||||
alias(libs.plugins.kotlinMultiplatform)
|
||||
alias(libs.plugins.androidLibrary)
|
||||
}
|
||||
```
|
||||
|
||||
## Version Catalog Benefits
|
||||
|
||||
### 1. Centralized Version Management
|
||||
Update once, applies everywhere:
|
||||
```toml
|
||||
# Change one line
|
||||
kotlin = "2.3.0" → "2.4.0"
|
||||
|
||||
# Affects all usages
|
||||
- kotlinMultiplatform plugin
|
||||
- jetbrainsKotlinAndroid plugin
|
||||
- kotlin-stdlib
|
||||
- All Kotlin-related dependencies
|
||||
```
|
||||
|
||||
### 2. Type-Safe Accessors
|
||||
```kotlin
|
||||
// Compile-time checked
|
||||
implementation(libs.okhttp) // ✅ IDE autocomplete
|
||||
|
||||
// vs string-based (error-prone)
|
||||
implementation("com.squareup.okhttp3:okhttp:5.3.2") // ❌ No autocomplete
|
||||
```
|
||||
|
||||
### 3. Dependency Consistency
|
||||
```kotlin
|
||||
// All modules reference same catalog
|
||||
:quartz → libs.okhttp
|
||||
:commons → libs.okhttp
|
||||
:desktopApp → libs.okhttp
|
||||
|
||||
// Same version everywhere
|
||||
```
|
||||
|
||||
### 4. Gradle Sync Improvements
|
||||
- Faster IDE sync (pre-parsed catalog)
|
||||
- Better dependency resolution
|
||||
- Clearer error messages
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### GitHub Dependencies (Jitpack)
|
||||
```toml
|
||||
[versions]
|
||||
markdown = "f92ef49c9d" # Git commit hash
|
||||
|
||||
[libraries]
|
||||
markdown-ui = { group = "com.github.vitorpamplona.compose-richtext", name = "richtext-ui", version.ref = "markdown" }
|
||||
```
|
||||
|
||||
**Repository config** (in settings.gradle):
|
||||
```kotlin
|
||||
repositories {
|
||||
maven { url = "https://jitpack.io" }
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Artifact Libraries
|
||||
```toml
|
||||
[versions]
|
||||
media3 = "1.9.0"
|
||||
|
||||
[libraries]
|
||||
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
|
||||
androidx-media3-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
|
||||
androidx-media3-session = { group = "androidx.media3", name = "media3-session", version.ref = "media3" }
|
||||
```
|
||||
|
||||
**Why:** All media3 artifacts share same version for compatibility
|
||||
|
||||
### Test Dependencies
|
||||
```toml
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" }
|
||||
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
|
||||
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test"}
|
||||
```
|
||||
|
||||
## Version Update Strategy
|
||||
|
||||
### Check for Updates
|
||||
```bash
|
||||
# Using Gradle Versions Plugin (if installed)
|
||||
./gradlew dependencyUpdates
|
||||
|
||||
# Manual check
|
||||
# Browse to Maven Central for specific library
|
||||
```
|
||||
|
||||
### Update Process
|
||||
1. **Update version in catalog**
|
||||
```toml
|
||||
okhttp = "5.3.2" → "5.4.0"
|
||||
```
|
||||
|
||||
2. **Test locally**
|
||||
```bash
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
3. **Check for breaking changes**
|
||||
- Review library changelog
|
||||
- Run full test suite
|
||||
|
||||
4. **Commit with clear message**
|
||||
```
|
||||
chore: update okhttp 5.3.2 → 5.4.0
|
||||
```
|
||||
|
||||
### Critical Version Alignments
|
||||
|
||||
#### Kotlin Ecosystem
|
||||
```toml
|
||||
kotlin = "2.3.0"
|
||||
kotlinxCoroutinesCore = "1.10.2"
|
||||
kotlinxSerialization = "1.9.0"
|
||||
```
|
||||
**Rule:** Kotlin version must be compatible with kotlinx libraries
|
||||
|
||||
#### Compose Ecosystem
|
||||
```toml
|
||||
composeMultiplatform = "1.9.3"
|
||||
composeBom = "2025.12.01"
|
||||
kotlin = "2.3.0"
|
||||
```
|
||||
**Rule:** Compose Multiplatform → Kotlin version (see compatibility matrix)
|
||||
|
||||
#### AGP & Gradle
|
||||
```toml
|
||||
agp = "8.13.2"
|
||||
# Requires Gradle 8.9+
|
||||
```
|
||||
**Rule:** AGP version dictates minimum Gradle version
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue 1: Unresolved Reference
|
||||
**Error:** `Unresolved reference: libs`
|
||||
|
||||
**Cause:** Gradle version < 7.0 (version catalogs not supported)
|
||||
|
||||
**Fix:** Upgrade Gradle in `gradle/wrapper/gradle-wrapper.properties`
|
||||
|
||||
### Issue 2: Library Not Found
|
||||
**Error:** `Could not find com.example:library:1.0.0`
|
||||
|
||||
**Cause:** Repository not configured or typo in catalog
|
||||
|
||||
**Fix:**
|
||||
1. Check repository in settings.gradle
|
||||
2. Verify group/name/version in libs.versions.toml
|
||||
|
||||
### Issue 3: Version Conflict
|
||||
**Error:** `Conflict with dependency ... and ...`
|
||||
|
||||
**Cause:** Different versions of same library via transitive dependencies
|
||||
|
||||
**Fix:**
|
||||
```kotlin
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force(libs.okhttp.get().toString())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Naming Conventions
|
||||
```toml
|
||||
# Hyphen-separated, hierarchical
|
||||
androidx-compose-ui
|
||||
androidx-compose-material3
|
||||
kotlinx-coroutines-core
|
||||
|
||||
# Platform suffixes
|
||||
secp256k1-kmp-jni-android
|
||||
secp256k1-kmp-jni-jvm
|
||||
```
|
||||
|
||||
### 2. Group Related Dependencies
|
||||
```toml
|
||||
# Camera APIs together
|
||||
androidx-camera-core
|
||||
androidx-camera-camera2
|
||||
androidx-camera-view
|
||||
```
|
||||
|
||||
### 3. Document Special Cases
|
||||
```toml
|
||||
# JNA requires @aar for Android (see build.gradle.kts)
|
||||
jna = { group = "net.java.dev.jna", name = "jna", version.ref = "jna" }
|
||||
```
|
||||
|
||||
### 4. Keep BOMs Updated
|
||||
```toml
|
||||
# Update BOM, individual libs follow
|
||||
composeBom = "2025.12.01" # Latest stable
|
||||
```
|
||||
|
||||
### 5. Test Version Updates
|
||||
```bash
|
||||
# Before committing
|
||||
./gradlew :quartz:test
|
||||
./gradlew :commons:test
|
||||
./gradlew :desktopApp:run
|
||||
```
|
||||
Reference in New Issue
Block a user