Provide an extended DSL for final native binaries
In 1.3.0 only basic settings for final native binaries (e.g.
executables or frameworks) are available. They allow a user to
specify what kinds of binaries should be produced from one or another
compilation but they don't allow specifying different options for
different binaries or creating more than 2 (debug and release)
binaries of the same kind (e.g. executable) from the same
compilation. Also link tasks for these binaries are created after
project evaluation thus they are inaccessible for a user outside
of an afterEvaluate block.
This patch adds an extended binary DSL allowing a user to declare
binaries independently from compilations and to specify different
options (e.g. linker arguments) for different binaries. Also
link tasks for binaries declared in this DSL are created at a
configuration time so user can access them outside of an
afterEvaluate block.
Also this patch adds creating run tasks for all executables
declared in the buildscript (KT-28106)
Initial DSL methods for binaries declaration are still supported.
Kotlin DSL example is the following:
kotlin {
macosX64 {
binaries {
// Create debug and release executable with
// a name prefix 'Foo'.
// Two domain objects will be created:
// fooDebugExecutable and fooReleaseExecutable
executable("Foo", listOf(RELEASE, DEBUG)) {
compilation = compilations["foo"]
entryPoint = "foo.main"
linkerOpts.add("-Llib/path")
println(runTask.name)
}
// Name prefix and build types are optional.
// debugFramework and releaseFramework are created here.
framework()
}
}
}
This commit is contained in:
@@ -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<Jar>(generateMppTargetContainerWithPresets.name + "WriteClassPath").configure {
|
||||
archiveName = generateMppTargetContainerWithPresets.name
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -32,3 +32,9 @@ internal fun TypeName.renderErased(): String =
|
||||
internal fun TypeName.collectFqNames(): Set<String> =
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+191
@@ -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 <T : $nativeBinary> 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)
|
||||
}
|
||||
+1
-6
@@ -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" }
|
||||
}
|
||||
+3
-3
@@ -16,10 +16,10 @@ internal class KotlinPresetEntry(
|
||||
|
||||
internal fun KotlinPresetEntry.typeNames(): Set<TypeName> = 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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user