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.
*/
interface KotlinNativeFramework : KotlinNativeBinary
interface KotlinNativeFramework : KotlinNativeBinary {
/**
* Klibs exported in the framework.
*/
val export: FileCollection
}
/**
* 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.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<Unit>)
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 {
@@ -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
@@ -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<Action<MavenPom>>()
override fun getDependencies() = dependencies
@@ -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<in Dependency>) {
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(
@@ -29,7 +29,7 @@ import javax.inject.Inject
open class KotlinNativeDynamicImpl @Inject constructor(
name: String,
baseName: Provider<String>,
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 {
@@ -38,7 +38,7 @@ import javax.inject.Inject
open class KotlinNativeExecutableImpl @Inject constructor(
name: String,
baseName: Provider<String>,
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,
@@ -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<String>,
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)
}
}
@@ -38,7 +38,7 @@ import javax.inject.Inject
open class KotlinNativeLibraryImpl @Inject constructor(
name: String,
baseName: Provider<String>,
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,
@@ -75,7 +75,7 @@ open class KotlinNativeMainComponent @Inject constructor(
type,
"$name${identity.name.capitalize()}",
baseName,
getImplementationDependencies(),
dependencies,
this,
identity
).apply {
@@ -30,7 +30,7 @@ import javax.inject.Inject
open class KotlinNativeStaticImpl @Inject constructor(
name: String,
baseName: Provider<String>,
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 {
@@ -34,7 +34,7 @@ import javax.inject.Inject
open class KotlinNativeTestExecutableImpl @Inject constructor(
name: String,
baseName: Provider<String>,
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 {
@@ -56,7 +56,7 @@ open class KotlinNativeTestSuite @Inject constructor(
KotlinNativeTestExecutableImpl::class.java,
"$name${identity.name.capitalize()}",
getBaseName(),
getImplementationDependencies(),
dependencies,
this,
testedComponent.sources,
identity
@@ -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<File>.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 })
@@ -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<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 "))
}
}