Kotlin-native: Can't export dependencies in mingw sharedLib builds

Created on 12 Nov 2019  路  6Comments  路  Source: JetBrains/kotlin-native

If your project has

dependencies {
    api "some-dependency"
}

that you would like for native consumers of your library to be able to use, for iOS framework builds you can export the symbols from that dependency by using

framework {
    export "some-dependency"
}

However, mingw sharedLib targets have no equivalent option, so the generated C header and DLL do not contain those types (except opaque pointers for those mentioned in your public API).

A workaround is to remove the dependency and use submodules, etc. to pull the code in and build from source instead (e.g. kotlin.srcDirs += 'some-dependency/src/commonMain/kotlin'), but this isn't always desirable. Ideally mingw could also provide an export option.

Most helpful comment

The compiler already supports such an exporting and we will provide a corresponding DSL in the MPP plugin.

As a workaround you can implement such a logic manually in a buildscript. I've checked this snippet on a MacOS machine but it will work for MinGW too, you only need to change the target:

fun copyAttributes(from: AttributeContainer, to: AttributeContainer) {
    fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
        to.attribute(key, from.getAttribute(key)!!)
    }

    from.keySet().forEach { key -> copyAttribute(key, from, to) }
}

kotlin.macosX64("mac") {
    val attributes = compilations["main"].compileDependencyConfigurationName.let {
        configurations[it].attributes
    }

    val exportDeps by configurations.creating {
        // Copy attributes from the regular dependency configuration
        // (aka compileClasspath) to enable variant-aware dependency resolution.
        copyAttributes(attributes, this.attributes)
        isCanBeConsumed = false
        isCanBeResolved = true
        // Make export non-transitive for more granular exporting.
        isTransitive = false
    }

    // Add dependencies to be exported.
    dependencies.add("exportDeps", project("dep1"))

    binaries.sharedLib {
        linkTask.apply {
            // Take exported dependencies into account during UP-TO-DATE checks.
            inputs.files(exportDeps)
            doFirst {
                // Modify compiler args in a doFirst block to avoid
                // resolving the export configuration at the configuration stage.
                freeCompilerArgs += exportDeps.resolve().map {
                    "-Xexport-library=${it.absolutePath}"
                }
            }
        }
    }
}

// All exported dependencies must be added to the API configuration.
kotlin.sourceSets["commonMain"].dependencies {
    api(project("dep1"))
}

All 6 comments

The compiler already supports such an exporting and we will provide a corresponding DSL in the MPP plugin.

As a workaround you can implement such a logic manually in a buildscript. I've checked this snippet on a MacOS machine but it will work for MinGW too, you only need to change the target:

fun copyAttributes(from: AttributeContainer, to: AttributeContainer) {
    fun <T> copyAttribute(key: Attribute<T>, from: AttributeContainer, to: AttributeContainer) {
        to.attribute(key, from.getAttribute(key)!!)
    }

    from.keySet().forEach { key -> copyAttribute(key, from, to) }
}

kotlin.macosX64("mac") {
    val attributes = compilations["main"].compileDependencyConfigurationName.let {
        configurations[it].attributes
    }

    val exportDeps by configurations.creating {
        // Copy attributes from the regular dependency configuration
        // (aka compileClasspath) to enable variant-aware dependency resolution.
        copyAttributes(attributes, this.attributes)
        isCanBeConsumed = false
        isCanBeResolved = true
        // Make export non-transitive for more granular exporting.
        isTransitive = false
    }

    // Add dependencies to be exported.
    dependencies.add("exportDeps", project("dep1"))

    binaries.sharedLib {
        linkTask.apply {
            // Take exported dependencies into account during UP-TO-DATE checks.
            inputs.files(exportDeps)
            doFirst {
                // Modify compiler args in a doFirst block to avoid
                // resolving the export configuration at the configuration stage.
                freeCompilerArgs += exportDeps.resolve().map {
                    "-Xexport-library=${it.absolutePath}"
                }
            }
        }
    }
}

// All exported dependencies must be added to the API configuration.
kotlin.sourceSets["commonMain"].dependencies {
    api(project("dep1"))
}

Looks like this workaround works. Thanks @ilmat192! Would you like me to file a KT issue to track the issue for the Kotlin repo?

Not mandatory, we shall have it fixed.

Sounds good thanks!

Filed an issue to mirror this one: https://youtrack.jetbrains.com/issue/KT-35352

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nvlizlo picture nvlizlo  路  4Comments

brettwillis picture brettwillis  路  4Comments

antanas-arvasevicius picture antanas-arvasevicius  路  4Comments

9468305 picture 9468305  路  3Comments

barsan-md picture barsan-md  路  4Comments