GreenhouseAdmin is a Kotlin Multiplatform project that can be built for multiple platforms. The primary target is Web (Wasm/JS) with additional support for Android, iOS, and Desktop (JVM).
Build Overview
The project uses Gradle as its build system with platform-specific build tasks. All commands use the Gradle wrapper (./gradlew or gradlew.bat) to ensure consistent build environments.
Always use the Gradle wrapper commands provided here rather than a global Gradle installation to ensure version compatibility.
The Web platform is the primary target for GreenhouseAdmin, with two build variants:
WebAssembly (Wasm) - Recommended
WebAssembly is the modern, faster option with better performance but requires newer browsers.
# Development build with hot reload
./gradlew :composeApp:wasmJsBrowserDevelopmentRun
# Production build
./gradlew :composeApp:wasmJsBrowserDistribution
Development build:
Starts a local development server (usually on port 8080)
Includes source maps for debugging
Supports hot reload for faster development
Not optimized for production
Production build output:
Located in composeApp/build/dist/wasmJs/productionExecutable/
Optimized and minified code
Ready for deployment to web servers
Includes all required assets and dependencies
JavaScript (JS) - Legacy
The JavaScript target supports older browsers but has slower performance.
# Development build with hot reload
./gradlew :composeApp:jsBrowserDevelopmentRun
# Production build
./gradlew :composeApp:jsBrowserDistribution
Production build output:
Located in composeApp/build/dist/js/productionExecutable/
Yarn Lock Management (Critical)
After any dependency changes in build.gradle.kts or libs.versions.toml, you must upgrade the Yarn lock files. Failure to do so will cause build failures.
# Update JS target lock file
./gradlew kotlinUpgradeYarnLock
# Update Wasm target lock file
./gradlew kotlinWasmUpgradeYarnLock
# Update both (recommended)
./gradlew kotlinUpgradeYarnLock kotlinWasmUpgradeYarnLock
When to run these commands:
After adding, removing, or updating dependencies
After changing Kotlin or Compose Multiplatform versions
When encountering “package-lock.json” or Yarn-related errors
Before committing dependency changes to version control
Building for Android
Build the Android application as an APK or AAB (Android App Bundle).
# Build debug APK
./gradlew :composeApp:assembleDebug
# Build release APK (requires signing configuration)
./gradlew :composeApp:assembleRelease
# Build Android App Bundle for Play Store
./gradlew :composeApp:bundleRelease
Build outputs:
Debug APK: composeApp/build/outputs/apk/debug/composeApp-debug.apk
Release APK: composeApp/build/outputs/apk/release/composeApp-release.apk
App Bundle: composeApp/build/outputs/bundle/release/composeApp-release.aab
Installing on Device/Emulator
# Install debug APK on connected device
./gradlew :composeApp:installDebug
# Run on device/emulator
./gradlew :composeApp:installDebug && adb shell am start -n com.apptolast.greenhouse.admin/.MainActivity
Android Build Configuration
Key configuration from build.gradle.kts:139-164:
android {
namespace = "com.apptolast.greenhouse.admin"
compileSdk = 36
defaultConfig {
applicationId = "com.apptolast.greenhouse.admin"
minSdk = 24 // Android 7.0+
targetSdk = 36 // Latest Android
versionCode = 1
versionName = "1.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
Building for iOS
iOS builds require Xcode and must be run on macOS.
Generate Xcode Project
The iOS app entry point is located in the iosApp/ directory. # Open iOS project in Xcode
open iosApp/iosApp.xcodeproj
Build in Xcode
Select a simulator or connected device from the device dropdown
Press Cmd + B to build
Press Cmd + R to run
Command-Line Build (Optional)
# Build iOS framework
./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64
# Or for physical device
./gradlew :composeApp:linkDebugFrameworkIosArm64
iOS Framework Configuration
From build.gradle.kts:45-53:
listOf (
iosArm64 (), // Physical devices
iosSimulatorArm64 () // Apple Silicon simulators
). forEach { iosTarget ->
iosTarget.binaries. framework {
baseName = "ComposeApp"
isStatic = true
}
}
Building for Desktop (JVM)
Build and run the desktop application for Windows, macOS, or Linux.
# Run desktop application
./gradlew :composeApp:run
# Create distribution packages
./gradlew :composeApp:packageDistributionForCurrentOS
# Create specific formats
./gradlew :composeApp:packageDmg # macOS
./gradlew :composeApp:packageDeb # Linux
./gradlew :composeApp:packageMsi # Windows
Distribution outputs:
macOS: composeApp/build/compose/binaries/main/dmg/
Windows: composeApp/build/compose/binaries/main/msi/
Linux: composeApp/build/compose/binaries/main/deb/
Desktop Configuration
From build.gradle.kts:170-180:
compose. desktop {
application {
mainClass = "com.apptolast.greenhouse.admin.MainKt"
nativeDistributions {
targetFormats (TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "com.apptolast.greenhouse.admin"
packageVersion = "1.0.0"
}
}
}
Build Configuration
Environment-Specific Builds
The application uses BuildKonfig to inject environment variables at compile time. Configure via local.properties:
# Development environment
API_BASE_URL =https://api-dev.example.com/api/v1/
# Production environment
API_BASE_URL =https://api.example.com/api/v1/
The API_BASE_URL value is compiled into the application binary. To change environments, update local.properties and rebuild.
BuildKonfig Configuration
From build.gradle.kts:182-189:
buildkonfig {
packageName = "com.apptolast.greenhouse.admin"
defaultConfigs {
// Injected from local.properties or CI/CD secrets
buildConfigField (STRING, "API_BASE_URL" ,
localProperties. getProperty ( "API_BASE_URL" , "" ))
}
}
Common Build Tasks
Clean Build
Remove all build artifacts:
Full Rebuild
# Build everything
./gradlew build
# Build only Web targets
./gradlew :composeApp:jsBrowserDistribution :composeApp:wasmJsBrowserDistribution
Check Build Configuration
# List all available tasks
./gradlew tasks
# List tasks for specific module
./gradlew :composeApp:tasks
# Show project structure
./gradlew projects
Gradle Configuration Cache
Enabled by default in gradle.properties:
org.gradle.configuration-cache =true
org.gradle.caching =true
Memory Optimization
Adjust JVM memory in gradle.properties:6-7:
org.gradle.jvmargs =-Xmx4096M - Dfile.encoding =UTF-8
kotlin.daemon.jvmargs =-Xmx4092M
For large projects or low-memory systems, you may need to adjust these values. Increase for better performance or decrease to reduce memory usage.
Parallel Builds
# Enable parallel execution
./gradlew build --parallel
# Limit parallel workers
./gradlew build --parallel --max-workers=4
Troubleshooting
Web Build Failures
Issue: Yarn lock file errors
Solution:
./gradlew kotlinUpgradeYarnLock kotlinWasmUpgradeYarnLock
./gradlew clean :composeApp:wasmJsBrowserDistribution
Android Build Failures
Issue: SDK not found
Solution:
# Ensure ANDROID_HOME is set
export ANDROID_HOME = $HOME / Library / Android / sdk
./gradlew :composeApp:assembleDebug
Out of Memory Errors
Solution:
# Stop Gradle daemon
./gradlew --stop
# Increase memory in gradle.properties
# Then rebuild
./gradlew clean build --no-daemon
Incremental Build Issues
Solution:
# Clean and rebuild
./gradlew clean build --rerun-tasks
Next Steps
Testing Learn how to run tests for different platforms
Deployment Deploy your builds using Docker and CI/CD