diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt index af7988ab601..6cae25ffbeb 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt @@ -102,10 +102,20 @@ interface KotlinNativeLibrary : KotlinNativeBinary, ConfigurableComponentWithLinkUsage /** - * Represents an Objective C framework compiled from KN sources. + * Represents an Objective C framework compiled from Kotlin/Native sources. */ interface KotlinNativeFramework : KotlinNativeBinary, ComponentWithOutputs +/** + * A shared library compiled from Kotlin/Native sources. + */ +interface KotlinNativeDynamic : KotlinNativeBinary, ComponentWithOutputs + +/** + * A static library compiled from Kotlin/Native sources. + */ +interface KotlinNativeStatic : KotlinNativeBinary, ComponentWithOutputs + /** * Represents a test executable. */ diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt new file mode 100644 index 00000000000..44b160f6c69 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeDynamicImpl.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.gradle.plugin.experimental.internal + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer +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.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeDynamic +import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import javax.inject.Inject + +open class KotlinNativeDynamicImpl @Inject constructor( + name: String, + baseName: Provider, + componentImplementation: Configuration, + component: KotlinNativeMainComponent, + identity: KotlinNativeVariantIdentity, + projectLayout: ProjectLayout, + objects: ObjectFactory, + configurations: ConfigurationContainer, + fileOperations: FileOperations +) : AbstractKotlinNativeBinary( + name, + baseName, + component, + identity, + projectLayout, + CompilerOutputKind.DYNAMIC, + objects, + componentImplementation, + configurations, + fileOperations +), KotlinNativeDynamic { + override val outputRootName: String = "lib" +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt index 83f46228422..39c53e7ee6b 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt @@ -93,13 +93,10 @@ open class KotlinNativeMainComponent @Inject constructor( // endregion companion object { - @JvmStatic - val EXECUTABLE = OutputKind.EXECUTABLE - - @JvmStatic - val KLIBRARY = OutputKind.KLIBRARY - - @JvmStatic - val FRAMEWORK = OutputKind.FRAMEWORK + @JvmStatic val EXECUTABLE = OutputKind.EXECUTABLE + @JvmStatic val KLIBRARY = OutputKind.KLIBRARY + @JvmStatic val FRAMEWORK = OutputKind.FRAMEWORK + @JvmStatic val DYNAMIC = OutputKind.DYNAMIC + @JvmStatic val STATIC = OutputKind.STATIC } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt new file mode 100644 index 00000000000..29e61a2b286 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeStaticImpl.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.gradle.plugin.experimental.internal + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer +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.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeStatic +import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import javax.inject.Inject + + +open class KotlinNativeStaticImpl @Inject constructor( + name: String, + baseName: Provider, + componentImplementation: Configuration, + component: KotlinNativeMainComponent, + identity: KotlinNativeVariantIdentity, + projectLayout: ProjectLayout, + objects: ObjectFactory, + configurations: ConfigurationContainer, + fileOperations: FileOperations +) : AbstractKotlinNativeBinary( + name, + baseName, + component, + identity, + projectLayout, + CompilerOutputKind.STATIC, + objects, + componentImplementation, + configurations, + fileOperations +), KotlinNativeStatic { + override val outputRootName: String = "lib" +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt index 590d720e2a4..2efa7921d24 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt @@ -54,6 +54,26 @@ enum class OutputKind( ) { override fun availableFor(target: KonanTarget) = target.family == Family.OSX || target.family == Family.IOS + }, + DYNAMIC( + CompilerOutputKind.DYNAMIC, + KotlinNativeDynamicImpl::class.java, + 3, + Usage.NATIVE_RUNTIME, + Usage.NATIVE_LINK, + false + ) { + override fun availableFor(target: KonanTarget): Boolean = target != KonanTarget.WASM32 + }, + STATIC( + CompilerOutputKind.STATIC, + KotlinNativeStaticImpl::class.java, + 4, + Usage.NATIVE_RUNTIME, + Usage.NATIVE_LINK, + false + ) { + override fun availableFor(target: KonanTarget): Boolean = target != KonanTarget.WASM32 }; open fun availableFor(target: KonanTarget) = true diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt index 29a0ffa858d..e14ba821b77 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.gradle.plugin.hasProperty import org.jetbrains.kotlin.gradle.plugin.konanCompilerDownloadDir import org.jetbrains.kotlin.gradle.plugin.setProperty import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompilerDownloadTask +import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File @@ -120,6 +121,18 @@ class KotlinNativeBasePlugin: Plugin { ).apply { group = BasePlugin.BUILD_GROUP description = "Compiles Kotlin/Native source set '${binary.sourceSet.name}' into a ${binary.kind.name.toLowerCase()}" + + // Register an API header produced for shared/static library as a task output. + if (binary.kind == CompilerOutputKind.DYNAMIC || binary.kind == CompilerOutputKind.STATIC) { + val headerFileProvider = provider { + with(binary) { + val prefix = kind.prefix(konanTarget) + val baseName = getBaseName().get().replace('-', '_') + outputFile.parentFile.resolve("$prefix${baseName}_api.h") + } + } + outputs.file(headerFileProvider) + } } binary.compileTask.set(compileTask) binary.outputs.from(compileTask.outputLocationProvider) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt index b0470f8d351..91b1d017d87 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt @@ -30,6 +30,8 @@ import org.jetbrains.kotlin.gradle.plugin.addArg import org.jetbrains.kotlin.gradle.plugin.addKey import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeBinary import org.jetbrains.kotlin.konan.target.CompilerOutputKind +import org.jetbrains.kotlin.konan.target.CompilerOutputKind.* +import org.jetbrains.kotlin.konan.target.KonanTarget import java.io.File import javax.inject.Inject @@ -68,7 +70,11 @@ open class KotlinNativeCompile @Inject constructor(internal val binary: Abstract val baseName = getBaseName().get() var fileName = "${prefix}${baseName}${suffix}" - if (kind == CompilerOutputKind.FRAMEWORK) { + if (kind == FRAMEWORK || + kind == STATIC || + kind == DYNAMIC || + kind == PROGRAM && konanTarget == KonanTarget.WASM32 + ) { fileName = fileName.replace('-', '_') } diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt index 6e16e5f94b1..e692b1c7385 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeMain import org.jetbrains.kotlin.gradle.plugin.experimental.internal.OutputKind import org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinNativePlugin import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget import org.junit.Assume.assumeTrue @@ -45,6 +46,9 @@ class ExperimentalPluginTests { project.block() } + private fun assertFileExists(path: String, message: String = "No such file: $path") + = assertTrue(projectDirectory.resolve(path).exists(), message) + @Test fun `Plugin should compile one executable`() { val project = KonanProject.create(projectDirectory).apply { @@ -457,9 +461,6 @@ class ExperimentalPluginTests { } } - val ttt = project.createRunner().withArguments("tasks").build() - println(ttt.output) - val result1 = project.createRunner().withArguments("assemble").build() assertCompileOutcome(result1, compileTasks, TaskOutcome.SUCCESS) @@ -484,4 +485,72 @@ class ExperimentalPluginTests { assertEquals("test_framework_project", frameworkTask.outputFile.nameWithoutExtension) assertEquals("test-framework-project", klibraryTask.outputFile.nameWithoutExtension) } + + @Test + fun `Plugin should be able to build static and dynamic libraries`() { + + val project = KonanProject.create(projectDirectory).apply { + buildFile.writeText(""" + plugins { id 'kotlin-native' } + + sourceSets.main { + component { + outputKinds = [ DYNAMIC, STATIC ] + target 'host' + } + } + + """.trimIndent()) + settingsFile.appendText("\nrootProject.name = 'test-library'") + } + + val baseName = "test_library" + val sharedPrefix = CompilerOutputKind.DYNAMIC.prefix(HostManager.host) + val sharedSuffix = CompilerOutputKind.DYNAMIC.suffix(HostManager.host) + val staticPrefix = CompilerOutputKind.STATIC.prefix(HostManager.host) + val staticSuffix = CompilerOutputKind.STATIC.suffix(HostManager.host) + + val libraryPaths = listOf( + "build/lib/main/debug/dynamic/$sharedPrefix$baseName$sharedSuffix", + "build/lib/main/release/dynamic/$sharedPrefix$baseName$sharedSuffix", + "build/lib/main/debug/static/$staticPrefix$baseName$staticSuffix", + "build/lib/main/release/static/$staticPrefix$baseName$staticSuffix" + ) + + val headerPaths = listOf( + "build/lib/main/debug/dynamic/$sharedPrefix${baseName}_api.h", + "build/lib/main/release/dynamic/$sharedPrefix${baseName}_api.h", + "build/lib/main/debug/static/$staticPrefix${baseName}_api.h", + "build/lib/main/release/static/$staticPrefix${baseName}_api.h" + ) + + val linkTasks = listOf( + ":compileDebugDynamicKotlinNative", + ":compileReleaseDynamicKotlinNative", + ":compileDebugStaticKotlinNative", + ":compileReleaseStaticKotlinNative" + ) + + project.createRunner().withArguments("assemble", "-i").build().let { result -> + libraryPaths.forEach { assertFileExists(it) } + headerPaths.forEach { assertFileExists(it) } + linkTasks.forEach { assertEquals(TaskOutcome.SUCCESS, result.task(it)!!.outcome) } + } + + project.createRunner().withArguments("assemble").build().let { result -> + linkTasks.forEach { assertEquals(TaskOutcome.UP_TO_DATE, result.task(it)!!.outcome) } + } + + assertTrue(projectDirectory.resolve(headerPaths[0]).delete()) + + project.createRunner().withArguments("assemble").build().let { result -> + assertEquals(TaskOutcome.SUCCESS, result.task(linkTasks[0])!!.outcome) + linkTasks.drop(1).forEach { + assertEquals(TaskOutcome.UP_TO_DATE, result.task(it)!!.outcome) + } + + libraryPaths.forEach { assertFileExists(it) } + headerPaths.forEach { assertFileExists(it) } + } + } } \ No newline at end of file