Provide preset-like factory functions for creating targets in new MPP
* Pull the `targets` and `presets` properties of the `kotlin` extension
up to an interface in `kotlin-gradle-plugin-api`
* Generate the code: we need type-safe statically-typed functions for
different target type, which seems to be only possible to achieve by
providing a function per preset.
* Place these functions in the top-level `kotlin` Gradle extension,
rather than extension functions for `kotlin.targets`:
- `kotlin.jvm { ... }` will work, while `kotlin.targets.jvm { ... }`
won't, the extension function needs to be somehow brought into scope
- reducing the nesting level by one seems to be a good move here
Issue #KT-26389 Fixed
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(gradleApi())
|
||||
compile(project(":kotlin-gradle-plugin-api"))
|
||||
compile(project(":kotlin-native:kotlin-native-utils"))
|
||||
}
|
||||
|
||||
val generateMppTargetContainerWithPresets by generator(
|
||||
"org.jetbrains.kotlin.generators.gradle.dsl.MppPresetFunctionsCodegenKt",
|
||||
sourceSets["main"]
|
||||
)
|
||||
|
||||
generateMppTargetContainerWithPresets.run {
|
||||
systemProperty(
|
||||
"org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot",
|
||||
project(":kotlin-gradle-plugin").projectDir.resolve("src/main/kotlin").absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
// Workaround: 'java -jar' refuses to read the original dotted filename on Windows, 'Unable to access jarFile org.jetbrains.kotlin....jar'
|
||||
tasks.named<Jar>(generateMppTargetContainerWithPresets.name + "WriteClassPath").configure {
|
||||
archiveName = generateMppTargetContainerWithPresets.name
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
internal data class TypeName(val fqName: String, val typeArguments: List<TypeName>)
|
||||
|
||||
internal fun typeName(fqName: String, vararg typeArgumentFlatFqNames: String): TypeName {
|
||||
require(typeArgumentFlatFqNames.none { "<" in it }) { "generics won't render to short type names properly, use the full constructor" }
|
||||
return TypeName(
|
||||
fqName,
|
||||
typeArgumentFlatFqNames.map {
|
||||
TypeName(
|
||||
it,
|
||||
emptyList()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
internal fun TypeName.shortName() = fqName.split(".").last()
|
||||
|
||||
internal fun TypeName.packageName() = fqName.substringBeforeLast(".")
|
||||
|
||||
internal fun TypeName.renderShort(): String =
|
||||
shortName() + typeArguments.takeIf { it.isNotEmpty() }?.joinToString(", ", "<", ">") { it.renderShort() }.orEmpty()
|
||||
|
||||
internal fun TypeName.renderErased(): String =
|
||||
shortName() + typeArguments.takeIf { it.isNotEmpty() }?.joinToString(", ", "<", ">") { "*" }.orEmpty()
|
||||
|
||||
internal fun TypeName.collectFqNames(): Set<String> =
|
||||
setOf(fqName) + typeArguments.flatMap { it.collectFqNames() }.toSet()
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainerWithPresets
|
||||
import java.io.File
|
||||
|
||||
fun main() {
|
||||
generateKotlinTargetContainerWithPresetFunctionsInterface()
|
||||
}
|
||||
|
||||
private val parentInterface = KotlinTargetsContainerWithPresets::class
|
||||
|
||||
private val presetsProperty = KotlinTargetsContainerWithPresets::presets.name
|
||||
|
||||
private fun generateKotlinTargetContainerWithPresetFunctionsInterface() {
|
||||
// Generate KotlinMutliplatformExtension subclass with member functions for the presets:
|
||||
val functions = allPresetEntries.map {
|
||||
generatePresetFunction(it, presetsProperty, "configureOrCreate")
|
||||
}
|
||||
|
||||
val parentInterfaceName =
|
||||
typeName(parentInterface.java.canonicalName)
|
||||
|
||||
val className =
|
||||
typeName("org.jetbrains.kotlin.gradle.dsl.KotlinTargetContainerWithPresetFunctions")
|
||||
|
||||
val imports = allPresetEntries
|
||||
.flatMap { it.typeNames() }
|
||||
.plus(parentInterfaceName)
|
||||
.filter { it.packageName() != className.packageName() }
|
||||
.flatMap { it.collectFqNames() }
|
||||
.toSortedSet()
|
||||
.joinToString("\n") { "import $it" }
|
||||
|
||||
val generatedCodeWarning = "// DO NOT EDIT MANUALLY! Generated by ${object {}.javaClass.enclosingClass.name}"
|
||||
|
||||
val code = listOf(
|
||||
"package ${className.packageName()}",
|
||||
imports,
|
||||
generatedCodeWarning,
|
||||
"interface ${className.renderShort()} : ${parentInterfaceName.renderShort()} {",
|
||||
functions.joinToString("\n\n") { it.indented(4) },
|
||||
"}"
|
||||
).joinToString("\n\n")
|
||||
|
||||
val outputSourceRoot = System.getProperties()["org.jetbrains.kotlin.generators.gradle.dsl.outputSourceRoot"]
|
||||
val targetFile = File("$outputSourceRoot/${className.fqName.replace(".", "/")}.kt")
|
||||
targetFile.writeText(code)
|
||||
}
|
||||
|
||||
private fun generatePresetFunction(
|
||||
presetEntry: KotlinPresetEntry,
|
||||
getPresetsExpression: String,
|
||||
configureOrCreateFunctionName: String
|
||||
): String = """
|
||||
fun ${presetEntry.presetName}(
|
||||
name: String = "${presetEntry.presetName}",
|
||||
configure: ${presetEntry.targetType.renderShort()}.() -> Unit = { }
|
||||
): ${presetEntry.targetType.renderShort()} =
|
||||
$configureOrCreateFunctionName(
|
||||
name,
|
||||
$getPresetsExpression.getByName("${presetEntry.presetName}") as ${presetEntry.presetType.renderErased()},
|
||||
configure
|
||||
)
|
||||
""".trimIndent()
|
||||
|
||||
private fun String.indented(nSpaces: Int = 4): String {
|
||||
val spaces = String(CharArray(nSpaces) { ' ' })
|
||||
return lines().joinToString("\n") { "$spaces$it" }
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.konan.target.HostManager
|
||||
import org.jetbrains.kotlin.konan.target.presetName
|
||||
|
||||
internal class KotlinPresetEntry(
|
||||
val presetName: String,
|
||||
val presetType: TypeName,
|
||||
val targetType: TypeName
|
||||
)
|
||||
|
||||
internal fun KotlinPresetEntry.typeNames(): Set<TypeName> = setOf(presetType, targetType)
|
||||
|
||||
private 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"
|
||||
|
||||
private const val KOTLIN_ONLY_TARGET_CLASS_FQNAME = "$MPP_PACKAGE.KotlinOnlyTarget"
|
||||
|
||||
internal val jvmPresetEntry = KotlinPresetEntry(
|
||||
"jvm",
|
||||
typeName("$MPP_PACKAGE.KotlinJvmTargetPreset"),
|
||||
typeName(
|
||||
KOTLIN_ONLY_TARGET_CLASS_FQNAME,
|
||||
"$MPP_PACKAGE.KotlinJvmCompilation"
|
||||
)
|
||||
)
|
||||
|
||||
internal val jsPresetEntry = KotlinPresetEntry(
|
||||
"js",
|
||||
typeName("$MPP_PACKAGE.KotlinJsTargetPreset"),
|
||||
typeName(
|
||||
KOTLIN_ONLY_TARGET_CLASS_FQNAME,
|
||||
"$MPP_PACKAGE.KotlinJsCompilation"
|
||||
)
|
||||
)
|
||||
|
||||
internal val androidPresetEntry = KotlinPresetEntry(
|
||||
"android",
|
||||
typeName("$MPP_PACKAGE.KotlinAndroidTargetPreset"),
|
||||
typeName("$MPP_PACKAGE.KotlinAndroidTarget")
|
||||
)
|
||||
|
||||
internal val nativePresetEntries = HostManager().targets.map { (_, target) ->
|
||||
KotlinPresetEntry(
|
||||
target.presetName,
|
||||
typeName(KOTLIN_NATIVE_TARGET_PRESET_CLASS_FQNAME),
|
||||
typeName(KOTLIN_NATIVE_TARGET_CLASS_FQNAME)
|
||||
)
|
||||
}
|
||||
|
||||
internal val allPresetEntries = listOf(
|
||||
jvmPresetEntry,
|
||||
jsPresetEntry,
|
||||
androidPresetEntry
|
||||
) + nativePresetEntries
|
||||
Reference in New Issue
Block a user