Skip to main content
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.

Building for Web (Primary Platform)

The Web platform is the primary target for GreenhouseAdmin, with two build variants: 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.
1

Generate Xcode Project

The iOS app entry point is located in the iosApp/ directory.
# Open iOS project in Xcode
open iosApp/iosApp.xcodeproj
2

Build in Xcode

  1. Select a simulator or connected device from the device dropdown
  2. Press Cmd + B to build
  3. Press Cmd + R to run
3

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:
./gradlew clean

Full Rebuild

./gradlew clean build

Build All Platforms

# 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

Build Performance

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

Build docs developers (and LLMs) love