diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts index 57c3470599c..0f76de14378 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/build.gradle.kts @@ -15,8 +15,13 @@ val generateMppTargetContainerWithPresets by generator( sourceSets["main"] ) -generateMppTargetContainerWithPresets.run { - systemProperty( +val generateAbstractBinaryContainer by generator( + "org.jetbrains.kotlin.generators.gradle.dsl.MppNativeBinaryDSLCodegenKt", + sourceSets["main"] +) + +listOf(generateMppTargetContainerWithPresets, generateAbstractBinaryContainer).forEach { + it.systemProperty( "org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot", project(":kotlin-gradle-plugin").projectDir.resolve("src/main/kotlin").absolutePath ) @@ -25,4 +30,4 @@ generateMppTargetContainerWithPresets.run { // Workaround: 'java -jar' refuses to read the original dotted filename on Windows, 'Unable to access jarFile org.jetbrains.kotlin....jar' tasks.named(generateMppTargetContainerWithPresets.name + "WriteClassPath").configure { archiveName = generateMppTargetContainerWithPresets.name -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt index 65adce78437..f48119c8b9f 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/codegenUtils.kt @@ -32,3 +32,9 @@ internal fun TypeName.renderErased(): String = internal fun TypeName.collectFqNames(): Set = setOf(fqName) + typeArguments.flatMap { it.collectFqNames() }.toSet() +internal fun String.indented(nSpaces: Int = 4): String { + val spaces = String(CharArray(nSpaces) { ' ' }) + return lines().joinToString("\n") { + if (it.isNotBlank()) "$spaces$it" else it + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt new file mode 100644 index 00000000000..527f4480819 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppNativeBinaryDSLCodegen.kt @@ -0,0 +1,191 @@ +/* + * 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.generators.gradle.dsl + +import java.io.File + +fun main() { + generateAbstractKotlinNativeBinaryContainer() +} + +internal data class BinaryType( + val description: String, + val className: TypeName, + val nativeOutputKind: TypeName, + val factoryMethod: String, + val getMethod: String, + val findMethod: String +) + +private fun binaryType(description: String, className: String, outputKind: String, baseMethodName: String) = + BinaryType( + description, + typeName("$MPP_PACKAGE.$className"), + typeName("${nativeOutputKindClass.fqName}.$outputKind"), + baseMethodName, + "get${baseMethodName.capitalize()}", + "find${baseMethodName.capitalize()}" + ) + +private val nativeBuildTypeClass = typeName("$MPP_PACKAGE.NativeBuildType") +private val nativeOutputKindClass = typeName("$MPP_PACKAGE.NativeOutputKind") +private val nativeBinaryBaseClass = typeName("$MPP_PACKAGE.NativeBinary") + +private fun generateFactoryMethods(binaryType: BinaryType): String { + val className = binaryType.className.renderShort() + val outputKind = binaryType.nativeOutputKind.renderShort() + val outputKindClass = nativeOutputKindClass.renderShort() + val nativeBuildType = nativeBuildTypeClass.renderShort() + val methodName = binaryType.factoryMethod + val binaryDescription = binaryType.description + + return """ + /** Creates $binaryDescription with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun $methodName( + namePrefix: String, + buildTypes: Collection<$nativeBuildType> = $nativeBuildType.DEFAULT_BUILD_TYPES, + configure: $className.() -> Unit = {} + ) = createBinaries(namePrefix, namePrefix, $outputKindClass.$outputKind, buildTypes, ::$className, configure) + + /** Creates $binaryDescription with the empty name prefix for each build type and configures it. */ + @JvmOverloads + fun $methodName( + buildTypes: Collection<$nativeBuildType> = $nativeBuildType.DEFAULT_BUILD_TYPES, + configure: $className.() -> Unit = {} + ) = createBinaries("", project.name, $outputKindClass.$outputKind, buildTypes, ::$className, configure) + + /** Creates $binaryDescription with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun $methodName( + namePrefix: String, + buildTypes: Collection<$nativeBuildType> = $nativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = $methodName(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates $binaryDescription with the default name prefix for each build type and configures it. */ + @JvmOverloads + fun $methodName( + buildTypes: Collection<$nativeBuildType> = $nativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = $methodName(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + """.trimIndent() +} + +private fun generateTypedGetters(binaryType: BinaryType): String = with(binaryType) { + val className = className.renderShort() + val buildType = nativeBuildTypeClass.renderShort() + val nativeBuildType = nativeBuildTypeClass.renderShort() + + return """ + /** Returns $description with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + abstract fun $getMethod(namePrefix: String, buildType: $buildType): $className + + /** Returns $description with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + fun $getMethod(namePrefix: String, buildType: String): $className = + $getMethod(namePrefix, $nativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns $description with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun $getMethod(buildType: $buildType): $className = $getMethod("", buildType) + + /** Returns $description with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun $getMethod(buildType: String): $className = $getMethod("", buildType) + + /** Returns $description with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + abstract fun $findMethod(namePrefix: String, buildType: $buildType): $className? + + /** Returns $description with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + fun $findMethod(namePrefix: String, buildType: String): $className? = + $findMethod(namePrefix, $nativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns $description with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun $findMethod(buildType: $buildType): $className? = $findMethod("", buildType) + + /** Returns $description with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun $findMethod(buildType: String): $className? = $findMethod("", buildType) + """.trimIndent() +} + +fun generateAbstractKotlinNativeBinaryContainer() { + + val binaryTypes = listOf( + binaryType("an executable","Executable", "EXECUTABLE", "executable"), + binaryType("a static library","StaticLibrary", "STATIC", "staticLib"), + binaryType("a shared library","SharedLibrary", "DYNAMIC", "sharedLib"), + binaryType("an Objective-C framework","Framework", "FRAMEWORK", "framework") + ) + + val className = typeName("org.jetbrains.kotlin.gradle.dsl.AbstractKotlinNativeBinaryContainer") + val superClassName = typeName("org.gradle.api.DomainObjectSet", nativeBinaryBaseClass.fqName) + + val imports = """ + import groovy.lang.Closure + import org.gradle.api.DomainObjectSet + import org.gradle.api.Project + import org.gradle.util.ConfigureUtil + import $MPP_PACKAGE.* + """.trimIndent() + + val generatedCodeWarning = "// DO NOT EDIT MANUALLY! Generated by ${object {}.javaClass.enclosingClass.name}" + + val classProperties = listOf( + "abstract val project: Project", + "abstract val target: ${typeName(KOTLIN_NATIVE_TARGET_CLASS_FQNAME).shortName()}" + ).joinToString(separator = "\n") { it.indented(4) } + + val nativeBinary = nativeBinaryBaseClass.renderShort() + val nativeOutputKind = nativeOutputKindClass.renderShort() + val nativeBuildType = nativeBuildTypeClass.renderShort() + + val buildTypeConstants = listOf( + "// User-visible constants.", + "val DEBUG = $nativeBuildType.DEBUG", + "val RELEASE = $nativeBuildType.RELEASE" + ).joinToString(separator = "\n") { it.indented(4) } + + val baseFactoryFunction = """ + protected abstract fun createBinaries( + namePrefix: String, + baseName: String, + outputKind: $nativeOutputKind, + buildTypes: Collection<$nativeBuildType>, + create: (name: String, baseName: String, buildType: $nativeBuildType, compilation: KotlinNativeCompilation) -> T, + configure: T.() -> Unit + ) + """.trimIndent().indented(4) + + val namedGetters = """ + /** Provide an access to binaries using the [] operator in Groovy DSL. */ + fun getAt(name: String): NativeBinary = getByName(name) + + /** Provide an access to binaries using the [] operator in Kotlin DSL. */ + operator fun get(name: String): NativeBinary = getByName(name) + + /** Returns a binary with the given [name]. Throws an exception if there is no such binary. */ + abstract fun getByName(name: String): NativeBinary + + /** Returns a binary with the given [name]. Returns null if there is no such binary. */ + abstract fun findByName(name: String): NativeBinary? + """.trimIndent().indented(4) + + val code = listOf( + "package ${className.packageName()}", + imports, + generatedCodeWarning, + "abstract class ${className.renderShort()} : ${superClassName.renderShort()} {", + classProperties, + buildTypeConstants, + baseFactoryFunction, + namedGetters, + binaryTypes.joinToString(separator = "\n\n") { generateTypedGetters(it).indented(4) }, + binaryTypes.joinToString(separator = "\n\n") { generateFactoryMethods(it).indented(4) }, + "}" + ).joinToString(separator = "\n\n") + + val outputSourceRoot = System.getProperties()["org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot"] + val targetFile = File("$outputSourceRoot/${className.fqName.replace(".", "/")}.kt") + targetFile.writeText(code) +} diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt index d7e0f46c754..1e2a456bc78 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/mppPresetFunctionsCodegen.kt @@ -19,7 +19,7 @@ private val parentInterface = KotlinTargetsContainerWithPresets::class private val presetsProperty = KotlinTargetsContainerWithPresets::presets.name private fun generateKotlinTargetContainerWithPresetFunctionsInterface() { - // Generate KotlinMutliplatformExtension subclass with member functions for the presets: + // Generate KotlinMultiplatformExtension subclass with member functions for the presets: val functions = allPresetEntries.map { generatePresetFunctions(it, presetsProperty, "configureOrCreate") } @@ -79,8 +79,3 @@ private fun generatePresetFunctions( fun $presetName(configure: Closure<*>) = $presetName { ConfigureUtil.configure(configure, this) } """.trimIndent() } - -private fun String.indented(nSpaces: Int = 4): String { - val spaces = String(CharArray(nSpaces) { ' ' }) - return lines().joinToString("\n") { "$spaces$it" } -} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt index ee3b6378203..0057ee6ded6 100644 --- a/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt +++ b/libraries/tools/kotlin-gradle-plugin-dsl-codegen/src/main/kotlin/org/jetbrains/kotlin/generators/gradle/dsl/presetEntries.kt @@ -16,10 +16,10 @@ internal class KotlinPresetEntry( internal fun KotlinPresetEntry.typeNames(): Set = setOf(presetType, targetType) -private const val MPP_PACKAGE = "org.jetbrains.kotlin.gradle.plugin.mpp" +internal const val MPP_PACKAGE = "org.jetbrains.kotlin.gradle.plugin.mpp" -private const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset" -private const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" +internal const val KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTargetPreset" +internal const val KOTLIN_NATIVE_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinNativeTarget" private const val KOTLIN_ONLY_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinOnlyTarget" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt new file mode 100644 index 00000000000..cb992852294 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/AbstractKotlinNativeBinaryContainer.kt @@ -0,0 +1,265 @@ +package org.jetbrains.kotlin.gradle.dsl + +import groovy.lang.Closure +import org.gradle.api.DomainObjectSet +import org.gradle.api.Project +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.plugin.mpp.* + +// DO NOT EDIT MANUALLY! Generated by org.jetbrains.kotlin.generators.gradle.dsl.MppNativeBinaryDSLCodegenKt + +abstract class AbstractKotlinNativeBinaryContainer : DomainObjectSet { + + abstract val project: Project + abstract val target: KotlinNativeTarget + + // User-visible constants. + val DEBUG = NativeBuildType.DEBUG + val RELEASE = NativeBuildType.RELEASE + + protected abstract fun createBinaries( + namePrefix: String, + baseName: String, + outputKind: NativeOutputKind, + buildTypes: Collection, + create: (name: String, baseName: String, buildType: NativeBuildType, compilation: KotlinNativeCompilation) -> T, + configure: T.() -> Unit + ) + + /** Provide an access to binaries using the [] operator in Groovy DSL. */ + fun getAt(name: String): NativeBinary = getByName(name) + + /** Provide an access to binaries using the [] operator in Kotlin DSL. */ + operator fun get(name: String): NativeBinary = getByName(name) + + /** Returns a binary with the given [name]. Throws an exception if there is no such binary. */ + abstract fun getByName(name: String): NativeBinary + + /** Returns a binary with the given [name]. Returns null if there is no such binary. */ + abstract fun findByName(name: String): NativeBinary? + + /** Returns an executable with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + abstract fun getExecutable(namePrefix: String, buildType: NativeBuildType): Executable + + /** Returns an executable with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + fun getExecutable(namePrefix: String, buildType: String): Executable = + getExecutable(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns an executable with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getExecutable(buildType: NativeBuildType): Executable = getExecutable("", buildType) + + /** Returns an executable with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getExecutable(buildType: String): Executable = getExecutable("", buildType) + + /** Returns an executable with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + abstract fun findExecutable(namePrefix: String, buildType: NativeBuildType): Executable? + + /** Returns an executable with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + fun findExecutable(namePrefix: String, buildType: String): Executable? = + findExecutable(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns an executable with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findExecutable(buildType: NativeBuildType): Executable? = findExecutable("", buildType) + + /** Returns an executable with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findExecutable(buildType: String): Executable? = findExecutable("", buildType) + + /** Returns a static library with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + abstract fun getStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary + + /** Returns a static library with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + fun getStaticLib(namePrefix: String, buildType: String): StaticLibrary = + getStaticLib(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns a static library with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getStaticLib(buildType: NativeBuildType): StaticLibrary = getStaticLib("", buildType) + + /** Returns a static library with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getStaticLib(buildType: String): StaticLibrary = getStaticLib("", buildType) + + /** Returns a static library with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + abstract fun findStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary? + + /** Returns a static library with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + fun findStaticLib(namePrefix: String, buildType: String): StaticLibrary? = + findStaticLib(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns a static library with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findStaticLib(buildType: NativeBuildType): StaticLibrary? = findStaticLib("", buildType) + + /** Returns a static library with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findStaticLib(buildType: String): StaticLibrary? = findStaticLib("", buildType) + + /** Returns a shared library with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + abstract fun getSharedLib(namePrefix: String, buildType: NativeBuildType): SharedLibrary + + /** Returns a shared library with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + fun getSharedLib(namePrefix: String, buildType: String): SharedLibrary = + getSharedLib(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns a shared library with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getSharedLib(buildType: NativeBuildType): SharedLibrary = getSharedLib("", buildType) + + /** Returns a shared library with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getSharedLib(buildType: String): SharedLibrary = getSharedLib("", buildType) + + /** Returns a shared library with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + abstract fun findSharedLib(namePrefix: String, buildType: NativeBuildType): SharedLibrary? + + /** Returns a shared library with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + fun findSharedLib(namePrefix: String, buildType: String): SharedLibrary? = + findSharedLib(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns a shared library with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findSharedLib(buildType: NativeBuildType): SharedLibrary? = findSharedLib("", buildType) + + /** Returns a shared library with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findSharedLib(buildType: String): SharedLibrary? = findSharedLib("", buildType) + + /** Returns an Objective-C framework with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + abstract fun getFramework(namePrefix: String, buildType: NativeBuildType): Framework + + /** Returns an Objective-C framework with the given [namePrefix] and the given build type. Throws an exception if there is no such binary.*/ + fun getFramework(namePrefix: String, buildType: String): Framework = + getFramework(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns an Objective-C framework with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getFramework(buildType: NativeBuildType): Framework = getFramework("", buildType) + + /** Returns an Objective-C framework with the empty name prefix and the given build type. Throws an exception if there is no such binary.*/ + fun getFramework(buildType: String): Framework = getFramework("", buildType) + + /** Returns an Objective-C framework with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + abstract fun findFramework(namePrefix: String, buildType: NativeBuildType): Framework? + + /** Returns an Objective-C framework with the given [namePrefix] and the given build type. Returns null if there is no such binary. */ + fun findFramework(namePrefix: String, buildType: String): Framework? = + findFramework(namePrefix, NativeBuildType.valueOf(buildType.toUpperCase())) + + /** Returns an Objective-C framework with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findFramework(buildType: NativeBuildType): Framework? = findFramework("", buildType) + + /** Returns an Objective-C framework with the empty name prefix and the given build type. Returns null if there is no such binary. */ + fun findFramework(buildType: String): Framework? = findFramework("", buildType) + + /** Creates an executable with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun executable( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: Executable.() -> Unit = {} + ) = createBinaries(namePrefix, namePrefix, NativeOutputKind.EXECUTABLE, buildTypes, ::Executable, configure) + + /** Creates an executable with the empty name prefix for each build type and configures it. */ + @JvmOverloads + fun executable( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: Executable.() -> Unit = {} + ) = createBinaries("", project.name, NativeOutputKind.EXECUTABLE, buildTypes, ::Executable, configure) + + /** Creates an executable with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun executable( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = executable(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates an executable with the default name prefix for each build type and configures it. */ + @JvmOverloads + fun executable( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = executable(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates a static library with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun staticLib( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: StaticLibrary.() -> Unit = {} + ) = createBinaries(namePrefix, namePrefix, NativeOutputKind.STATIC, buildTypes, ::StaticLibrary, configure) + + /** Creates a static library with the empty name prefix for each build type and configures it. */ + @JvmOverloads + fun staticLib( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: StaticLibrary.() -> Unit = {} + ) = createBinaries("", project.name, NativeOutputKind.STATIC, buildTypes, ::StaticLibrary, configure) + + /** Creates a static library with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun staticLib( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = staticLib(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates a static library with the default name prefix for each build type and configures it. */ + @JvmOverloads + fun staticLib( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = staticLib(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates a shared library with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun sharedLib( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: SharedLibrary.() -> Unit = {} + ) = createBinaries(namePrefix, namePrefix, NativeOutputKind.DYNAMIC, buildTypes, ::SharedLibrary, configure) + + /** Creates a shared library with the empty name prefix for each build type and configures it. */ + @JvmOverloads + fun sharedLib( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: SharedLibrary.() -> Unit = {} + ) = createBinaries("", project.name, NativeOutputKind.DYNAMIC, buildTypes, ::SharedLibrary, configure) + + /** Creates a shared library with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun sharedLib( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = sharedLib(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates a shared library with the default name prefix for each build type and configures it. */ + @JvmOverloads + fun sharedLib( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = sharedLib(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates an Objective-C framework with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun framework( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: Framework.() -> Unit = {} + ) = createBinaries(namePrefix, namePrefix, NativeOutputKind.FRAMEWORK, buildTypes, ::Framework, configure) + + /** Creates an Objective-C framework with the empty name prefix for each build type and configures it. */ + @JvmOverloads + fun framework( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configure: Framework.() -> Unit = {} + ) = createBinaries("", project.name, NativeOutputKind.FRAMEWORK, buildTypes, ::Framework, configure) + + /** Creates an Objective-C framework with the given [namePrefix] for each build type and configures it. */ + @JvmOverloads + fun framework( + namePrefix: String, + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = framework(namePrefix, buildTypes) { ConfigureUtil.configure(configureClosure, this) } + + /** Creates an Objective-C framework with the default name prefix for each build type and configures it. */ + @JvmOverloads + fun framework( + buildTypes: Collection = NativeBuildType.DEFAULT_BUILD_TYPES, + configureClosure: Closure<*> + ) = framework(buildTypes) { ConfigureUtil.configure(configureClosure, this) } + +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt new file mode 100644 index 00000000000..87f29cfad6e --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinNativeBinaryContainer.kt @@ -0,0 +1,153 @@ +/* + * 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.dsl + +import org.gradle.api.* +import org.gradle.api.plugins.ExtensionAware +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName +import javax.inject.Inject + +/* +Naming: + +executable('foo', [debug, release]) -> fooDebugExecutable + fooReleaseExecutable +executable -> debugExecutable, releaseExecutable +executable([debug]) -> debugExecutable + +// Tests: +1. Cannot add a second binary with the same parameters. +2. Can access a binary by name +3. Access linkTask +4. Access runTask +5. Final binaries have correct names +6. Old APIs work fine: + - creating binaries, + - getting binaries and link tasks, + - setting binary parameters using compilation DSL: linker opts and entry points +*/ + +open class KotlinNativeBinaryContainer @Inject constructor( + override val target: KotlinNativeTarget, + backingContainer: DomainObjectSet +) : AbstractKotlinNativeBinaryContainer(), + DomainObjectSet by backingContainer +{ + override val project: Project + get() = target.project + + private val defaultCompilation: KotlinNativeCompilation + get() = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME) + + private val nameToBinary = mutableMapOf() + + // region DSL getters. + private fun generateName(prefix: String, buildType: NativeBuildType, outputKindClassifier: String) = + lowerCamelCaseName(prefix, buildType.getName(), outputKindClassifier) + + private inline fun getBinary( + namePrefix: String, + buildType: NativeBuildType, + outputKind: NativeOutputKind + ): T { + val classifier = outputKind.taskNameClassifier + val name = generateName(namePrefix, buildType, classifier) + val binary = getByName(name) + require(binary is T && binary.buildType == buildType) { + "Binary $name has incorrect outputKind or build type.\n" + + "Expected: ${buildType.getName()} $classifier. Actual: ${binary.buildType.getName()} ${binary.outputKind.taskNameClassifier}." + } + return binary as T + } + + private inline fun findBinary( + namePrefix: String, + buildType: NativeBuildType, + outputKind: NativeOutputKind + ): T? { + val classifier = outputKind.taskNameClassifier + val name = generateName(namePrefix, buildType, classifier) + val binary = findByName(name) + return if (binary is T && binary.buildType == buildType) { + binary + } else { + null + } + } + + override fun getByName(name: String): NativeBinary = nameToBinary.getValue(name) + override fun findByName(name: String): NativeBinary? = nameToBinary[name] + + override fun getExecutable(namePrefix: String, buildType: NativeBuildType): Executable = + getBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE) + + override fun getStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary = + getBinary(namePrefix, buildType, NativeOutputKind.STATIC) + + override fun getSharedLib(namePrefix: String, buildType: NativeBuildType): SharedLibrary = + getBinary(namePrefix, buildType, NativeOutputKind.DYNAMIC) + + override fun getFramework(namePrefix: String, buildType: NativeBuildType): Framework = + getBinary(namePrefix, buildType, NativeOutputKind.FRAMEWORK) + + + override fun findExecutable(namePrefix: String, buildType: NativeBuildType): Executable? = + findBinary(namePrefix, buildType, NativeOutputKind.EXECUTABLE) + + override fun findStaticLib(namePrefix: String, buildType: NativeBuildType): StaticLibrary? = + findBinary(namePrefix, buildType, NativeOutputKind.STATIC) + + override fun findSharedLib(namePrefix: String, buildType: NativeBuildType): SharedLibrary? = + findBinary(namePrefix, buildType, NativeOutputKind.DYNAMIC) + + override fun findFramework(namePrefix: String, buildType: NativeBuildType): Framework? = + findBinary(namePrefix, buildType, NativeOutputKind.FRAMEWORK) + // endregion. + + // region Factories + override fun createBinaries( + namePrefix: String, + baseName: String, + outputKind: NativeOutputKind, + buildTypes: Collection, + create: (name: String, baseName: String, buildType: NativeBuildType, compilation: KotlinNativeCompilation) -> T, + configure: T.() -> Unit + ) = buildTypes.forEach { buildType -> + val name = generateName(namePrefix, buildType, outputKind.taskNameClassifier) + + require(name !in nameToBinary) { + "Cannot create binary $name: binary with such a name already exists" + } + + require(outputKind.availableFor(target.konanTarget)) { + "Cannot create binary $name: ${outputKind.taskNameClassifier.decapitalize()} binaries are not available for target ${target.name}" + } + + val binary = create(name, baseName, buildType, defaultCompilation) + add(binary) + nameToBinary[binary.name] = binary + // Allow accessing binaries as properties of the container in Groovy DSL. + if (this is ExtensionAware) { + extensions.add(binary.name, binary) + } + binary.configure() + } + + internal fun defaultTestExecutable( + configure: Executable.() -> Unit + ) = createBinaries( + "test", + "test", + NativeOutputKind.EXECUTABLE, + listOf(NativeBuildType.DEBUG), + { name, baseName, buildType, compilation -> + Executable(name, baseName, buildType, compilation, true) + }, + configure + ) + // endregion. +} 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 3ba20c472dc..b77b3130716 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 @@ -23,6 +23,7 @@ import org.gradle.api.plugins.BasePlugin import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.tasks.Copy import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.Exec import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.testing.Test import org.gradle.language.base.plugins.LifecycleBasePlugin @@ -33,8 +34,10 @@ import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.mpp.* +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile import org.jetbrains.kotlin.gradle.tasks.CInteropProcess import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -59,7 +62,8 @@ abstract class AbstractKotlinTargetConfigurator } - abstract fun configureArchivesAndComponent(target: KotlinTargetType) + abstract protected fun configureArchivesAndComponent(target: KotlinTargetType) + abstract protected fun configureTest(target: KotlinTargetType) private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) { val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete @@ -116,22 +120,6 @@ abstract class AbstractKotlinTargetConfigurator } } - protected fun configureTest(target: KotlinTarget) { - val testCompilation = target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles - ?: return // Otherwise, there is no runtime classpath - - target.project.tasks.create(lowerCamelCaseName(target.targetName, testTaskNameSuffix), Test::class.java).apply { - project.afterEvaluate { - // use afterEvaluate to override the JavaPlugin defaults for Test tasks - conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs } - conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles } - description = "Runs the unit tests." - group = JavaBasePlugin.VERIFICATION_GROUP - target.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(this@apply) - } - } - } - protected fun configureResourceProcessing( compilation: KotlinCompilationWithResources<*>, resourceSet: FileCollection @@ -372,6 +360,22 @@ open class KotlinTargetConfigurator } } + override fun configureTest(target: KotlinOnlyTarget) { + val testCompilation = target.compilations.findByName(KotlinCompilation.TEST_COMPILATION_NAME) as? KotlinCompilationToRunnableFiles<*> + ?: return // Otherwise, there is no runtime classpath + + target.project.tasks.create(lowerCamelCaseName(target.targetName, testTaskNameSuffix), Test::class.java).apply { + project.afterEvaluate { + // use afterEvaluate to override the JavaPlugin defaults for Test tasks + conventionMapping.map("testClassesDirs") { testCompilation.output.classesDirs } + conventionMapping.map("classpath") { testCompilation.runtimeDependencyFiles } + description = "Runs the unit tests." + group = JavaBasePlugin.VERIFICATION_GROUP + target.project.tasks.findByName(JavaBasePlugin.CHECK_TASK_NAME)?.dependsOn(this@apply) + } + } + } + private fun addJar(configuration: Configuration, jarArtifact: PublishArtifact) { val publications = configuration.outgoing @@ -388,49 +392,6 @@ open class KotlinNativeTargetConfigurator( createDefaultSourceSets = true, createTestCompilation = true ) { - private val hostTargets = listOf(KonanTarget.LINUX_X64, KonanTarget.MACOS_X64, KonanTarget.MINGW_X64) - - private val Collection<*>.isDimensionVisible: Boolean - get() = size > 1 - - 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) - val testTask = tasks.create(taskName, RunTestExecutable::class.java).apply { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = "Executes Kotlin/Native unit tests from the '${compilation.name}' compilation " + - "for target '${compilation.target.name}'." - enabled = compilation.target.konanTarget.isCurrentHost - - val testExecutableProperty = testExecutableLinkTask.outputFile - executable = testExecutableProperty.get().absolutePath - outputDir = project.layout.projectDirectory.asFile - - if (project.hasProperty("teamcity.version")) { - args("--ktest_logger=TEAMCITY") - } - - onlyIf { testExecutableProperty.get().exists() } - inputs.file(testExecutableProperty) - dependsOn(testExecutableLinkTask) - } - tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).apply { - dependsOn(testTask) - } - } - - private fun Project.binaryOutputDirectory( - buildType: NativeBuildType, - kind: NativeOutputKind, - compilation: KotlinNativeCompilation - ): File { - val targetSubDirectory = compilation.target.disambiguationClassifier?.let { "$it/" }.orEmpty() - val buildTypeSubDirectory = buildType.name.toLowerCase() - val kindSubDirectory = kind.outputDirectoryName - - return buildDir.resolve("bin/$targetSubDirectory${compilation.name}/$buildTypeSubDirectory/$kindSubDirectory") - } - private fun Project.klibOutputDirectory( compilation: KotlinNativeCompilation ): File { @@ -438,55 +399,14 @@ open class KotlinNativeTargetConfigurator( return buildDir.resolve("classes/kotlin/$targetSubDirectory${compilation.name}") } - private fun KotlinNativeCompile.addCompilerPlugins() { + private fun AbstractKotlinNativeCompile.addCompilerPlugins() { SubpluginEnvironment .loadSubplugins(project, kotlinPluginVersion) .addSubpluginOptions(project, this, compilerPluginOptions) compilerPluginClasspath = project.configurations.getByName(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME) } - private fun Project.createBinaryLinkTasks(compilation: KotlinNativeCompilation) = whenEvaluated { - val konanTarget = compilation.target.konanTarget - val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) } - val linkAll = project.tasks.maybeCreate(compilation.linkAllTaskName) - - for (buildType in compilation.buildTypes) { - for (kind in availableOutputKinds) { - val compilerOutputKind = kind.compilerOutputKind - - val linkTask = project.tasks.create( - compilation.linkTaskName(kind, buildType), - KotlinNativeCompile::class.java - ).apply { - this.compilation = compilation - outputKind = compilerOutputKind - group = BasePlugin.BUILD_GROUP - description = "Links ${kind.description} from the '${compilation.name}' " + - "compilation for target '${compilation.platformType.name}'." - enabled = compilation.target.konanTarget.enabledOnCurrentHost - - optimized = buildType.optimized - debuggable = buildType.debuggable - - destinationDir = binaryOutputDirectory(buildType, kind, compilation) - addCompilerPlugins() - - linkAll.dependsOn(this) - } - - compilation.binaryTasks[kind to buildType] = linkTask - - if (compilation.isTestCompilation && - buildType == NativeBuildType.DEBUG && - konanTarget in hostTargets - ) { - // TODO: Refactor and move into the corresponding method of AbstractKotlinTargetConfigurator. - createTestTask(compilation, linkTask) - } - } - } - } - + // region Artifact creation. private fun Project.createKlibArtifact( compilation: KotlinNativeCompilation, artifactFile: File, @@ -541,7 +461,52 @@ open class KotlinNativeTargetConfigurator( interop: DefaultCInteropSettings, interopTask: CInteropProcess ) = createKlibArtifact(interop.compilation, interopTask.outputFile, "cinterop-${interop.name}", interopTask, copy = true) + // endregion. + // region Task creation. + private fun Project.createLinkTask(binary: NativeBinary) { + // TODO: drop compilation.linkAllTaskName + // TODO: drop compilation.binaryTasks + // TODO: Provide a link all task. + tasks.create( + binary.linkTaskName, + KotlinNativeLink::class.java + ).apply { + val target = binary.target + this.binary = binary + group = BasePlugin.BUILD_GROUP + description = "Links ${binary.outputKind.description} '${binary.name}' for a target '${target.name}'." + enabled = target.konanTarget.enabledOnCurrentHost + destinationDir = binary.outputDirectory + addCompilerPlugins() + } + } + + private fun Project.createRunTask(binary: Executable) { + val taskName = binary.runTaskName + // TODO provide a special exec task for tests. + tasks.create(taskName, Exec::class.java).apply { + if (binary.isDefaultTestExecutable) { + group = LifecycleBasePlugin.VERIFICATION_GROUP + description = "Executes Kotlin/Native unit tests for target ${binary.target.name}." + tasks.maybeCreate(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(this) + if (project.hasProperty("teamcity.version")) { + args("--ktest_logger=TEAMCITY") + } + } else { + group = RUN_GROUP + description = "Executes Kotlin/Native executable ${binary.baseName} for target ${binary.target.name}" + } + + enabled = binary.target.konanTarget.isCurrentHost + + executable = binary.outputFile.absolutePath + workingDir = project.projectDir + + onlyIf { binary.outputFile.exists() } + dependsOn(binary.linkTaskName) + } + } private fun Project.createKlibCompilationTask(compilation: KotlinNativeCompilation) { val compileTask = tasks.create( @@ -549,7 +514,6 @@ open class KotlinNativeTargetConfigurator( KotlinNativeCompile::class.java ).apply { this.compilation = compilation - outputKind = CompilerOutputKind.LIBRARY group = BasePlugin.BUILD_GROUP description = "Compiles a klibrary from the '${compilation.name}' " + "compilation for target '${compilation.platformType.name}'." @@ -602,12 +566,20 @@ open class KotlinNativeTargetConfigurator( createCInteropKlibArtifact(interop, interopTask) } } + // endregion. + + // region Configuration. + override fun configureTarget(target: KotlinNativeTarget) { + super.configureTarget(target) + configureBinaries(target) + configureCInterops(target) + warnAboutIncorrectDependencies(target) + } override fun configureArchivesAndComponent(target: KotlinNativeTarget): Unit = with(target.project) { tasks.create(target.artifactsTaskName) target.compilations.all { createKlibCompilationTask(it) - createBinaryLinkTasks(it) } with(configurations.getByName(target.apiElementsConfigurationName)) { @@ -615,6 +587,12 @@ open class KotlinNativeTargetConfigurator( } } + override fun configureTest(target: KotlinNativeTarget) { + target.binaries.defaultTestExecutable { + compilation = target.compilations.maybeCreate(KotlinCompilation.TEST_COMPILATION_NAME) + } + } + protected fun configureCInterops(target: KotlinNativeTarget): Unit = with(target.project) { target.compilations.all { compilation -> createCInteropTasks(compilation) @@ -633,10 +611,44 @@ open class KotlinNativeTargetConfigurator( } } - override fun configureTarget(target: KotlinNativeTarget) { - super.configureTarget(target) - configureCInterops(target) - warnAboutIncorrectDependencies(target) + protected fun configureBinaries(target: KotlinNativeTarget) { + val project = target.project + target.binaries.all { + project.createLinkTask(it) + } + + target.binaries.withType(Executable::class.java).all { + project.createRunTask(it) + } + + // Create binaries for output kinds declared using the old DSL. + project.whenEvaluated { + target.compilations.all { compilation -> + val binaries = target.binaries + val konanTarget = compilation.target.konanTarget + val name = compilation.name + val buildTypes = compilation.buildTypes + val availableOutputKinds = compilation.outputKinds.filter { it.availableFor(konanTarget) } + + val configure: NativeBinary.() -> Unit = { + this.compilation = compilation + linkerOpts.addAll(compilation.linkerOpts) + if (this is Executable) { + entryPoint = compilation.entryPoint + } + compilation.binaryTasks[outputKind to buildType] = this + } + + for (kind in availableOutputKinds) { + when (kind) { + NativeOutputKind.EXECUTABLE -> binaries.executable(name, buildTypes, configure) + NativeOutputKind.DYNAMIC -> binaries.sharedLib(name, buildTypes, configure) + NativeOutputKind.STATIC -> binaries.staticLib(name, buildTypes, configure) + NativeOutputKind.FRAMEWORK -> binaries.framework(name, buildTypes, configure) + } + } + } + } } override fun defineConfigurationsForTarget(target: KotlinNativeTarget) { @@ -685,6 +697,7 @@ open class KotlinNativeTargetConfigurator( } } } + // endregion. object NativeArtifactFormat { const val KLIB = "org.jetbrains.kotlin.klib" @@ -692,6 +705,7 @@ open class KotlinNativeTargetConfigurator( companion object { const val INTEROP_GROUP = "interop" + const val RUN_GROUP = "run" protected fun defineConfigurationsForCInterop( compilation: KotlinNativeCompilation, diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt index 92fa2cb0c64..e63b6e6573d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinCompilationFactory.kt @@ -85,12 +85,9 @@ class KotlinNativeCompilationFactory( KotlinNativeCompilation(target, name).apply { if (name == KotlinCompilation.TEST_COMPILATION_NAME) { friendCompilationName = KotlinCompilation.MAIN_COMPILATION_NAME - outputKinds = mutableListOf(NativeOutputKind.EXECUTABLE) - buildTypes = mutableListOf(NativeBuildType.DEBUG) isTestCompilation = true - } else { - buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE) } + buildTypes = mutableListOf(NativeBuildType.DEBUG, NativeBuildType.RELEASE) } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt new file mode 100644 index 00000000000..6ec3e5871db --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaries.kt @@ -0,0 +1,156 @@ +/* + * 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.Named +import org.gradle.api.Project +import org.gradle.api.tasks.AbstractExecTask +import org.gradle.util.ConfigureUtil +import org.jetbrains.kotlin.gradle.plugin.AbstractKotlinTargetConfigurator +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink +import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName +import java.io.File + +// TODO: Extract API. + +// TODO: Should the baseName be a var? + +/** + * A base class representing a final binary produced by the Kotlin/Native compiler + * @param name - a name of the DSL entity. + * @param baseName - a base name for the output binary file. E.g. for baseName foo we produce binaries foo.kexe, libfoo.so, foo.framework. + * @param compilation - a compilation used to produce this binary. + * + */ +sealed class NativeBinary( + private val name: String, + val baseName: String, + val buildType: NativeBuildType, + var compilation: KotlinNativeCompilation +) : Named { + + val target: KotlinNativeTarget + get() = compilation.target + + val project: Project + get() = target.project + + abstract val outputKind: NativeOutputKind + + // Configuration DSL. + var debuggable: Boolean = false + var optimized: Boolean = false + + var linkerOpts: MutableList = mutableListOf() + + fun linkerOpts(vararg options: String) { + linkerOpts.addAll(options.toList()) + } + + fun linkerOpts(options: Iterable) { + linkerOpts.addAll(options) + } + + // Link task access. + val linkTaskName: String + get() = lowerCamelCaseName("link", name, target.targetName) + + val linkTask: KotlinNativeLink + get() = project.tasks.getByName(linkTaskName) as KotlinNativeLink + + // Output access. + // TODO: Provide output configurations and integrate them with Gradle Native. + val outputDirectory: File = with(project) { + val targetSubDirectory = target.disambiguationClassifier?.let { "$it/" }.orEmpty() + buildDir.resolve("bin/$targetSubDirectory${this@NativeBinary.name}") + } + + val outputFile: File + get() = linkTask.outputFile.get() + + // Named implementation. + override fun getName(): String = name +} + +class Executable constructor( + name: String, + baseName: String, + buildType: NativeBuildType, + compilation: KotlinNativeCompilation, + internal val isDefaultTestExecutable: Boolean +) : NativeBinary(name, baseName, buildType, compilation) { + + constructor( + name: String, + baseName: String, + buildType: NativeBuildType, + compilation: KotlinNativeCompilation) : this(name, baseName, buildType, compilation, false) + + override val outputKind: NativeOutputKind + get() = NativeOutputKind.EXECUTABLE + + var entryPoint: String? = null + + fun entryPoint(point: String?) { + entryPoint = point + } + + val runTaskName: String + get() = if (isDefaultTestExecutable) { + lowerCamelCaseName(compilation.target.targetName, AbstractKotlinTargetConfigurator.testTaskNameSuffix) + } else { + lowerCamelCaseName("run", name, compilation.target.targetName) + } + + // TODO: may make it lateinit (along with linkTasks)? + val runTask: AbstractExecTask<*> + get() = project.tasks.getByName(runTaskName) as AbstractExecTask<*> + + fun runTask(configure: AbstractExecTask<*>.() -> Unit) { + runTask.configure() + } + + fun runTask(configure: Closure<*>) { + ConfigureUtil.configure(configure, runTask) + } +} + +class StaticLibrary( + name: String, + baseName: String, + buildType: NativeBuildType, + compilation: KotlinNativeCompilation +) : NativeBinary(name, baseName, buildType, compilation) { + override val outputKind: NativeOutputKind + get() = NativeOutputKind.STATIC +} + +class SharedLibrary( + name: String, + baseName: String, + buildType: NativeBuildType, + compilation: KotlinNativeCompilation +) : NativeBinary(name, baseName, buildType, compilation) { + override val outputKind: NativeOutputKind + get() = NativeOutputKind.DYNAMIC +} + +class Framework( + name: String, + baseName: String, + buildType: NativeBuildType, + compilation: KotlinNativeCompilation +) : NativeBinary(name, baseName, buildType, compilation) { + + override val outputKind: NativeOutputKind + get() = NativeOutputKind.FRAMEWORK + + // TODO: Pack task configuration. +} + + diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt index c322e33bb81..65565ede859 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/NativeBinaryTypes.kt @@ -1,7 +1,6 @@ package org.jetbrains.kotlin.gradle.plugin.mpp import org.gradle.api.Named -import org.gradle.api.attributes.Usage import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.KonanTarget @@ -16,51 +15,40 @@ enum class NativeBuildType(val optimized: Boolean, val debuggable: Boolean) : Na DEBUG(false, true); override fun getName(): String = name.toLowerCase() + + companion object { + val DEFAULT_BUILD_TYPES = setOf(DEBUG, RELEASE) + } } enum class NativeOutputKind( val compilerOutputKind: CompilerOutputKind, val taskNameClassifier: String, - val outputDirectoryName: String = taskNameClassifier, - val description: String = taskNameClassifier, - val additionalCompilerFlags: List = emptyList(), - val runtimeUsageName: String? = null, - val linkUsageName: String? = null, - val publishable: Boolean = true // Not used yet. + val description: String = taskNameClassifier ) { EXECUTABLE( CompilerOutputKind.PROGRAM, "executable", - description = "an executable", - runtimeUsageName = Usage.NATIVE_RUNTIME, - publishable = false + description = "an executable" ), DYNAMIC( CompilerOutputKind.DYNAMIC, "shared", - description = "a dynamic library", - runtimeUsageName = Usage.NATIVE_RUNTIME, - linkUsageName = Usage.NATIVE_LINK, - publishable = false + description = "a dynamic library" ) { override fun availableFor(target: KonanTarget) = target != KonanTarget.WASM32 }, STATIC( CompilerOutputKind.STATIC, "static", - description = "a static library", - runtimeUsageName = Usage.NATIVE_RUNTIME, - linkUsageName = Usage.NATIVE_LINK, - publishable = false + description = "a static library" ) { override fun availableFor(target: KonanTarget) = target != KonanTarget.WASM32 }, FRAMEWORK( CompilerOutputKind.FRAMEWORK, "framework", - description = "an Objective-C framework", - linkUsageName = KotlinNativeUsage.FRAMEWORK, - publishable = false + description = "a framework" ) { override fun availableFor(target: KonanTarget) = target.family == Family.OSX || target.family == Family.IOS 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 8db58db35d3..38a5cb35e55 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 @@ -15,13 +15,11 @@ import org.gradle.api.file.SourceDirectorySet import org.gradle.api.tasks.SourceSet import org.gradle.util.ConfigureUtil import org.jetbrains.kotlin.gradle.dsl.* +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy -import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile -import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile -import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon -import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.* import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName import java.io.File @@ -321,7 +319,8 @@ class KotlinNativeCompilation( target.compilations.getByName(it) } - internal val binaryTasks = mutableMapOf, KotlinNativeCompile>() + // Used only to support the old APIs. TODO: Remove when the old APIs are removed. + internal val binaryTasks = mutableMapOf, NativeBinary>() // Native-specific DSL. var extraOpts = mutableListOf() @@ -376,10 +375,9 @@ class KotlinNativeCompilation( } // Task accessors. + fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink? = binaryTasks[kind to buildType]?.linkTask - fun findLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeCompile? = binaryTasks[kind to buildType] - - fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeCompile = + fun getLinkTask(kind: NativeOutputKind, buildType: NativeBuildType): KotlinNativeLink = findLinkTask(kind, buildType) ?: throw IllegalArgumentException("Cannot find a link task for the binary kind '$kind' and the build type '$buildType'") @@ -403,6 +401,7 @@ class KotlinNativeCompilation( override val processResourcesTaskName: String get() = disambiguateName("processResources") + // TODO: Add linkAll task and add a deprecation here. val linkAllTaskName: String get() = lowerCamelCaseName( "link", diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt index 619a3fb7202..80425b5e3ed 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/kotlinTargets.kt @@ -21,6 +21,7 @@ import org.gradle.util.ConfigureUtil import org.gradle.util.WrapUtil import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast @@ -191,28 +192,41 @@ class KotlinNativeTarget( attributes.attribute(konanTargetAttribute, konanTarget.name) } - // TODO: Should binary files be output of a target or a compilation? + val binaries = if(isGradleVersionAtLeast(4, 2)) { + // Use newInstance to allow accessing binaries by their names in Groovy using the extension mechanism. + project.objects.newInstance(KotlinNativeBinaryContainer::class.java, this, WrapUtil.toDomainObjectSet(NativeBinary::class.java)) + } else { + KotlinNativeBinaryContainer(this, WrapUtil.toDomainObjectSet(NativeBinary::class.java)) + } + + fun binaries(configure: KotlinNativeBinaryContainer.() -> Unit) { + binaries.configure() + } + + fun binaries(configure: Closure<*>) { + ConfigureUtil.configure(configure, binaries) + } + override val artifactsTaskName: String get() = disambiguateName("link") override val publishable: Boolean get() = konanTarget.enabledOnCurrentHost + // User-visible constants + val DEBUG = NativeBuildType.DEBUG + val RELEASE = NativeBuildType.RELEASE + + val EXECUTABLE = NativeOutputKind.EXECUTABLE + val FRAMEWORK = NativeOutputKind.FRAMEWORK + val DYNAMIC = NativeOutputKind.DYNAMIC + val STATIC = NativeOutputKind.STATIC + companion object { val konanTargetAttribute = Attribute.of( "org.jetbrains.kotlin.native.target", String::class.java ) - - // TODO: Can we do it better? - // User-visible constants - val DEBUG = NativeBuildType.DEBUG - val RELEASE = NativeBuildType.RELEASE - - val EXECUTABLE = NativeOutputKind.EXECUTABLE - val FRAMEWORK = NativeOutputKind.FRAMEWORK - val DYNAMIC = NativeOutputKind.DYNAMIC - val STATIC = NativeOutputKind.STATIC } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt index 1d6525cfed7..7285c2a3883 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/sources/DefaultLanguageSettingsBuilder.kt @@ -13,7 +13,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersion import org.jetbrains.kotlin.gradle.plugin.LanguageSettingsBuilder import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile -import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinNativeCompile internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { private var languageVersionImpl: LanguageVersion? = null @@ -70,7 +70,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { val pluginOptionsTask = compilerPluginOptionsTask.value ?: return null return when (pluginOptionsTask) { is AbstractKotlinCompile<*> -> pluginOptionsTask.pluginOptions - is KotlinNativeCompile -> pluginOptionsTask.compilerPluginOptions + is AbstractKotlinNativeCompile -> pluginOptionsTask.compilerPluginOptions else -> error("Unexpected task: $pluginOptionsTask") }.arguments } @@ -80,7 +80,7 @@ internal class DefaultLanguageSettingsBuilder : LanguageSettingsBuilder { val pluginClasspathTask = compilerPluginOptionsTask.value ?: return null return when (pluginClasspathTask) { is AbstractKotlinCompile<*> -> pluginClasspathTask.pluginClasspath - is KotlinNativeCompile -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files() + is AbstractKotlinNativeCompile -> pluginClasspathTask.compilerPluginClasspath ?: pluginClasspathTask.project.files() else -> error("Unexpected task: $pluginClasspathTask") } } 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 95ac4596e6b..2d1f85f800f 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 @@ -16,10 +16,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinCompile 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.gradle.plugin.mpp.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind.* import org.jetbrains.kotlin.konan.target.KonanTarget @@ -81,20 +78,26 @@ private fun FileCollection.filterOutPublishableInteropLibs(project: Project): Fi } // endregion - -open class KotlinNativeCompile : AbstractCompile(), KotlinCompile { +abstract class AbstractKotlinNativeCompile : AbstractCompile(), KotlinCompile { init { sourceCompatibility = "1.6" targetCompatibility = "1.6" } - @Internal - lateinit var compilation: KotlinNativeCompilation + abstract val compilation: KotlinNativeCompilation // region inputs/outputs - @Input - lateinit var outputKind: CompilerOutputKind + @get:Input + abstract val outputKind: CompilerOutputKind + + @get:Input + abstract val optimized: Boolean + + @get:Input + abstract val debuggable: Boolean + + abstract val baseName: String // Inputs and outputs @InputFiles @@ -116,23 +119,13 @@ open class KotlinNativeCompile : AbstractCompile(), KotlinCompile - @Input get() = compilation.linkerOpts - + // TODO: rework. val additionalCompilerOptions: Collection @Input get() = compilation.extraOpts @@ -163,13 +156,14 @@ open class KotlinNativeCompile : AbstractCompile(), KotlinCompile get() = compilation.extraOpts @@ -202,7 +196,6 @@ open class KotlinNativeCompile : AbstractCompile(), KotlinCompile @Internal get() = buildCommonArgs(true) - private fun buildCommonArgs(defaultsOnly: Boolean = false) = mutableListOf().apply { + private fun buildCommonArgs(defaultsOnly: Boolean = false): List = mutableListOf().apply { add("-Xmulti-platform") @@ -264,7 +257,7 @@ open class KotlinNativeCompile : AbstractCompile(), KotlinCompile().apply { + protected open fun buildArgs(defaultsOnly: Boolean = false): List = mutableListOf().apply { addKey("-opt", optimized) addKey("-g", debuggable) addKey("-ea", debuggable) @@ -272,7 +265,6 @@ open class KotlinNativeCompile : AbstractCompile(), KotlinCompile + get() = binary.linkerOpts + + override fun buildArgs(defaultsOnly: Boolean): List { + val superArgs = super.buildArgs(defaultsOnly) + return mutableListOf().apply { + addAll(superArgs) + addArgIfNotNull("-entry", entryPoint) + addListArg("-linker-options", linkerOpts) + } + } +} + open class CInteropProcess : DefaultTask() { @Internal