Support cinterop in the native part of kotlin-multiplatform

This commit is contained in:
Ilya Matveev
2018-09-07 19:55:32 +07:00
committed by Ilya Matveev
parent f7949a2131
commit 3269c0e51b
19 changed files with 630 additions and 62 deletions
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.Named
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSetOutput
interface CInteropSettings: Named {
interface IncludeDirectories {
fun allHeaders(vararg includeDirs: Any)
fun allHeaders(includeDirs: Collection<Any>)
fun headerFilterOnly(vararg includeDirs: Any)
fun headerFilterOnly(includeDirs: Collection<Any>)
}
// TODO: Provide an interface for native compilations.
val compilation: KotlinCompilation
// DSL.
fun defFile(file: Any)
fun packageName(value: String)
fun header(file: Any) = headers(file)
fun headers(vararg files: Any)
fun headers(files: FileCollection)
fun includeDirs(vararg values: Any)
fun includeDirs(closure: Closure<Unit>)
fun includeDirs(action: Action<IncludeDirectories>)
fun includeDirs(configure: IncludeDirectories.() -> Unit)
fun compilerOpts(vararg values: String)
fun compilerOpts(values: List<String>)
fun linkerOpts(vararg values: String)
fun linkerOpts(values: List<String>)
fun extraOpts(vararg values: Any)
fun extraOpts(values: List<Any>)
}
@@ -551,4 +551,29 @@ class NewMultiplatformIT : BaseGradleIT() {
assertTasksExecuted(hostTestTask)
}
}
}
@Test
fun testCinterop() = with(Project("new-mpp-native-cinterop", gradleVersion)) {
val host = nativeHostTargetName
build(":projectLibrary:build") {
assertSuccessful()
assertTasksExecuted(":projectLibrary:cinteropStdio${host.capitalize()}")
assertTrue(output.contains("Project test"), "No test output found")
assertFileExists("projectLibrary/build/classes/kotlin/$host/main/projectLibrary-cinterop-stdio.klib")
}
build(":publishedLibrary:build", ":publishedLibrary:publish") {
assertSuccessful()
assertTasksExecuted(":publishedLibrary:cinteropStdio${host.capitalize()}")
assertTrue(output.contains("Published test"), "No test output found")
assertFileExists("publishedLibrary/build/classes/kotlin/$host/main/publishedLibrary-cinterop-stdio.klib")
assertFileExists("repo/org/example/publishedLibrary-$host/1.0/publishedLibrary-$host-1.0-cinterop-stdio.klib")
}
build(":build") {
assertSuccessful()
assertTrue(output.contains("Dependent: Project print"), "No test output found")
assertTrue(output.contains("Dependent: Published print"), "No test output found")
}
}
}
@@ -0,0 +1,49 @@
buildscript {
repositories {
mavenLocal()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin-multiplatform'
repositories {
mavenLocal()
jcenter()
maven { url "http://dl.bintray.com/kotlin/kotlinx.html/" }
maven { url 'repo' }
}
kotlin {
sourceSets {
allNative {
dependencies {
implementation project(':projectLibrary')
implementation 'org.example:publishedLibrary:1.0'
}
}
nativeTest
macos64Main { dependsOn sourceSets.allNative }
linux64Main { dependsOn sourceSets.allNative }
mingw64Main { dependsOn sourceSets.allNative }
macos64Test { dependsOn sourceSets.nativeTest }
linux64Test { dependsOn sourceSets.nativeTest }
mingw64Test { dependsOn sourceSets.nativeTest }
}
targets {
fromPreset(presets.macosX64, 'macos64')
fromPreset(presets.linuxX64, 'linux64')
fromPreset(presets.mingwX64, 'mingw64')
configure([macos64, linux64, mingw64]) {
compilations.main.outputKinds ['EXECUTABLE']
}
}
}
@@ -0,0 +1,37 @@
apply plugin: 'kotlin-multiplatform'
repositories {
mavenLocal()
jcenter()
maven { url "http://dl.bintray.com/kotlin/kotlinx.html/" }
}
kotlin {
sourceSets {
allNative
nativeTest
macos64Main { dependsOn sourceSets.allNative }
linux64Main { dependsOn sourceSets.allNative }
mingw64Main { dependsOn sourceSets.allNative }
macos64Test { dependsOn sourceSets.nativeTest }
linux64Test { dependsOn sourceSets.nativeTest }
mingw64Test { dependsOn sourceSets.nativeTest }
}
targets {
fromPreset(presets.macosX64, 'macos64')
fromPreset(presets.linuxX64, 'linux64')
fromPreset(presets.mingwX64, 'mingw64')
configure([macos64, linux64, mingw64]) {
compilations.main.cinterops {
stdio {
packageName 'example.cinterop.project.stdio'
extraOpts '-nodefaultlibs'
}
}
}
}
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package example.cinterop.project
import example.cinterop.project.stdio.*
fun projectPrint(str: String) {
printf(str + '\n')
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package example.cinterop.project
import kotlin.test.*
@Test
fun projectTest() {
projectPrint("Project test")
}
@@ -0,0 +1,51 @@
apply plugin: 'kotlin-multiplatform'
apply plugin: 'maven-publish'
group = 'org.example'
version = '1.0'
repositories {
mavenLocal()
jcenter()
maven { url "http://dl.bintray.com/kotlin/kotlinx.html/" }
}
kotlin {
sourceSets {
allNative
nativeTest
macos64Main { dependsOn sourceSets.allNative }
linux64Main { dependsOn sourceSets.allNative }
mingw64Main { dependsOn sourceSets.allNative }
macos64Test { dependsOn sourceSets.nativeTest }
linux64Test { dependsOn sourceSets.nativeTest }
mingw64Test { dependsOn sourceSets.nativeTest }
}
targets {
fromPreset(presets.macosX64, 'macos64')
fromPreset(presets.linuxX64, 'linux64')
fromPreset(presets.mingwX64, 'mingw64')
configure([macos64, linux64, mingw64]) {
compilations.main.cinterops {
stdio {
packageName 'example.cinterop.published.stdio'
extraOpts '-nodefaultlibs'
}
}
}
}
}
publishing {
repositories {
maven {
url = '../repo'
}
}
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package example.cinterop.published
import example.cinterop.published.stdio.*
fun publishedPrint(str: String) {
printf(str + '\n')
}
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package example.cinterop.published
import kotlin.test.*
@Test
fun publishedTest() {
publishedPrint("Published test")
}
@@ -0,0 +1,6 @@
enableFeaturePreview('GRADLE_METADATA')
rootProject.name = 'native-cinterop'
include 'projectLibrary'
include 'publishedLibrary'
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
import example.cinterop.project.*
import example.cinterop.published.*
fun dependentProject() {
projectPrint("Dependent: Project print")
publishedPrint("Dependent: Published print")
}
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
import kotlin.test.*
@Test
fun dependentTest() {
dependentProject()
}
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.tasks.KonanCompilerDownloadTask
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
@@ -387,9 +388,6 @@ open class KotlinNativeTargetConfigurator(
private val Collection<*>.isDimensionVisible: Boolean
get() = size > 1
private val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
private fun Project.createTestTask(compilation: KotlinNativeCompilation, testExecutableLinkTask: KotlinNativeCompile) {
val compilationSuffix = compilation.name.takeIf { it != KotlinCompilation.TEST_COMPILATION_NAME }.orEmpty()
val taskName = lowerCamelCaseName(compilation.target.targetName, compilationSuffix, testTaskNameSuffix)
@@ -430,7 +428,7 @@ open class KotlinNativeTargetConfigurator(
return buildDir.resolve("bin/$targetSubDirectory${compilation.name}/$buildTypeSubDirectory/$kindSubDirectory")
}
private fun Project.klibOutputDirectory(
private fun Project. klibOutputDirectory(
compilation: KotlinNativeCompilation
): File {
val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty()
@@ -481,7 +479,6 @@ open class KotlinNativeTargetConfigurator(
private fun Project.createBinaryLinkTasks(compilation: KotlinNativeCompilation) = whenEvaluated {
val konanTarget = compilation.target.konanTarget
val buildTypes = compilation.buildTypes
val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) }
val linkAll = project.tasks.maybeCreate(compilation.linkAllTaskName)
@@ -506,6 +503,7 @@ open class KotlinNativeTargetConfigurator(
registerOutputFiles(binaryOutputDirectory(buildType, kind, compilation))
addCompilerPlugins()
dependsOn(compilation.compileKotlinTaskName)
dependsOnCompilerDownloading()
linkAll.dependsOn(this)
}
@@ -523,7 +521,12 @@ open class KotlinNativeTargetConfigurator(
}
}
private fun Project.createKlibPublishableArtifact(compilation: KotlinNativeCompilation, compileTask: KotlinNativeCompile) {
private fun Project.createKlibArtifact(
compilation: KotlinNativeCompilation,
artifactFile: File,
classifier: String?,
producingTask: Task
) {
if (!compilation.target.konanTarget.enabledOnCurrentHost) {
return
}
@@ -533,10 +536,10 @@ open class KotlinNativeTargetConfigurator(
compilation.name,
"klib",
"klib",
null,
classifier,
Date(),
compileTask.outputFile.get(),
compileTask
artifactFile,
producingTask
)
project.extensions.getByType(DefaultArtifactPublicationSet::class.java).addCandidate(klibArtifact)
@@ -546,6 +549,17 @@ open class KotlinNativeTargetConfigurator(
}
}
private fun Project.createRegularKlibArtifact(
compilation: KotlinNativeCompilation,
compileTask: KotlinNativeCompile
) = createKlibArtifact(compilation, compileTask.outputFile.get(), null, compileTask)
private fun Project.createCInteropKlibArtifact(
interop: DefaultCInteropSettings,
interopTask: CInteropProcess
) = createKlibArtifact(interop.compilation, interopTask.outputFile, "cinterop-${interop.name}", interopTask)
private fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilation) {
val compileTask = tasks.create(
compilation.compileKotlinTaskName,
@@ -577,7 +591,29 @@ open class KotlinNativeTargetConfigurator(
dependsOn(compileTask)
dependsOn(compilation.linkAllTaskName)
}
createKlibPublishableArtifact(compilation, compileTask)
createRegularKlibArtifact(compilation, compileTask)
}
}
private fun Project.createCInteropTasks(compilation: KotlinNativeCompilation) {
compilation.cinterops.all { interop ->
val interopTask = tasks.create(interop.interopProcessingTaskName, CInteropProcess::class.java).apply {
settings = interop
destinationDir = provider { klibOutputDirectory(compilation) }
group = INTEROP_GROUP
description = "Generates Kotlin/Native interop library '${interop.name}' " +
"for compilation '${compilation.name}'" +
"of target '${konanTarget.name}'."
enabled = compilation.target.konanTarget.enabledOnCurrentHost
dependsOnCompilerDownloading()
val interopOutput = project.files(outputFileProvider).builtBy(this)
with(compilation) {
interopFiles += interopOutput
output.tryAddClassesDir { interopOutput }
}
}
createCInteropKlibArtifact(interop, interopTask)
}
}
@@ -585,6 +621,7 @@ open class KotlinNativeTargetConfigurator(
tasks.create(target.artifactsTaskName)
target.compilations.all {
createKlibCompilationTask(it)
createCInteropTasks(it)
createBinaryLinkTasks(it)
}
@@ -599,6 +636,10 @@ open class KotlinNativeTargetConfigurator(
object NativeArtifactFormat {
const val KLIB = "org.jetbrains.kotlin.klib"
}
companion object {
const val INTEROP_GROUP = "interop"
}
}
internal fun Project.usageByName(usageName: String): Usage =
@@ -0,0 +1,99 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSetOutput
import org.gradle.util.ConfigureUtil
import org.jetbrains.kotlin.gradle.plugin.CInteropSettings
import org.jetbrains.kotlin.gradle.plugin.CInteropSettings.IncludeDirectories
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
import java.io.File
import javax.inject.Inject
open class DefaultCInteropSettings @Inject constructor(
private val project: Project,
private val name: String,
override val compilation: KotlinNativeCompilation
) : CInteropSettings {
inner class DefaultIncludeDirectories: CInteropSettings.IncludeDirectories {
var allHeadersDirs: FileCollection = project.files()
var headerFilterDirs: FileCollection = project.files()
override fun allHeaders(vararg includeDirs: Any) = allHeaders(includeDirs.toList())
override fun allHeaders(includeDirs: Collection<Any>) {
allHeadersDirs += project.files(*includeDirs.toTypedArray())
}
override fun headerFilterOnly(vararg includeDirs: Any) = headerFilterOnly(includeDirs.toList())
override fun headerFilterOnly(includeDirs: Collection<Any>) {
headerFilterDirs += project.files(*includeDirs.toTypedArray())
}
}
override fun getName(): String = name
val target: KotlinNativeTarget
get() = compilation.target
val interopProcessingTaskName: String
get() = lowerCamelCaseName(
"cinterop",
compilation.compilationName.takeIf { it != "main" }.orEmpty(),
name,
target.disambiguationClassifier
)
var defFile: File = project.projectDir.resolve("src/nativeInterop/cinterop/$name.def")
var packageName: String? = null
val compilerOpts = mutableListOf<String>()
val linkerOpts = mutableListOf<String>()
val extraOpts = mutableListOf<String>()
val includeDirs = DefaultIncludeDirectories()
var headers: FileCollection = project.files()
// DSL methods.
override fun defFile(file: Any) {
defFile = project.file(file)
}
override fun packageName(value: String) {
packageName = value
}
override fun header(file: Any) = headers(file)
override fun headers(vararg files: Any) = headers(project.files(files))
override fun headers(files: FileCollection) {
headers += files
}
override fun includeDirs(vararg values: Any) = includeDirs.allHeaders(values.toList())
override fun includeDirs(closure: Closure<Unit>) = includeDirs(ConfigureUtil.configureUsing(closure))
override fun includeDirs(action: Action<IncludeDirectories>) = includeDirs { action.execute(this) }
override fun includeDirs(configure: IncludeDirectories.() -> Unit) = includeDirs.configure()
override fun compilerOpts(vararg values: String) = compilerOpts(values.toList())
override fun compilerOpts(values: List<String>) {
compilerOpts.addAll(values)
}
override fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
override fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
override fun extraOpts(vararg values: Any) = extraOpts(values.toList())
override fun extraOpts(values: List<Any>) {
extraOpts.addAll(values.map { it.toString() })
}
}
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.gradle.plugin.mpp
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.FileCollection
@@ -269,6 +271,9 @@ class KotlinNativeCompilation(
override val output: SourceSetOutput
) : AbstractKotlinCompilation(target, name) {
private val project: Project
get() = target.project
// A FileCollection containing source files from all source sets used by this compilation
// (taking into account dependencies between source sets). Used by both compilation
// and linking tasks.
@@ -279,6 +284,8 @@ class KotlinNativeCompilation(
var friendCompilationName: String? = null
var interopFiles: FileCollection = project.files()
internal val friendCompilation: KotlinNativeCompilation?
get() = friendCompilationName?.let {
target.compilations.getByName(it)
@@ -320,6 +327,24 @@ class KotlinNativeCompilation(
var entryPoint: String? = null
fun entryPoint(value: String) { entryPoint = value }
// Interop DSL.
val cinterops = project.container(DefaultCInteropSettings::class.java) { cinteropName ->
DefaultCInteropSettings(project, cinteropName,this)
}
var linkerOpts = mutableListOf<String>()
fun cinterops(action: NamedDomainObjectContainer<DefaultCInteropSettings>.() -> Unit) = cinterops.action()
fun cinterops(action: Closure<Unit>) = cinterops(ConfigureUtil.configureUsing(action))
fun cinterops(action: Action<NamedDomainObjectContainer<DefaultCInteropSettings>>) = action.execute(cinterops)
fun linkerOpts(vararg values: String) = linkerOpts(values.toList())
fun linkerOpts(values: List<String>) {
linkerOpts.addAll(values)
}
// Task accessors.
fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeCompile? = binaryTasks[kind to buildType]
fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeCompile =
@@ -381,7 +406,6 @@ class KotlinNativeCompilation(
// TODO: support optional expectations
allSources += sourceSet.kotlin
}
}
private object CompilationSourceSetUtil {
@@ -302,3 +302,6 @@ internal val KonanTarget.presetName: String
KonanTarget.ANDROID_ARM64 -> "androidNativeArm64"
else -> lowerCamelCaseName(*this.name.split('_').toTypedArray())
}
internal val KotlinNativeCompilation.isMainCompilation: Boolean
get() = name == KotlinCompilation.MAIN_COMPILATION_NAME
@@ -1,27 +1,78 @@
package org.jetbrains.kotlin.gradle.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.repositories.ArtifactRepository
import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.api.tasks.compile.AbstractCompile
import org.jetbrains.kotlin.compilerRunner.*
import org.jetbrains.kotlin.gradle.dsl.kotlinExtension
import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.defaultSourceSetName
import org.jetbrains.kotlin.gradle.plugin.mpp.isMainCompilation
import org.jetbrains.kotlin.konan.KonanVersion
import org.jetbrains.kotlin.konan.KonanVersionImpl
import org.jetbrains.kotlin.konan.MetaVersion
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.DependencyDirectories
import java.io.File
// TODO: It's just temporary tasks used while KN isn't integrated with Big Kotlin compilation infrastructure.
// region Useful extensions
internal fun MutableList<String>.addArg(parameter: String, value: String) {
add(parameter)
add(value)
}
internal fun MutableList<String>.addArgs(parameter: String, values: Iterable<String>) {
values.forEach {
addArg(parameter, it)
}
}
internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: String?) {
if (value != null) {
addArg(parameter, value)
}
}
internal fun MutableList<String>.addKey(key: String, enabled: Boolean) {
if (enabled) {
add(key)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: FileCollection) {
values.files.forEach {
addArg(parameter, it.canonicalPath)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: Collection<FileCollection>) {
values.forEach {
addFileArgs(parameter, it)
}
}
internal fun MutableList<String>.addListArg(parameter: String, values: List<String>) {
if (values.isNotEmpty()) {
addArg(parameter, values.joinToString(separator = " "))
}
}
private fun File.providedByCompiler(project: Project): Boolean =
toPath().startsWith(project.file(project.konanHome).toPath())
// endregion
open class KotlinNativeCompile : AbstractCompile() {
@@ -55,7 +106,7 @@ open class KotlinNativeCompile : AbstractCompile() {
get() = compilation.allSources
val libraries: FileCollection
@InputFiles get() = compilation.compileDependencyFiles
@InputFiles get() = compilation.compileDependencyFiles + compilation.interopFiles
private val friendModule: FileCollection?
// It's already taken into account in libraries
@@ -63,7 +114,7 @@ open class KotlinNativeCompile : AbstractCompile() {
override fun getClasspath(): FileCollection = libraries
override fun setClasspath(configuration: FileCollection?) {
throw UnsupportedOperationException("Setting class path directly is unsupported.")
throw UnsupportedOperationException("Setting classpath directly is unsupported.")
}
@Input
@@ -80,6 +131,9 @@ open class KotlinNativeCompile : AbstractCompile() {
val entryPoint: String?
@Optional @Input get() = compilation.entryPoint
val linkerOpts: List<String>
@Input get() = compilation.linkerOpts
val additionalCompilerOptions: Collection<String>
@Input get() = compilation.extraOpts
@@ -117,52 +171,6 @@ open class KotlinNativeCompile : AbstractCompile() {
@Optional @InputFiles
var compilerPluginClasspath: FileCollection? = null
// region Useful extensions
internal fun MutableList<String>.addArg(parameter: String, value: String) {
add(parameter)
add(value)
}
internal fun MutableList<String>.addArgs(parameter: String, values: Iterable<String>) {
values.forEach {
addArg(parameter, it)
}
}
internal fun MutableList<String>.addArgIfNotNull(parameter: String, value: String?) {
if (value != null) {
addArg(parameter, value)
}
}
internal fun MutableList<String>.addKey(key: String, enabled: Boolean) {
if (enabled) {
add(key)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: FileCollection) {
values.files.forEach {
addArg(parameter, it.canonicalPath)
}
}
internal fun MutableList<String>.addFileArgs(parameter: String, values: Collection<FileCollection>) {
values.forEach {
addFileArgs(parameter, it)
}
}
internal fun MutableList<String>.addListArg(parameter: String, values: List<String>) {
if (values.isNotEmpty()) {
addArg(parameter, values.joinToString(separator = " "))
}
}
// endregion
private val File.providedByCompiler: Boolean
get() = toPath().startsWith(project.file(project.konanHome).toPath())
@TaskAction
override fun compile() {
val output = outputFile.get()
@@ -204,7 +212,7 @@ open class KotlinNativeCompile : AbstractCompile() {
// Libraries.
libraries.files.filter {
// Support only klib files for now.
it.extension == "klib" && !it.providedByCompiler
it.extension == "klib" && !it.providedByCompiler(project)
}.forEach { library ->
library.parent?.let { addArg("-r", it) }
addArg("-l", library.nameWithoutExtension)
@@ -215,6 +223,8 @@ open class KotlinNativeCompile : AbstractCompile() {
addArg("-friend-modules", friends.map { it.absolutePath }.joinToString(File.pathSeparator))
}
addListArg("-linkerOpts", linkerOpts)
// Sources.
// TODO: Filter only kt files?
addAll(sources.files.map { it.absolutePath })
@@ -224,6 +234,106 @@ open class KotlinNativeCompile : AbstractCompile() {
}
}
open class CInteropProcess: DefaultTask() {
@Internal
lateinit var settings: DefaultCInteropSettings
@Internal // Taken into account in the outputFileProvider property
lateinit var destinationDir: Provider<File>
val konanTarget: KonanTarget
@Internal get() = settings.compilation.target.konanTarget
val interopName: String
@Internal get() = settings.name
val outputFileName: String
@Internal get() = with(CompilerOutputKind.LIBRARY) {
val baseName = settings.compilation.let {
if (it.isMainCompilation) project.name else it.name
}
val suffix = suffix(konanTarget)
return "$baseName-cinterop-$interopName$suffix"
}
val outputFile: File
@Internal get() = outputFileProvider.get().asFile
// Inputs and outputs.
@OutputFile
val outputFileProvider = newOutputFile().apply {
set { destinationDir.get().resolve(outputFileName) }
}
val defFile: File
@InputFile get() = settings.defFile
val packageName: String?
@Optional @Input get() = settings.packageName
val compilerOpts: List<String>
@Input get() = settings.compilerOpts
val linkerOpts: List<String>
@Input get() = settings.linkerOpts
val headers: FileCollection
@InputFiles get() = settings.headers
val allHeadersDirs: Set<File>
@Input get() = settings.includeDirs.allHeadersDirs.files
val headerFilterDirs: Set<File>
@Input get() = settings.includeDirs.headerFilterDirs.files
val libraries: FileCollection
@InputFiles get() = settings.compilation.compileDependencyFiles
val extraOpts: List<String>
@Input get() = settings.extraOpts
// Task action.
@TaskAction
fun processInterop() {
val args = mutableListOf<String>().apply {
addArg("-o", outputFile.absolutePath)
addArgIfNotNull("-target", konanTarget.visibleName)
addArgIfNotNull("-def", defFile.canonicalPath)
addArgIfNotNull("-pkg", packageName)
addFileArgs("-h", headers)
compilerOpts.forEach {
addArg("-copt", it)
}
linkerOpts.forEach {
addArg("-lopt", it)
}
libraries.files.filter {
// Support only klib files for now.
it.extension == "klib" && !it.providedByCompiler(project)
}.forEach { library ->
library.parent?.let { addArg("-r", it) }
addArg("-l", library.nameWithoutExtension)
}
addArgs("-copt", allHeadersDirs.map { "-I${it.absolutePath}" })
addArgs("-headerFilterAdditionalSearchPrefix", headerFilterDirs.map { it.absolutePath })
addAll(extraOpts)
}
outputFile.parentFile.mkdirs()
KonanInteropRunner(project).run(args)
}
}
open class KonanCompilerDownloadTask : DefaultTask() {
internal companion object {
@@ -319,5 +429,4 @@ open class KonanCompilerDownloadTask : DefaultTask() {
logger.info("Use Kotlin/Native distribution: $compilerDirectory")
}
}
}