diff --git a/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CinteropSettings.kt b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CinteropSettings.kt new file mode 100644 index 00000000000..bfb28b8a641 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-api/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/CinteropSettings.kt @@ -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) + + fun headerFilterOnly(vararg includeDirs: Any) + fun headerFilterOnly(includeDirs: Collection) + } + + // 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) + fun includeDirs(action: Action) + fun includeDirs(configure: IncludeDirectories.() -> Unit) + + fun compilerOpts(vararg values: String) + fun compilerOpts(values: List) + + fun linkerOpts(vararg values: String) + fun linkerOpts(values: List) + + fun extraOpts(vararg values: Any) + fun extraOpts(values: List) +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt index a9fb5475f07..db3e63acedf 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/NewMultiplatformIT.kt @@ -551,4 +551,29 @@ class NewMultiplatformIT : BaseGradleIT() { assertTasksExecuted(hostTestTask) } } -} \ No newline at end of file + + @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") + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/build.gradle new file mode 100644 index 00000000000..0775ea9c755 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/build.gradle @@ -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'] + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/build.gradle new file mode 100644 index 00000000000..4548c08bb63 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/build.gradle @@ -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' + } + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/allNative/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/allNative/kotlin/main.kt new file mode 100644 index 00000000000..1cd7a12c51d --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/allNative/kotlin/main.kt @@ -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') +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeInterop/cinterop/stdio.def b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeInterop/cinterop/stdio.def new file mode 100644 index 00000000000..69362d03386 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeInterop/cinterop/stdio.def @@ -0,0 +1,2 @@ +headers = stdio.h +compilerOpts.osx = -D_ANSI_SOURCE diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeTest/kotlin/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeTest/kotlin/test.kt new file mode 100644 index 00000000000..5098a74f5f3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/projectLibrary/src/nativeTest/kotlin/test.kt @@ -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") +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/build.gradle new file mode 100644 index 00000000000..a982b134ee0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/build.gradle @@ -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' + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/allNative/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/allNative/kotlin/main.kt new file mode 100644 index 00000000000..0dfaf366fba --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/allNative/kotlin/main.kt @@ -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') +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeInterop/cinterop/stdio.def b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeInterop/cinterop/stdio.def new file mode 100644 index 00000000000..69362d03386 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeInterop/cinterop/stdio.def @@ -0,0 +1,2 @@ +headers = stdio.h +compilerOpts.osx = -D_ANSI_SOURCE diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeTest/kotlin/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeTest/kotlin/test.kt new file mode 100644 index 00000000000..58eed8e95a3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/publishedLibrary/src/nativeTest/kotlin/test.kt @@ -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") +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/settings.gradle new file mode 100644 index 00000000000..f7bc267a2f2 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/settings.gradle @@ -0,0 +1,6 @@ +enableFeaturePreview('GRADLE_METADATA') + +rootProject.name = 'native-cinterop' + +include 'projectLibrary' +include 'publishedLibrary' diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/allNative/kotlin/main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/allNative/kotlin/main.kt new file mode 100644 index 00000000000..e4e0acc354b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/allNative/kotlin/main.kt @@ -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") +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/nativeTest/kotlin/test.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/nativeTest/kotlin/test.kt new file mode 100644 index 00000000000..21cd37d03eb --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/new-mpp-native-cinterop/src/nativeTest/kotlin/test.kt @@ -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() +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt index d900b46c451..86b76a4356c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinTargetConfigurator.kt @@ -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 = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CInteropSettings.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CInteropSettings.kt new file mode 100644 index 00000000000..ab513513aa3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/CInteropSettings.kt @@ -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) { + allHeadersDirs += project.files(*includeDirs.toTypedArray()) + } + + override fun headerFilterOnly(vararg includeDirs: Any) = headerFilterOnly(includeDirs.toList()) + override fun headerFilterOnly(includeDirs: Collection) { + 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() + val linkerOpts = mutableListOf() + val extraOpts = mutableListOf() + + 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) = includeDirs(ConfigureUtil.configureUsing(closure)) + override fun includeDirs(action: Action) = 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) { + compilerOpts.addAll(values) + } + + override fun linkerOpts(vararg values: String) = linkerOpts(values.toList()) + override fun linkerOpts(values: List) { + linkerOpts.addAll(values) + } + + override fun extraOpts(vararg values: Any) = extraOpts(values.toList()) + override fun extraOpts(values: List) { + extraOpts.addAll(values.map { it.toString() }) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt index 990177109f2..bf76d994553 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinCompilations.kt @@ -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() + + fun cinterops(action: NamedDomainObjectContainer.() -> Unit) = cinterops.action() + fun cinterops(action: Closure) = cinterops(ConfigureUtil.configureUsing(action)) + fun cinterops(action: Action>) = action.execute(cinterops) + + fun linkerOpts(vararg values: String) = linkerOpts(values.toList()) + fun linkerOpts(values: List) { + 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 { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt index 1e10e8bea50..9f7b1f2eec3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargetPresets.kt @@ -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 diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt index 570a484aeed..8f0a4e4587a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/KotlinNativeTasks.kt @@ -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.addArg(parameter: String, value: String) { + add(parameter) + add(value) +} + +internal fun MutableList.addArgs(parameter: String, values: Iterable) { + values.forEach { + addArg(parameter, it) + } +} + +internal fun MutableList.addArgIfNotNull(parameter: String, value: String?) { + if (value != null) { + addArg(parameter, value) + } +} + +internal fun MutableList.addKey(key: String, enabled: Boolean) { + if (enabled) { + add(key) + } +} + +internal fun MutableList.addFileArgs(parameter: String, values: FileCollection) { + values.files.forEach { + addArg(parameter, it.canonicalPath) + } +} + +internal fun MutableList.addFileArgs(parameter: String, values: Collection) { + values.forEach { + addFileArgs(parameter, it) + } +} + +internal fun MutableList.addListArg(parameter: String, values: List) { + 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 + @Input get() = compilation.linkerOpts + val additionalCompilerOptions: Collection @Input get() = compilation.extraOpts @@ -117,52 +171,6 @@ open class KotlinNativeCompile : AbstractCompile() { @Optional @InputFiles var compilerPluginClasspath: FileCollection? = null - // region Useful extensions - internal fun MutableList.addArg(parameter: String, value: String) { - add(parameter) - add(value) - } - - internal fun MutableList.addArgs(parameter: String, values: Iterable) { - values.forEach { - addArg(parameter, it) - } - } - - internal fun MutableList.addArgIfNotNull(parameter: String, value: String?) { - if (value != null) { - addArg(parameter, value) - } - } - - internal fun MutableList.addKey(key: String, enabled: Boolean) { - if (enabled) { - add(key) - } - } - - internal fun MutableList.addFileArgs(parameter: String, values: FileCollection) { - values.files.forEach { - addArg(parameter, it.canonicalPath) - } - } - - internal fun MutableList.addFileArgs(parameter: String, values: Collection) { - values.forEach { - addFileArgs(parameter, it) - } - } - - internal fun MutableList.addListArg(parameter: String, values: List) { - 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 + + 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 + @Input get() = settings.compilerOpts + + val linkerOpts: List + @Input get() = settings.linkerOpts + + val headers: FileCollection + @InputFiles get() = settings.headers + + val allHeadersDirs: Set + @Input get() = settings.includeDirs.allHeadersDirs.files + + val headerFilterDirs: Set + @Input get() = settings.includeDirs.headerFilterDirs.files + + val libraries: FileCollection + @InputFiles get() = settings.compilation.compileDependencyFiles + + val extraOpts: List + @Input get() = settings.extraOpts + + + // Task action. + @TaskAction + fun processInterop() { + val args = mutableListOf().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") } } - } \ No newline at end of file