Support the -Xexport-library option in the K/N Gradle plugin

This commit is contained in:
Ilya Matveev
2018-12-17 19:24:47 +07:00
committed by Ilya Matveev
parent b7d5452c1d
commit 1ef76cf289
15 changed files with 142 additions and 22 deletions
@@ -107,7 +107,12 @@ interface KotlinNativeLibrary : KotlinNativeBinary,
/** /**
* Represents an Objective C framework compiled from Kotlin/Native sources. * 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. * A shared library compiled from Kotlin/Native sources.
@@ -20,6 +20,7 @@ import groovy.lang.Closure
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.provider.Provider import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty import org.gradle.api.provider.SetProperty
import org.gradle.api.publish.maven.MavenPom 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: CInterop.() -> Unit)
fun cinterop(name: String, action: Closure<Unit>) fun cinterop(name: String, action: Closure<Unit>)
fun cinterop(name: String, action: Action<CInterop>) fun cinterop(name: String, action: Action<CInterop>)
fun export(notation: Any)
fun export(notation: Any, configure: Closure<*>)
fun export(notation: Any, configure: Action<in Dependency>)
var transitiveExport: Boolean
} }
interface TargetSettings { interface TargetSettings {
@@ -64,7 +64,7 @@ abstract class AbstractKotlinNativeBinary(
val projectLayout: ProjectLayout, val projectLayout: ProjectLayout,
override val kind: CompilerOutputKind, override val kind: CompilerOutputKind,
objects: ObjectFactory, objects: ObjectFactory,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
configurations: ConfigurationContainer, configurations: ConfigurationContainer,
val fileOperations: FileOperations val fileOperations: FileOperations
) : KotlinNativeBinary, ) : KotlinNativeBinary,
@@ -101,7 +101,7 @@ abstract class AbstractKotlinNativeBinary(
DefaultComponentDependencies::class.java, DefaultComponentDependencies::class.java,
name + "Implementation" name + "Implementation"
).apply { ).apply {
implementationDependencies.extendsFrom(componentImplementation) implementationDependencies.extendsFrom(componentDependencies.implementationDependencies)
} }
override fun getDependencies(): ComponentDependencies = dependencies override fun getDependencies(): ComponentDependencies = dependencies
@@ -80,7 +80,9 @@ abstract class AbstractKotlinNativeComponent @Inject constructor(
private val dependencies: KotlinNativeDependenciesImpl = objectFactory.newInstance( private val dependencies: KotlinNativeDependenciesImpl = objectFactory.newInstance(
KotlinNativeDependenciesImpl::class.java, KotlinNativeDependenciesImpl::class.java,
project, project,
names.withSuffix("implementation")) names.withSuffix("implementation"),
names.withSuffix("export")
)
internal val poms = mutableListOf<Action<MavenPom>>() internal val poms = mutableListOf<Action<MavenPom>>()
override fun getDependencies() = dependencies override fun getDependencies() = dependencies
@@ -20,6 +20,7 @@ import groovy.lang.Closure
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.Dependency
import org.gradle.language.internal.DefaultComponentDependencies import org.gradle.language.internal.DefaultComponentDependencies
import org.gradle.util.ConfigureUtil import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.experimental.CInterop import org.jetbrains.kotlin.gradle.plugin.experimental.CInterop
@@ -30,10 +31,40 @@ import javax.inject.Inject
open class KotlinNativeDependenciesImpl @Inject constructor( open class KotlinNativeDependenciesImpl @Inject constructor(
private val project: Project, private val project: Project,
configurations: ConfigurationContainer, configurations: ConfigurationContainer,
implementationName: String implementationName: String,
exportName: String
) : DefaultComponentDependencies(configurations, implementationName), ) : DefaultComponentDependencies(configurations, implementationName),
KotlinNativeDependencies { 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<in Dependency>) {
val dependency = dependencyHandler.create(notation)
configure.execute(dependency)
exportDependencies.dependencies.add(dependency)
}
override val cinterops = project.container(CInteropImpl::class.java) { name -> override val cinterops = project.container(CInteropImpl::class.java) { name ->
CInteropImpl(project, name).apply { CInteropImpl(project, name).apply {
dependencies.implementationDependencies.extendsFrom( dependencies.implementationDependencies.extendsFrom(
@@ -29,7 +29,7 @@ import javax.inject.Inject
open class KotlinNativeDynamicImpl @Inject constructor( open class KotlinNativeDynamicImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
component: KotlinNativeMainComponent, component: KotlinNativeMainComponent,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
@@ -44,7 +44,7 @@ open class KotlinNativeDynamicImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.DYNAMIC, CompilerOutputKind.DYNAMIC,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations fileOperations
), KotlinNativeDynamic { ), KotlinNativeDynamic {
@@ -38,7 +38,7 @@ import javax.inject.Inject
open class KotlinNativeExecutableImpl @Inject constructor( open class KotlinNativeExecutableImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
component: KotlinNativeMainComponent, component: KotlinNativeMainComponent,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
objects: ObjectFactory, objects: ObjectFactory,
@@ -52,7 +52,7 @@ open class KotlinNativeExecutableImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.PROGRAM, CompilerOutputKind.PROGRAM,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations), fileOperations),
KotlinNativeExecutable, KotlinNativeExecutable,
@@ -16,13 +16,18 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.file.ProjectLayout import org.gradle.api.file.ProjectLayout
import org.gradle.api.internal.file.FileOperations import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider 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.experimental.KotlinNativeFramework
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import javax.inject.Inject import javax.inject.Inject
@@ -30,7 +35,7 @@ import javax.inject.Inject
open class KotlinNativeFrameworkImpl @Inject constructor( open class KotlinNativeFrameworkImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
component: KotlinNativeMainComponent, component: KotlinNativeMainComponent,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
@@ -45,9 +50,23 @@ open class KotlinNativeFrameworkImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.FRAMEWORK, CompilerOutputKind.FRAMEWORK,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations fileOperations
), KotlinNativeFramework { ), KotlinNativeFramework {
override val outputRootName: String = "lib" 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)
}
} }
@@ -38,7 +38,7 @@ import javax.inject.Inject
open class KotlinNativeLibraryImpl @Inject constructor( open class KotlinNativeLibraryImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
component: KotlinNativeMainComponent, component: KotlinNativeMainComponent,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
@@ -52,7 +52,7 @@ open class KotlinNativeLibraryImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.LIBRARY, CompilerOutputKind.LIBRARY,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations), fileOperations),
KotlinNativeLibrary, KotlinNativeLibrary,
@@ -75,7 +75,7 @@ open class KotlinNativeMainComponent @Inject constructor(
type, type,
"$name${identity.name.capitalize()}", "$name${identity.name.capitalize()}",
baseName, baseName,
getImplementationDependencies(), dependencies,
this, this,
identity identity
).apply { ).apply {
@@ -30,7 +30,7 @@ import javax.inject.Inject
open class KotlinNativeStaticImpl @Inject constructor( open class KotlinNativeStaticImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
component: KotlinNativeMainComponent, component: KotlinNativeMainComponent,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
projectLayout: ProjectLayout, projectLayout: ProjectLayout,
@@ -45,7 +45,7 @@ open class KotlinNativeStaticImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.STATIC, CompilerOutputKind.STATIC,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations fileOperations
), KotlinNativeStatic { ), KotlinNativeStatic {
@@ -34,7 +34,7 @@ import javax.inject.Inject
open class KotlinNativeTestExecutableImpl @Inject constructor( open class KotlinNativeTestExecutableImpl @Inject constructor(
name: String, name: String,
baseName: Provider<String>, baseName: Provider<String>,
componentImplementation: Configuration, componentDependencies: KotlinNativeDependenciesImpl,
val testComponent: KotlinNativeTestSuite, val testComponent: KotlinNativeTestSuite,
val mainSources: KotlinNativeSourceSet, val mainSources: KotlinNativeSourceSet,
identity: KotlinNativeVariantIdentity, identity: KotlinNativeVariantIdentity,
@@ -49,7 +49,7 @@ open class KotlinNativeTestExecutableImpl @Inject constructor(
projectLayout, projectLayout,
CompilerOutputKind.PROGRAM, CompilerOutputKind.PROGRAM,
objects, objects,
componentImplementation, componentDependencies,
configurations, configurations,
fileOperations), fileOperations),
KotlinNativeTestExecutable { KotlinNativeTestExecutable {
@@ -56,7 +56,7 @@ open class KotlinNativeTestSuite @Inject constructor(
KotlinNativeTestExecutableImpl::class.java, KotlinNativeTestExecutableImpl::class.java,
"$name${identity.name.capitalize()}", "$name${identity.name.capitalize()}",
getBaseName(), getBaseName(),
getImplementationDependencies(), dependencies,
this, this,
testedComponent.sources, testedComponent.sources,
identity identity
@@ -26,6 +26,7 @@ import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.compile.AbstractCompile 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.experimental.internal.AbstractKotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.konan.* import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions import org.jetbrains.kotlin.gradle.tasks.CompilerPluginOptions
@@ -54,6 +55,14 @@ open class KotlinNativeCompile @Inject constructor(internal val binary: Abstract
val libraries: Configuration val libraries: Configuration
@InputFiles get() = binary.klibs @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 getClasspath(): FileCollection = libraries
override fun setClasspath(configuration: FileCollection?) { override fun setClasspath(configuration: FileCollection?) {
@@ -158,12 +167,20 @@ open class KotlinNativeCompile @Inject constructor(internal val binary: Abstract
addAll(additionalCompilerOptions) addAll(additionalCompilerOptions)
libraries.files.filter { fun Set<File>.filterKlibs() = filter {
it.extension == "klib" it.extension == "klib"
}.forEach { }
libraries.files.filterKlibs().forEach {
addArg("-l", it.absolutePath) 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) addListArg("-linker-options", linkerOpts)
addAll(sources.files.map { it.absolutePath }) addAll(sources.files.map { it.absolutePath })
@@ -1131,4 +1131,43 @@ class ExperimentalPluginTests {
val result = project.createRunner().withArguments("build").build() val result = project.createRunner().withArguments("build").build()
assertTrue(result.output.contains("A.a")) 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<String>) { 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 "))
}
} }