Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kotlin multiplatform: how to reduce build time?

I have a kotlin multiplatform project to make a shared library targeting iOS and Android. I have a handful of multiplatform external libraries : sqldelight, kodein-di, ktor, klock, kotlinx-coroutines and kotlinx-serialization.

It takes around 5 minutes to build the shared lib as an apple framework using command line ./gradlew :SharedCode:build

Is it possible to reduce this build time ?

My SharedCode multiplatform lib build.gradle

apply plugin: 'kotlinx-serialization'
apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.multiplatform'
apply plugin: "com.squareup.sqldelight"
apply plugin: "org.jetbrains.dokka"

group = 'com.example.multiplatform'
version = '1.0'

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 15
    }
    buildTypes {
        release {
            minifyEnabled false
        }
    }
    compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }
}

sqldelight {
    MyDB {
        packageName = "com.example.multiplatform"
    }
}

dokka {
    outputFormat = "markdown"
    impliedPlatforms = ["common"] // This will force platform tags for all non-common sources e.g. "JVM"
    kotlinTasks {
        // dokka fails to retrieve sources from MPP-tasks so they must be set empty to avoid exception
        // use sourceRoot instead (see below)
        []
    }
    sourceRoot {
        // assuming there is only a single source dir...
        path = kotlin.sourceSets.commonMain.kotlin.srcDirs[0]
        platforms = ["common"]
    }
}

dependencies {
    // Specify Kotlin/JVM stdlib dependency.
    implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'

    testImplementation 'junit:junit:4.12'
    testImplementation 'org.jetbrains.kotlin:kotlin-test'
    testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'

    androidTestImplementation 'junit:junit:4.12'
    androidTestImplementation 'org.jetbrains.kotlin:kotlin-test'
    androidTestImplementation 'org.jetbrains.kotlin:kotlin-test-junit'

    androidTestImplementation "io.ktor:ktor-client-serialization-jvm:$ktor_version"

    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

kotlin {
    targets {
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos")   \
                            ? presets.iosArm64 : presets.iosX64

        fromPreset(iOSTarget, 'ios') {
            binaries {
                framework('SharedCode')
            }
        }

        fromPreset(presets.android, 'android')
    }

    sourceSets {
       all {
            dependencies {
                implementation "org.kodein.di:kodein-di-core:$kodein_version"
                implementation "org.kodein.di:kodein-di-erased:$kodein_version"
            }
        }

        commonMain {
            dependencies {
                //HTTP
                implementation "io.ktor:ktor-client-json:$ktor_json_version"
                implementation "io.ktor:ktor-client-serialization:$ktor_version"
                //Coroutines
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlinx_coroutines_version"
                //Kotlinx serialization
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
                //kclock
                implementation "com.soywiz.korlibs.klock:klock:$klock_version"
            }
        }

        commonTest {
            dependencies {
                implementation 'org.jetbrains.kotlin:kotlin-test-common'
                implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'

                implementation "io.ktor:ktor-client-mock:$ktor_version"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$kotlinx_coroutines_version"
                implementation "org.kodein.di:kodein-di-core:$kodein_version"
                implementation "org.kodein.di:kodein-di-erased:$kodein_version"
            }
        }

        androidMain {
            dependencies {
                //HTTP
                implementation "io.ktor:ktor-client-json-jvm:$ktor_json_version"
                implementation "io.ktor:ktor-client-serialization-jvm:$ktor_version"
                //Coroutines
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_coroutines_version"
                //Kotlinx serialization
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
                //sqldelight
                implementation "com.squareup.sqldelight:android-driver:$sqldelight_version"
            }
        }

        androidTest {
            dependencies {
                implementation 'junit:junit:4.12'
                implementation 'org.jetbrains.kotlin:kotlin-test'
                implementation 'org.jetbrains.kotlin:kotlin-test-junit'
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
                implementation "io.ktor:ktor-client-mock-jvm:$ktor_version"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version"
            }
        }

        iosMain {
            dependencies {
                //HTTP
                implementation "io.ktor:ktor-client-ios:$ktor_version"
                implementation "io.ktor:ktor-client-json-native:$ktor_json_version"
                implementation "io.ktor:ktor-client-serialization-native:$ktor_version"
                //Coroutines
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$kotlinx_coroutines_version"
                //kotlinx serialization
                implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
                //sqldelight
                implementation "com.squareup.sqldelight:ios-driver:$sqldelight_version"
            }
        }

        iosTest {
            dependencies {
                implementation "io.ktor:ktor-client-mock-native:$ktor_version"
                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$kotlinx_coroutines_version"
            }
        }
    }
}

task iosTest {
    def device = project.findProperty("iosDevice")?.toString() ?: "iPhone X"
    def isDevice = System.getenv('SDK_NAME')?.startsWith("iphoneos")
    dependsOn 'linkTestDebugExecutableIos'
    group = JavaBasePlugin.VERIFICATION_GROUP
    description = "Runs tests for target 'ios' on an iOS simulator"

    doLast {
        if (!isDevice) {
            def binary = kotlin.targets.ios.binaries.getExecutable('test', 'DEBUG').outputFile
            exec {
                commandLine 'xcrun', 'simctl', 'spawn', device, binary.absolutePath
            }
        }
    }

}

tasks.check.dependsOn iosTest

tasks.withType(Test) {
    testLogging {
        exceptionFormat "full"
        events "passed", "failed"
        showStandardStreams true
    }
}

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = project.findProperty("XCODE_CONFIGURATION")?.toUpperCase() ?: 'DEBUG'
    final def framework = kotlin.targets.ios.binaries.getFramework("SharedCode", mode)

    inputs.property "mode", mode
    dependsOn framework.linkTask

    from { framework.outputFile.parentFile }
    into frameworkDir

    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
            setExecutable(true)
        }
    }
}
tasks.build.dependsOn packForXCode
like image 701
Alan Avatar asked Oct 15 '22 13:10

Alan


1 Answers

When I'm actively coding and testing, I just build the framework target rather than building the whole project with build. Calling build generally makes the debug and release frameworks, as well as test frameworks of both.

So, in my case, I run linkMainDebugFrameworkIos instead of the full build. That takes significantly less time. 1m 35s vs at least 5m, if not longer, for a full build.

That task name will probably change with 1.3.40+, and sqldelight and other libs should be on 1.3.40 soon-ish. Just FYI.

like image 115
Kevin Galligan Avatar answered Nov 02 '22 08:11

Kevin Galligan