diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt index 6fb28f7a8be..1a679e829f5 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt @@ -107,7 +107,12 @@ interface KotlinNativeLibrary : KotlinNativeBinary, /** * Represents an Objective C framework compiled from Kotlin/Native sources. */ -interface KotlinNativeFramework : KotlinNativeBinary +interface KotlinNativeFramework : KotlinNativeBinary { + /** + * Klibs exported in the framework. + */ + val export: FileCollection +} /** * A shared library compiled from Kotlin/Native sources. diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt index c1bd19922a4..45d553f6077 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt @@ -20,6 +20,7 @@ import groovy.lang.Closure import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty import org.gradle.api.publish.maven.MavenPom @@ -37,6 +38,12 @@ interface KotlinNativeDependencies: ComponentDependencies { fun cinterop(name: String, action: CInterop.() -> Unit) fun cinterop(name: String, action: Closure) fun cinterop(name: String, action: Action) + + fun export(notation: Any) + fun export(notation: Any, configure: Closure<*>) + fun export(notation: Any, configure: Action) + + var transitiveExport: Boolean } interface TargetSettings { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt index e508d624516..0169312b662 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt @@ -64,7 +64,7 @@ abstract class AbstractKotlinNativeBinary( val projectLayout: ProjectLayout, override val kind: CompilerOutputKind, objects: ObjectFactory, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, configurations: ConfigurationContainer, val fileOperations: FileOperations ) : KotlinNativeBinary, @@ -101,7 +101,7 @@ abstract class AbstractKotlinNativeBinary( DefaultComponentDependencies::class.java, name + "Implementation" ).apply { - implementationDependencies.extendsFrom(componentImplementation) + implementationDependencies.extendsFrom(componentDependencies.implementationDependencies) } override fun getDependencies(): ComponentDependencies = dependencies diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt index 08d83250adf..07e32a08655 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt @@ -80,7 +80,9 @@ abstract class AbstractKotlinNativeComponent @Inject constructor( private val dependencies: KotlinNativeDependenciesImpl = objectFactory.newInstance( KotlinNativeDependenciesImpl::class.java, project, - names.withSuffix("implementation")) + names.withSuffix("implementation"), + names.withSuffix("export") + ) internal val poms = mutableListOf>() override fun getDependencies() = dependencies diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDependenciesImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDependenciesImpl.kt index 6263209f3c5..3f4464f13fc 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDependenciesImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDependenciesImpl.kt @@ -20,6 +20,7 @@ import groovy.lang.Closure import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.Dependency import org.gradle.language.internal.DefaultComponentDependencies import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.plugin.experimental.CInterop @@ -30,10 +31,40 @@ import javax.inject.Inject open class KotlinNativeDependenciesImpl @Inject constructor( private val project: Project, configurations: ConfigurationContainer, - implementationName: String + implementationName: String, + exportName: String ) : DefaultComponentDependencies(configurations, implementationName), KotlinNativeDependencies { + internal val exportDependencies = configurations.create(exportName).apply { + isCanBeConsumed = false + isCanBeResolved = false + isTransitive = false + implementationDependencies.extendsFrom(this) + } + + override var transitiveExport: Boolean + get() = exportDependencies.isTransitive + set(value) { + exportDependencies.isTransitive = value + } + + override fun export(notation: Any) { + exportDependencies.dependencies.add(dependencyHandler.create(notation)) + } + + override fun export(notation: Any, configure: Closure<*>) { + val dependency = dependencyHandler.create(notation) + ConfigureUtil.configure(configure, dependency) + exportDependencies.dependencies.add(dependency) + } + + override fun export(notation: Any, configure: Action) { + val dependency = dependencyHandler.create(notation) + configure.execute(dependency) + exportDependencies.dependencies.add(dependency) + } + override val cinterops = project.container(CInteropImpl::class.java) { name -> CInteropImpl(project, name).apply { dependencies.implementationDependencies.extendsFrom( diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt index 44b160f6c69..702b0dbbaa5 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt @@ -29,7 +29,7 @@ import javax.inject.Inject open class KotlinNativeDynamicImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, component: KotlinNativeMainComponent, identity: KotlinNativeVariantIdentity, projectLayout: ProjectLayout, @@ -44,7 +44,7 @@ open class KotlinNativeDynamicImpl @Inject constructor( projectLayout, CompilerOutputKind.DYNAMIC, objects, - componentImplementation, + componentDependencies, configurations, fileOperations ), KotlinNativeDynamic { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt index 9799ae123a7..73bae613141 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt @@ -38,7 +38,7 @@ import javax.inject.Inject open class KotlinNativeExecutableImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, component: KotlinNativeMainComponent, identity: KotlinNativeVariantIdentity, objects: ObjectFactory, @@ -52,7 +52,7 @@ open class KotlinNativeExecutableImpl @Inject constructor( projectLayout, CompilerOutputKind.PROGRAM, objects, - componentImplementation, + componentDependencies, configurations, fileOperations), KotlinNativeExecutable, diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeFrameworkImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeFrameworkImpl.kt index 79c7f139a8e..dc41a659e8a 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeFrameworkImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeFrameworkImpl.kt @@ -16,13 +16,18 @@ package org.jetbrains.kotlin.gradle.plugin.experimental.internal -import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.attributes.Usage import org.gradle.api.file.ProjectLayout import org.gradle.api.internal.file.FileOperations import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider +import org.gradle.language.cpp.CppBinary +import org.gradle.nativeplatform.OperatingSystemFamily +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeFramework +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages import org.jetbrains.kotlin.konan.target.CompilerOutputKind import javax.inject.Inject @@ -30,7 +35,7 @@ import javax.inject.Inject open class KotlinNativeFrameworkImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, component: KotlinNativeMainComponent, identity: KotlinNativeVariantIdentity, projectLayout: ProjectLayout, @@ -45,9 +50,23 @@ open class KotlinNativeFrameworkImpl @Inject constructor( projectLayout, CompilerOutputKind.FRAMEWORK, objects, - componentImplementation, + componentDependencies, configurations, fileOperations ), KotlinNativeFramework { override val outputRootName: String = "lib" + + // A configuration containing exported klibs. + override val export = configurations.create(names.withPrefix("export")).apply { + isCanBeConsumed = false + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinUsages.KOTLIN_API)) + attributes.attribute(CppBinary.DEBUGGABLE_ATTRIBUTE, debuggable) + attributes.attribute(CppBinary.OPTIMIZED_ATTRIBUTE, optimized) + attributes.attribute(KotlinPlatformType.attribute, KotlinPlatformType.native) + attributes.attribute(KotlinNativeBinary.KONAN_TARGET_ATTRIBUTE, konanTarget.name) + attributes.attribute(KotlinNativeBinary.OLD_KONAN_TARGET_ATTRIBUTE, konanTarget.name) + attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, konanTarget.getGradleOSFamily(objects)) + extendsFrom(componentDependencies.exportDependencies) + getImplementationDependencies().extendsFrom(this) + } } \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeLibraryImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeLibraryImpl.kt index 560158d17d4..1862a05a207 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeLibraryImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeLibraryImpl.kt @@ -38,7 +38,7 @@ import javax.inject.Inject open class KotlinNativeLibraryImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, component: KotlinNativeMainComponent, identity: KotlinNativeVariantIdentity, projectLayout: ProjectLayout, @@ -52,7 +52,7 @@ open class KotlinNativeLibraryImpl @Inject constructor( projectLayout, CompilerOutputKind.LIBRARY, objects, - componentImplementation, + componentDependencies, configurations, fileOperations), KotlinNativeLibrary, diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt index 5c02a146df8..63b76d42524 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt @@ -75,7 +75,7 @@ open class KotlinNativeMainComponent @Inject constructor( type, "$name${identity.name.capitalize()}", baseName, - getImplementationDependencies(), + dependencies, this, identity ).apply { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt index 29e61a2b286..1f592c091ec 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt @@ -30,7 +30,7 @@ import javax.inject.Inject open class KotlinNativeStaticImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, component: KotlinNativeMainComponent, identity: KotlinNativeVariantIdentity, projectLayout: ProjectLayout, @@ -45,7 +45,7 @@ open class KotlinNativeStaticImpl @Inject constructor( projectLayout, CompilerOutputKind.STATIC, objects, - componentImplementation, + componentDependencies, configurations, fileOperations ), KotlinNativeStatic { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt index cd5f9156849..0da9e65c453 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt @@ -34,7 +34,7 @@ import javax.inject.Inject open class KotlinNativeTestExecutableImpl @Inject constructor( name: String, baseName: Provider, - componentImplementation: Configuration, + componentDependencies: KotlinNativeDependenciesImpl, val testComponent: KotlinNativeTestSuite, val mainSources: KotlinNativeSourceSet, identity: KotlinNativeVariantIdentity, @@ -49,7 +49,7 @@ open class KotlinNativeTestExecutableImpl @Inject constructor( projectLayout, CompilerOutputKind.PROGRAM, objects, - componentImplementation, + componentDependencies, configurations, fileOperations), KotlinNativeTestExecutable { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt index ff5abcbaa74..52947be7c7e 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt @@ -56,7 +56,7 @@ open class KotlinNativeTestSuite @Inject constructor( KotlinNativeTestExecutableImpl::class.java, "$name${identity.name.capitalize()}", getBaseName(), - getImplementationDependencies(), + dependencies, this, testedComponent.sources, identity diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt index d9384b8bacd..884c978438b 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt @@ -26,6 +26,7 @@ import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Optional import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.compile.AbstractCompile +import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeFramework import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeBinary import org.jetbrains.kotlin.gradle.plugin.konan.* import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions @@ -54,6 +55,14 @@ open class KotlinNativeCompile @Inject constructor(internal val binary: Abstract val libraries: Configuration @InputFiles get() = binary.klibs + @get:InputFiles + val exportLibraries: FileCollection + get() = if (binary is KotlinNativeFramework) { + binary.export + } else { + project.files() + } + override fun getClasspath(): FileCollection = libraries override fun setClasspath(configuration: FileCollection?) { @@ -158,12 +167,20 @@ open class KotlinNativeCompile @Inject constructor(internal val binary: Abstract addAll(additionalCompilerOptions) - libraries.files.filter { + fun Set.filterKlibs() = filter { it.extension == "klib" - }.forEach { + } + + libraries.files.filterKlibs().forEach { addArg("-l", it.absolutePath) } + // There is no need to check that all exported dependencies are passed with -l option + // because export configuration extends the libraries one. + exportLibraries.files.filterKlibs().forEach { + add("-Xexport-library=${it.absolutePath}") + } + addListArg("-linker-options", linkerOpts) addAll(sources.files.map { it.absolutePath }) diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt index 57a2319637a..3252e3056eb 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt @@ -1131,4 +1131,43 @@ class ExperimentalPluginTests { val result = project.createRunner().withArguments("build").build() assertTrue(result.output.contains("A.a")) } + + @Test + fun `Plugin should support symbol exporting for frameworks`() { + assumeTrue(HostManager.hostIsMac) + val libraryDir = tmpFolder.newFolder("library") + val libraryProject = KonanProject.createEmpty(libraryDir).apply { + buildFile.writeText(""" + plugins { id 'kotlin-native' } + """.trimIndent()) + generateSrcFile("library.kt", "fun foo() = 42") + } + + val project = KonanProject.createEmpty(projectDirectory).apply { + settingsFile.writeText(""" + include ':library' + rootProject.name = 'test' + """.trimIndent()) + buildFile.writeText(""" + plugins { id 'kotlin-native' } + + dependencies { + export project(':library') + } + + sourceSets.main.component { + outputKinds = [ FRAMEWORK ] + } + """.trimIndent()) + generateSrcFile("main.kt", "fun main(args: Array) { println(foo()) }") + } + + val compileDebugResult = project.createRunner().withArguments("compileDebugKotlinNative").build() + assertEquals(TaskOutcome.SUCCESS, compileDebugResult.task(":compileDebugKotlinNative")?.outcome) + assertEquals(TaskOutcome.SUCCESS, compileDebugResult.task(":library:compileDebugKotlinNative")?.outcome) + assertTrue(projectDirectory.resolve("build/lib/main/debug/test.framework").exists()) + val header = projectDirectory.resolve("build/lib/main/debug/test.framework/Headers/test.h") + assertTrue(header.exists()) + assertTrue(header.readText().contains("+ (int32_t)foo ")) + } }