BuildConfig for Kotlin Multiplatform Project

Overview

BuildKonfig

Maven Central

BuildConfig for Kotlin Multiplatform Project.
It currently supports embedding values from gradle file.

Table Of Contents

Motivation

Passing values from Android/iOS or any other platform code should work, but it's a hassle.
Setting up Android to read values from properties and add those into BuildConfig, and do the equivalent in iOS?
Rather I'd like to do it once.

Usage

Requirements

  • Kotlin 1.4.0 or later
  • Kotlin Multiplatform Project
  • Gradle 6.5 or later

Gradle Configuration

Simple configuration

Groovy DSL
buildScript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0'
        classpath 'com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:latest_version'
    }
}

apply plugin: 'org.jetbrains.kotlin.multiplatform'
apply plugin: 'com.codingfeline.buildkonfig'

kotlin {
    // your target config...
    android()
    iosX64('ios')
}

buildkonfig {
    packageName = 'com.example.app'
    // objectName = 'YourAwesomeConfig'
    // exposeObjectWithName = 'YourAwesomePublicConfig'

    defaultConfigs {
        buildConfigField 'STRING', 'name', 'value'
    }
}
Kotlin DSL
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0")
        classpath("com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:latest_version")
    }
}

plugins {
    kotlin("multiplatform")
    id("com.codingfeline.buildkonfig")
}

kotlin {
    // your target config...
    android()
    iosX64('ios')
}

buildkonfig {
    packageName = "com.example.app"
    // objectName = "YourAwesomeConfig"
    // exposeObjectWithName = "YourAwesomePublicConfig"

    defaultConfigs {
        buildConfigField(STRING, "name", "value")
    }
}
  • packageName Set the package name where BuildKonfig is being placed. Required.
  • objectName Set the name of the generated object. Defaults to BuildKonfig.
  • exposeObjectWithName Set the name of the generated object, and make it public.
  • defaultConfigs Set values which you want to have in common. Required.

To generate BuildKonfig files, run generateBuildKonfig task.
This task will be automatically run upon execution of kotlin compile tasks.

Above configuration will generate following simple object.

// commonMain
package com.example.app

internal object BuildKonfig {
    val name: String = "value"
}

Configuring target dependent values

If you want to change value depending on your targets, you can use targetConfigs to define target-dependent values.

Groovy DSL
buildScript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0'
        classpath 'com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:latest_version'
    }
}

apply plugin: 'org.jetbrains.kotlin.multiplatform'
apply plugin: 'com.codingfeline.buildkonfig'

kotlin {
    // your target config...
    android()
    iosX64('ios')
}

buildkonfig {
    packageName = 'com.example.app'
    
    // default config is required
    defaultConfigs {
        buildConfigField 'STRING', 'name', 'value'
        buildConfigNullableField 'STRING', 'nullableField', null
    }
    
    targetConfigs {
        // this name should be the same as target names you specified
        android {
            buildConfigField 'STRING', 'name2', 'value2'
            buildConfigNullableField 'STRING', 'nullableField', 'NonNull-value'
        }
        
        ios {
            buildConfigField 'STRING', 'name', 'valueForNative'
        }
    }
}
Kotlin DSL
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.STRING

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0")
        classpath("com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:latest_version")
    }
}

plugins {
    kotlin("multiplatform")
    id("com.codingfeline.buildkonfig")
}

kotlin {
    // your target config...
    android()
    iosX64('ios')
}

buildkonfig {
    packageName = "com.example.app"

    // default config is required
    defaultConfigs {
        buildConfigField(STRING, "name", "value")
    }

    targetConfigs {
        // names in create should be the same as target names you specified
        create("android") {
            buildConfigField(STRING, "name2", "value2")
            buildConfigNullableField(STRING, "nullableField", "NonNull-value")
        }

        create("ios") {
            buildConfigField(STRING, "name", "valueForNative")
        }
    }
}
  • packageName Set the package name where BuildKonfig is being placed. Required.
  • objectName Set the name of the generated object. Defaults to BuildKonfig.
  • exposeObjectWithName Set the name of the generated object, and make it public.
  • defaultConfigs Set values which you want to have in common. Required.
  • targetConfigs Set target specific values as closure. You can overwrite values specified in defaultConfigs.
  • buildConfigField(String type, String name, String value) Add new value or overwrite existing one.
  • buildConfigNullableField((String type, String name, String value) Add new nullable value or overwrite existing one.

Above configuration will generate following codes.

// commonMain
package com.example.app

internal expect object BuildKonfig {
    val name: String
    val nullableField: String?
}
// androidMain
package com.example.app

internal actual object BuildKonfig {
    actual val name: String = "value"
    actual val nullableField: String? = "NonNull-value"
    val name2: String = "value2"
}
// iosMain
package com.example.app

internal actual object BuildKonfig {
    actual val name: String = "valueForNative"
    actual val nullableField: String? = null
}

Note about the hierarchical project structure

Kotlin 1.4.0 adds support for the hierarchical project structure, but BuildKonfig currently does not support this. You can use the hierarchical project structure, but intermediate SourceSets can only see fields defined in defaultConfigs block. See details and progress at here.

Product Flavor?

Yes(sort of).
Kotlin Multiplatform Project does not support product flavor. Kotlin/Native part of the project has release/debug distinction, but it's not global.
So to mimick product flavor capability of Android, we need to provide additional property in order to determine flavors.

Specify default flavor in your gradle.properties

# ROOT_DIR/gradle.properties
buildkonfig.flavor=dev
Groovy DSL
// ./mpp_project/build.gradle

buildkonfig {
    packageName = 'com.example.app'
    
    // default config is required
    defaultConfigs {
        buildConfigField 'STRING', 'name', 'value'
    }
    // flavor is passed as a first argument of defaultConfigs 
    defaultConfigs("dev") {
        buildConfigField 'STRING', 'name', 'devValue'
    }
    
    targetConfigs {
        android {
            buildConfigField 'STRING', 'name2', 'value2'
        }
        
        ios {
            buildConfigField 'STRING', 'name', 'valueIos'
        }
    }
    // flavor is passed as a first argument of targetConfigs
    targetConfigs("dev") {
        ios {
            buildConfigField 'STRING', 'name', 'devValueIos'
        }
    }
}
Kotlin DSL
import com.codingfeline.buildkonfig.compiler.FieldSpec.Type.
import com.codingfeline.buildkonfig.gradle.TargetConfigDsl

buildkonfig {
    packageName = "com.example.app"

    // default config is required
    defaultConfigs {
        buildConfigField(STRING, "name", "value")
    }
    // flavor is passed as a first argument of defaultConfigs 
    defaultConfigs("dev") {
        buildConfigField(STRING, "name", "devValue")
    }

    targetConfigs(closureOf<NamedDomainObjectContainer<TargetConfigDsl>> {
        create("android") {
            buildConfigField(STRING, "name2", "value2")
        }

        create("ios") {
            buildConfigField(STRING, "name", "valueIos")
        }
    })
    // flavor is passed as a first argument of targetConfigs
    targetConfigs("dev", closureOf<NamedDomainObjectContainer<TargetConfigDsl>> {
        create("ios") {
            buildConfigField(STRING, "name", "devValueIos")
        }
    })
}

In a development phase you can change value in gradle.properties as you like.
In CI environment, you can pass value via CLI $ ./gradlew build -Pbuildkonfig.flavor=release

Overwriting Values

If you configure same field across multiple defaultConfigs and targetConfigs, flavored targetConfigs is the strongest.

Lefter the stronger.

Flavored TargetConfig > TargetConfig > Flavored DefaultConfig > DefaultConfig

Supported Types

  • String
  • Int
  • Long
  • Float
  • Boolean

Try out the sample

Have a look at ./sample directory.

# Publish the latest version of the plugin to mavenLocal()
$ ./gradlew publishToMavenLocal

# Try out the samples.
# BuildKonfig will be generated in ./sample/build/buildkonfig
$ ./gradlew -p sample generateBuildKonfig
Issues
  • Merge buildKonfig for native targets

    Merge buildKonfig for native targets

    For iOS, we have to create a target for the simulator (iosX64) and the device (iosArm64/iosArm32). I'm merging them through:

    val iosX64 = iosX64()
    val iosArm64 = iosArm64("ios")
    android()
    
    configure(listOf(iosX64, iosArm64)) {
        binaries {
            framework(frameworkName) {
                baseName = frameworkName
            }
        }
    }
    
    val commonMain by sourceSets.getting {
        dependencies {
            ...
        }
    }
    
    val iosX64Main by sourceSets.getting
    val iosMain by sourceSets.getting {
        iosX64Main.dependsOn(this)
        dependencies {
            ...
        }
    }
    

    Problem is, buildkonfig generates for both the targets but since iosX64 is dependent on iosArm64, it produces an redeclaration error. Is there any way we can ignore some of the targets?

    opened by kuuuurt 17
  • Allow BuildKonfig to be public

    Allow BuildKonfig to be public

    Object is generated as internal. To use it from the iOS or Android app requires adding another layer to expose the values. Add an option to generate it as public.

    contribution welcome enhancement 
    opened by dalewking 12
  • Support Gradle Kotlin DSL

    Support Gradle Kotlin DSL

    enhancement 
    opened by yshrsmz 6
  • Add isDebug to generated class

    Add isDebug to generated class

    In Android, BuildConfig holds a DEBUG variable we can use to check the build type.

    Can we have this? Would be useful for some cases like enabling Ktor's Logging on debug build but not on release builds.

    opened by kuuuurt 6
  • [java.lang.IllegalArgumentException: packageName must be provided] thrown when adding a js(IR) target

    [java.lang.IllegalArgumentException: packageName must be provided] thrown when adding a js(IR) target

    When adding a js(IR) target

    plugins {
        kotlin("multiplatform")
        id("com.android.library")
        id("com.codingfeline.buildkonfig")
    }
    
    kotlin {
        android()
       
        ...
    
        iosTarget("ios") { ... }
    
        js(IR) {
            browser()
        }
    
        sourceSets {
            val commonMain by getting
            val androidMain by getting
            val iosMain by getting
            val jsMain by getting
        }
    }
    
    configure<com.codingfeline.buildkonfig.gradle.BuildKonfigExtension> {
        packageName = "com.sample.buildkonfig.issues"
    
        defaultConfigs {
            buildConfigField(STRING, "VERSION", "mysupersecretpassword")
        }
    }
    

    throws during sync

    Caused by: java.lang.IllegalArgumentException: packageName must be provided
    

    even when the packageName is provided

    Minimal working example here: https://github.com/xxfast/BuildKonfig-Issues

    Or download here: BuildKonfig-Issues-master.zip

    full sync stacktrace:

    
    org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreationException: Could not create task ':shared:generateBuildKonfig'.
    	at org.gradle.api.internal.tasks.DefaultTaskContainer.taskCreationException(DefaultTaskContainer.java:719)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer.access$600(DefaultTaskContainer.java:77)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.domainObjectCreationException(DefaultTaskContainer.java:711)
    	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:948)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.access$1401(DefaultTaskContainer.java:658)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider$1.run(DefaultTaskContainer.java:684)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer$TaskCreatingProvider.tryCreate(DefaultTaskContainer.java:680)
    	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.calculateOwnValue(DefaultNamedDomainObjectCollection.java:929)
    	at org.gradle.api.internal.provider.AbstractMinimalProvider.calculateValue(AbstractMinimalProvider.java:103)
    	at org.gradle.api.internal.provider.Collectors$ElementFromProvider.collectEntries(Collectors.java:98)
    	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectEntries(Collectors.java:329)
    	at org.gradle.api.internal.provider.Collectors$TypedCollector.collectInto(Collectors.java:324)
    	at org.gradle.api.internal.collections.DefaultPendingSource.realize(DefaultPendingSource.java:61)
    	at org.gradle.api.internal.collections.DefaultPendingSource.realizePending(DefaultPendingSource.java:39)
    	at org.gradle.api.internal.collections.SortedSetElementSource.iterator(SortedSetElementSource.java:63)
    	at org.gradle.api.internal.DefaultDomainObjectCollection.iterator(DefaultDomainObjectCollection.java:130)
    	at org.gradle.api.internal.tasks.DefaultTaskContainer.iterator(DefaultTaskContainer.java:624)
    	at org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.buildTestRunTasks(KotlinMPPGradleModelBuilder.kt:1114)
    	at org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.buildTarget(KotlinMPPGradleModelBuilder.kt:340)
    	at org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.buildTargets(KotlinMPPGradleModelBuilder.kt:258)
    	at org.jetbrains.kotlin.gradle.KotlinMPPGradleModelBuilder.buildAll(KotlinMPPGradleModelBuilder.kt:59)
    	at org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder.buildAll(ExtraModelBuilder.java:115)
    	at org.jetbrains.plugins.gradle.tooling.internal.ExtraModelBuilder.buildAll(ExtraModelBuilder.java:80)
    	at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuilderWithParameter.build(DefaultToolingModelBuilderRegistry.java:192)
    	at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.lambda$build$0(DefaultToolingModelBuilderRegistry.java:211)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.lambda$withProjectLock$3(DefaultProjectStateRegistry.java:310)
    	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:310)
    	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.fromMutableState(DefaultProjectStateRegistry.java:291)
    	at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$LockSingleProjectBuilder.build(DefaultToolingModelBuilderRegistry.java:211)
    	at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder$1.call(DefaultToolingModelBuilderRegistry.java:246)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.tooling.provider.model.internal.DefaultToolingModelBuilderRegistry$BuildOperationWrappingBuilder.build(DefaultToolingModelBuilderRegistry.java:243)
    	at org.gradle.tooling.internal.provider.runner.DefaultBuildController.getModel(DefaultBuildController.java:102)
    	at org.gradle.tooling.internal.consumer.connection.ParameterAwareBuildControllerAdapter.getModel(ParameterAwareBuildControllerAdapter.java:39)
    	at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.getModel(UnparameterizedBuildController.java:113)
    	at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.getModel(NestedActionAwareBuildControllerAdapter.java:31)
    	at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.findModel(UnparameterizedBuildController.java:97)
    	at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.findModel(NestedActionAwareBuildControllerAdapter.java:31)
    	at org.gradle.tooling.internal.consumer.connection.UnparameterizedBuildController.findModel(UnparameterizedBuildController.java:81)
    	at org.gradle.tooling.internal.consumer.connection.NestedActionAwareBuildControllerAdapter.findModel(NestedActionAwareBuildControllerAdapter.java:31)
    	at org.jetbrains.plugins.gradle.model.ProjectImportAction$MyBuildController.findModel(ProjectImportAction.java:542)
    	at org.jetbrains.plugins.gradle.model.ClassSetProjectImportModelProvider.populateProjectModels(ClassSetProjectImportModelProvider.java:31)
    	at org.jetbrains.plugins.gradle.model.ProjectImportAction.addProjectModels(ProjectImportAction.java:201)
    	at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:114)
    	at org.jetbrains.plugins.gradle.model.ProjectImportAction.execute(ProjectImportAction.java:41)
    	at org.gradle.tooling.internal.consumer.connection.InternalBuildActionAdapter.execute(InternalBuildActionAdapter.java:64)
    	at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.runAction(AbstractClientProvidedBuildActionRunner.java:134)
    	at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner$ActionRunningListener.buildFinished(AbstractClientProvidedBuildActionRunner.java:119)
    	at jdk.internal.reflect.GeneratedMethodAccessor324.invoke(Unknown Source)
    	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:398)
    	at org.gradle.internal.event.DefaultListenerManager$ListenerDetails.dispatch(DefaultListenerManager.java:380)
    	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61)
    	at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:368)
    	at org.gradle.internal.event.DefaultListenerManager$EventBroadcast$ListenerDispatch.dispatch(DefaultListenerManager.java:355)
    	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:43)
    	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:245)
    	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:157)
    	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:61)
    	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:346)
    	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:249)
    	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:141)
    	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
    	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    	at com.sun.proxy.$Proxy18.buildFinished(Unknown Source)
    	at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:196)
    	at org.gradle.initialization.DefaultGradleLauncher.finishBuild(DefaultGradleLauncher.java:132)
    	at org.gradle.internal.invocation.GradleBuildController$1.create(GradleBuildController.java:72)
    	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:213)
    	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:67)
    	at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:56)
    	at org.gradle.tooling.internal.provider.runner.AbstractClientProvidedBuildActionRunner.runClientAction(AbstractClientProvidedBuildActionRunner.java:53)
    	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:47)
    	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:66)
    	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
    	at org.gradle.tooling.internal.provider.FileSystemWatchingBuildActionRunner.run(FileSystemWatchingBuildActionRunner.java:90)
    	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:41)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:49)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:44)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:44)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.lambda$execute$0(InProcessBuildActionExecuter.java:59)
    	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:86)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:58)
    	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:30)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.lambda$execute$0(BuildTreeScopeLifecycleBuildActionExecuter.java:34)
    	at org.gradle.internal.buildtree.BuildTreeState.run(BuildTreeState.java:53)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:33)
    	at org.gradle.launcher.exec.BuildTreeScopeLifecycleBuildActionExecuter.execute(BuildTreeScopeLifecycleBuildActionExecuter.java:28)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:104)
    	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:55)
    	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:64)
    	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:37)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.lambda$execute$0(SessionScopeLifecycleBuildActionExecuter.java:54)
    	at org.gradle.internal.session.BuildSessionState.run(BuildSessionState.java:67)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:50)
    	at org.gradle.tooling.internal.provider.SessionScopeLifecycleBuildActionExecuter.execute(SessionScopeLifecycleBuildActionExecuter.java:36)
    	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:36)
    	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:25)
    	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:59)
    	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:31)
    	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:58)
    	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:42)
    	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:47)
    	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:31)
    	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:65)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:29)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
    	at org.gradle.util.Swapper.swap(Swapper.java:38)
    	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:84)
    	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
    	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
    	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    	at java.base/java.lang.Thread.run(Thread.java:834)
    Caused by: java.lang.IllegalArgumentException: packageName must be provided
    	at com.codingfeline.buildkonfig.gradle.BuildKonfigPlugin$configure$3$task$1.execute(BuildKonfigPlugin.kt:68)
    	at com.codingfeline.buildkonfig.gradle.BuildKonfigPlugin$configure$3$task$1.execute(BuildKonfigPlugin.kt:16)
    	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44)
    	at org.gradle.api.internal.DefaultMutationGuard$2.execute(DefaultMutationGuard.java:44)
    	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:100)
    	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:95)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    	at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    	at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:92)
    	at org.gradle.internal.ImmutableActionSet$SetWithManyActions.execute(ImmutableActionSet.java:329)
    	at org.gradle.api.internal.DefaultDomainObjectCollection.doAdd(DefaultDomainObjectCollection.java:264)
    	at org.gradle.api.internal.DefaultNamedDomainObjectCollection.doAdd(DefaultNamedDomainObjectCollection.java:113)
    	at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:258)
    	at org.gradle.api.internal.DefaultNamedDomainObjectCollection$AbstractDomainObjectCreatingProvider.tryCreate(DefaultNamedDomainObjectCollection.java:944)
    	... 165 more
    
    opened by xxfast 4
  • Update readme to show how to configure using new gradle plugin syntax

    Update readme to show how to configure using new gradle plugin syntax

    The readme shows the old way to configure the plugin using classpath inside buildscript, but since you are publishing in the gradle plugin portal there is no reason to so it that way. The readme should reflect the prefered way using the new plugin syntax.

    contribution welcome good first issue 
    opened by dalewking 3
  • Kotlin 1.4 support

    Kotlin 1.4 support

    opened by slipdef 3
  • flavor support

    flavor support

    Some thing like Android's product flavor could be useful. But since Kotlin MPP does not support it, we need to come up with some workaround.

    buildkonfig {
      targetConfigs {
        android {
          flavor = "dev"
        }
        android {
          flavor = "release"
        }
    
        ios {
          flavor = "dev"
        }
        ios {
          flavor = "release"
        }
      }
    }
    
    opened by yshrsmz 3
  • `targetConfigs` does not work in Kotlin DSL

    `targetConfigs` does not work in Kotlin DSL

    build.config.kts (:kmm_shared:feature)

    plugins {
        //  ..
        id("com.codingfeline.buildkonfig") version "0.7.0"
    }
    
    buildkonfig {
        packageName = "app.mobius.feature"
    
        defaultConfigs { }
    
        targetConfigs {  // ERROR
            android{ }
        }
    
    }
    
    kotlin {
        android()
    }
    
    

    DefaultConfigs is working but targetConfigs generate the following error:

    ERROR:

     * What went wrong:
    Script compilation errors:
    
      Line 42:     targetConfigs {
                                 ^ Type mismatch: inferred type is () -> Unit but Closure<*> was expected
    
    * Exception is:
    ScriptCompilationException(errors=[ScriptCompilationError(message=Type mismatch: inferred type is () -> Unit but Closure<*> was expected, location=/tmp/gradle-kotlin-dsl-2843013171482525738.tmp/build.gradle.kts (42:19)), ScriptCompilationError(message=Type mismatch: inferred type is () -> Unit but Closure<*> was expected, location=/tmp/gradle-kotlin-dsl-2843013171482525738.tmp/build.gradle.kts (59:19))])
            at org.gradle.kotlin.dsl.support.KotlinCompilerKt.compileKotlinScriptModuleTo(KotlinCompiler.kt:177)
    .....
    
    opened by Coronel-B 3
  • Usage in build.gradle.kts

    Usage in build.gradle.kts

    Hi

    How can I do the buildkonfig in the Gradle Kotlinscript variant ?

    buildkonfig { } gives me a syntax error obviously

    opened by clojj 3
  • Build failed when BuildKonfig configuration moved to a separate file

    Build failed when BuildKonfig configuration moved to a separate file

    Hi,

    Our buildkonfig configuration has grown to 115 lines of code (with formatting), so we extract it to another gradle file. However, applying this plug-in to our shared/build.gradle fails our build with an exception.

    Steps to reproduce:

    • Extract buildkonfig to a separate gradle file, e.i. buildkonfig.gradle in our case.
    • Apply the plug-in by add apply(from = "$rootDir/gradle/buildkonfig.gradle") in shared/build.gradle file
    • Running ./gradlew build should produce the error below:

    Error:

    FAILURE: Build failed with an exception.
    
    * Where:
    Build file '/Users/kamara/sandbox/kmm-app/app/shared/build.gradle.kts' line: 133
    
    * What went wrong:
    A problem occurred evaluating script.
    > No signature of method: buildkonfig_bpv4u8f025ackfhk6ga5bwbu6.buildkonfig() is applicable for argument types: (buildkonfig_bpv4u8f025ackfhk6ga5bwbu6$_run_closure1) values: [[email protected]]
    
    opened by ernestkamara 1
  • provide a way to read values from properties file

    provide a way to read values from properties file

    Not sure the DSL below is possible, but something like this

    buildKonfig {
      defaultConfigs {
        // load all values as string
        fromProperties file('./secrets.properties') {
          // specify type
          propertyAs type: 'INT', key: 'KEY_OF_PROPERTY'
    
          // also specify different name
          propertyAs type: 'LONG', key: 'key.of.other', name: 'DIFFERENT_NAME'
        }
    
        // you can also add other stuff as usual
        buildKonfigField('STRING', 'FOO', 'bar')
      }
    }
    
    opened by yshrsmz 5
  • Support other Kotlin plugins

    Support other Kotlin plugins

    Currently the multiplatform plugin is required to use BuildKonfig. It would be nice to be able to use BuildKonfig in non-multiplatform projects as well, namely in standalone JVM, JS, or Native projects that use the other respective Kotlin plugins.

    enhancement help wanted 
    opened by dellisd 1
  • Support intermediate SourceSets

    Support intermediate SourceSets

    blocked by #36

    Kotlin 1.4.0 adds support for the hierarchical project structure.

    So how can we support this?

    kotlin {
      jvm()
      ios()
    }
    
    buildkonfig {
    
      defaultConfigs {
        buildConfigField 'STRING', 'commonField', 'commonValue'
      }
    
      targetConfigs {
        ios {
          buildConfigField 'STRING', 'iosField', 'iosValue'
        }
      }
    }
    

    Say I have a configuration above. Currently, BuildKonfig does not generate an object for iosMain, simply ignore it as there's no target named ios. We need to declare iosX64 and iosArm64 in targetConfigs.

    To generate objects for intermediate SourceSets, we need to check SourceSets' dependencies.

    In BuildKonfigPlugin,

    targets.filter { it.name != "metadata" }.forEach { target ->
        val name = "${target.name}Main"
        val sourceSetMain = sourceSets.getByName(name)
    
        // !!!!
        val dependentSourceSets = sourceSetMain.dependsOn.filter { it.name != "commonMain" }
        println(dependentSourceSets)
    
        val outDirMain = File(outputDirectory, name).also { it.mkdirs() }
        sourceSetMain.kotlin.srcDirs(outDirMain.toRelativeString(project.projectDir))
        outputDirectoryMap[TargetName(target.name, target.platformType.toKgqlPlatformType())] = outDirMain
    }
    

    If target is iosX64 and that is defined via ios(), this dependentSourceSets should have a iosMain SourceSets.

    enhancement help wanted 
    opened by yshrsmz 1
  • add an easy way to load flavor from arbitrary properties file

    add an easy way to load flavor from arbitrary properties file

    def configureBuildKonfigFlavorFromLocalProperties() {
        if (project.gradle.startParameter.projectProperties.containsKey("buildkonfig.flavor")) {
            // prefer cli parameter
            return
        }
        if (Files.exists(Paths.get("$project.rootDir/local.properties"))) {
            def key = "buildkonfig.flavor"
            // load buildkonfig.flavor if exists
            def localProperties = new Properties()
            localProperties.load(new FileInputStream("$project.rootDir/local.properties"))
            if (localProperties.containsKey(key)) {
                project.setProperty(key, localProperties.getProperty(key))
            }
        }
    }
    

    personally I wrote this script and use it in my project. But it would be nice if this is baked into BuildKonfig

    enhancement 
    opened by yshrsmz 0
  • Support for nullable

    Support for nullable

    Hi, it would be great to add nullable support. Any thoughts about this? If help is needed I could try.

    contribution welcome enhancement 
    opened by MikolajKakol 9
  • convenience methods like buildConfigStringField

    convenience methods like buildConfigStringField

    contribution welcome enhancement 
    opened by yshrsmz 3
  • more tests

    more tests

    opened by yshrsmz 0
  • CI

    CI

    • [x] run tests
    • [x] cache
    • [ ] ktlint
    • [ ] releasing
    opened by yshrsmz 2
Releases(v0.8.0)
Owner
Yasuhiro SHIMIZU
Android/Web developer
Yasuhiro SHIMIZU
BuildConfig for Kotlin Multiplatform Project

BuildKonfig BuildConfig for Kotlin Multiplatform Project. It currently supports embedding values from gradle file. Table Of Contents Motivation Usage

Yasuhiro SHIMIZU 187 Jul 28, 2021
Dependency Injection library for Kotlin Multiplatform, support iOS and Android

Multiplatform-DI library for Kotlin Multiplatform Lightweight dependency injection framework for Kotlin Multiplatform application Dependency injection

Anna Zharkova 12 Jul 27, 2021
Kotlin Multiplatform Mobile App Template

KMMT : Kotlin Multiplatform Mobile Template Kotlin Multiplatform Mobile Development Simplified KMMT is a KMM based project template designed to simpli

Jitty Andiyan 117 Jul 19, 2021
DI can be simple. Forget about modules and components. Just use it!

PopKorn - Kotlin Multiplatform DI PopKorn is a simple, powerful and lightweight Kotlin Multiplatform Dependency Injector. It doesn't need any modules

Pau Corbella 109 May 22, 2021
Generic AST parsing library for kotlin multiplatform

kotlinx.ast kotlinx.ast is a generic AST (Abstract Syntax Tree) parsing library, Kotlin is currently the only supported language. The library is desig

null 140 Jul 27, 2021
KaMP Kit by Touchlab is a collection of code and tools designed to get your mobile team started quickly with Kotlin Multiplatform.

KaMP Kit Welcome to the KaMP Kit! About Goal The goal of the KaMP Kit is to facilitate your evaluation of Kotlin Multiplatform (aka KMP). It is a coll

Touchlab 1.2k Aug 2, 2021
Kotlin Multiplatform project that gets network data from Food2Fork.ca

Food2Fork Recipe App This is the codebase for a Kotlin Multiplatform Mobile course. [Watch the course](https://codingwithmitch.com/courses/kotlin-mult

Mitch Tabian 181 Jul 24, 2021
A Bluetooth kotlin multiplatform "Cross-Platform" library for iOS and Android

Blue-Falcon A Bluetooth "Cross Platform" Kotlin Multiplatform library for iOS, Android, MacOS, Raspberry Pi and Javascript. Bluetooth in general has t

Andrew Reed 146 Jul 24, 2021
A collection of hand-crafted extensions for your Kotlin projects.

Splitties Splitties is a collection of small Kotlin multiplatform libraries (with Android as first target). These libraries are intended to reduce the

Louis CAD 1.8k Aug 4, 2021
The most essential libraries for Kotlin Multiplatform development

Essenty The most essential libraries for Kotlin Multiplatform development. Supported targets: android jvm js (IR and LEGACY) iosArm64, iosX64 watchosA

Arkadii Ivanov 13 Aug 2, 2021
:bouquet: An easy way to persist and run code block only as many times as necessary on Android.

Only ?? An easy way to persist and run code block only as many times as necessary on Android. Download Gradle Add below codes to your root build.gradl

Jaewoong Eum 471 Jul 21, 2021
:bouquet: An easy way to persist and run code block only as many times as necessary on Android.

Only ?? An easy way to persist and run code block only as many times as necessary on Android. Download Gradle Add below codes to your root build.gradl

Jaewoong Eum 468 Apr 14, 2021
An example for who are all going to start learning Kotlin programming language to develop Android application.

Kotlin Example Here is an example for who are all going to start learning Kotlin programming language to develop Android application. First check this

Prabhakar Thota 54 May 31, 2021
Design Patterns implemented in Kotlin

Design Patterns In Kotlin ⚠️ New article about testing: Unit Testing with Mockito 2 Project maintained by @dbacinski (Dariusz Baciński) Inspired by De

Dariusz Baciński 4.8k Jul 28, 2021