Introduce Kotlin compiler options with Gradle Properties API types

Old one is deprecated and delegates to new options. All new options
are marked with task input types, so they could be used as `@Nested`
input.

Generated options are using specific types in generated
compiler options. This should simplify code completion and provide
meaningful hints to user.

At this point repository compilation will fail.

^KT-27301 In Progress
This commit is contained in:
Yahor Berdnikau
2022-07-22 16:57:44 +02:00
parent 28dd3d4e71
commit 286d0d56af
47 changed files with 1453 additions and 742 deletions
@@ -11,149 +11,110 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.Printer
import java.io.File
import java.io.PrintStream
import java.util.*
import kotlin.reflect.KAnnotatedElement
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.KVisibility
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.withNullability
// Additional properties that should be included in interface
@Suppress("unused")
interface AdditionalGradleProperties {
@GradleOption(EmptyList::class)
@GradleOption(
value = DefaultValues.EmptyStringListDefault::class,
gradleInputType = GradleInputTypes.INPUT
)
@Argument(value = "", description = "A list of additional compiler arguments")
var freeCompilerArgs: List<String>
object EmptyList : DefaultValues("emptyList()")
}
private data class GeneratedOptions(
val optionsName: FqName,
val deprecatedOptionsName: FqName,
val properties: List<KProperty1<*, *>>
)
private const val GRADLE_API_SRC_DIR = "libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin"
private const val GRADLE_PLUGIN_SRC_DIR = "libraries/tools/kotlin-gradle-plugin/src/common/kotlin"
private const val OPTIONS_PACKAGE_PREFIX = "org.jetbrains.kotlin.gradle.dsl"
private const val IMPLEMENTATION_SUFFIX = "Default"
fun generateKotlinGradleOptions(withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit) {
val apiSrcDir = File("libraries/tools/kotlin-gradle-plugin-api/src/common/kotlin")
val srcDir = File("libraries/tools/kotlin-gradle-plugin/src/common/kotlin")
val apiSrcDir = File(GRADLE_API_SRC_DIR)
val srcDir = File(GRADLE_PLUGIN_SRC_DIR)
// common interface
val commonInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinCommonToolOptions")
val commonOptions = gradleOptions<CommonToolArguments>()
val additionalOptions = gradleOptions<AdditionalGradleProperties>()
withPrinterToFile(file(apiSrcDir, commonInterfaceFqName)) {
generateInterface(
commonInterfaceFqName,
commonOptions + additionalOptions
)
}
val commonToolOptions = generateKotlinCommonToolOptions(apiSrcDir, withPrinterToFile)
val commonToolImplFqName = generateKotlinCommonToolOptionsImpl(
srcDir,
commonToolOptions.optionsName,
commonToolOptions.properties,
withPrinterToFile
)
println("### Attributes common for JVM, JS, and JS DCE\n")
generateMarkdown(commonOptions + additionalOptions)
val commonCompilerOptions = generateKotlinCommonOptions(
apiSrcDir,
commonToolOptions,
withPrinterToFile
)
val commonCompilerOptionsImplFqName = generateKotlinCommonOptionsImpl(
srcDir,
commonCompilerOptions.optionsName,
commonToolImplFqName,
commonCompilerOptions.properties,
withPrinterToFile
)
val commonCompilerInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions")
val commonCompilerOptions = gradleOptions<CommonCompilerArguments>()
withPrinterToFile(file(apiSrcDir, commonCompilerInterfaceFqName)) {
generateInterface(
commonCompilerInterfaceFqName,
commonCompilerOptions,
parentType = commonInterfaceFqName
)
}
val jvmOptions = generateKotlinJvmOptions(
apiSrcDir,
commonCompilerOptions,
withPrinterToFile
)
generateKotlinJvmOptionsImpl(
srcDir,
jvmOptions.optionsName,
commonCompilerOptionsImplFqName,
jvmOptions.properties,
withPrinterToFile
)
println("\n### Attributes common for JVM and JS\n")
generateMarkdown(commonCompilerOptions)
val jsOptions = generateKotlinJsOptions(
apiSrcDir,
commonCompilerOptions,
withPrinterToFile
)
generateKotlinJsOptionsImpl(
srcDir,
jsOptions.optionsName,
commonCompilerOptionsImplFqName,
jsOptions.properties,
withPrinterToFile
)
// generate jvm interface
val jvmInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions")
val jvmOptions = gradleOptions<K2JVMCompilerArguments>()
withPrinterToFile(file(apiSrcDir, jvmInterfaceFqName)) {
generateInterface(
jvmInterfaceFqName,
jvmOptions,
parentType = commonCompilerInterfaceFqName
)
}
// generate jvm impl
val k2JvmCompilerArgumentsFqName = FqName(K2JVMCompilerArguments::class.qualifiedName!!)
val jvmImplFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptionsBase")
withPrinterToFile(file(srcDir, jvmImplFqName)) {
generateImpl(
jvmImplFqName,
jvmInterfaceFqName,
k2JvmCompilerArgumentsFqName,
commonOptions + commonCompilerOptions + jvmOptions
)
}
println("\n### Attributes specific for JVM\n")
generateMarkdown(jvmOptions)
// generate js interface
val jsInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJsOptions")
val jsOptions = gradleOptions<K2JSCompilerArguments>()
withPrinterToFile(file(apiSrcDir, jsInterfaceFqName)) {
generateInterface(
jsInterfaceFqName,
jsOptions,
parentType = commonCompilerInterfaceFqName
)
}
val k2JsCompilerArgumentsFqName = FqName(K2JSCompilerArguments::class.qualifiedName!!)
val jsImplFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJsOptionsBase")
withPrinterToFile(file(srcDir, jsImplFqName)) {
generateImpl(
jsImplFqName,
jsInterfaceFqName,
k2JsCompilerArgumentsFqName,
commonOptions + commonCompilerOptions + jsOptions
)
}
println("\n### Attributes specific for JS\n")
generateMarkdown(jsOptions)
// generate JS DCE interface and implementation
val jsDceInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJsDceOptions")
val jsDceOptions = gradleOptions<K2JSDceArguments>()
withPrinterToFile(file(apiSrcDir, jsDceInterfaceFqName)) {
generateInterface(
jsDceInterfaceFqName,
jsDceOptions,
parentType = commonInterfaceFqName
)
}
val k2JsDceArgumentsFqName = FqName(K2JSDceArguments::class.qualifiedName!!)
val jsDceImplFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinJsDceOptionsBase")
withPrinterToFile(file(srcDir, jsDceImplFqName)) {
generateImpl(
jsDceImplFqName,
jsDceInterfaceFqName,
k2JsDceArgumentsFqName,
commonOptions + jsDceOptions
)
}
// generate multiplatform common interface and implementation
val multiplatformCommonInterfaceFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformCommonOptions")
val multiplatformCommonOptions = gradleOptions<K2MetadataCompilerArguments>()
withPrinterToFile(file(srcDir, multiplatformCommonInterfaceFqName)) {
generateInterface(
multiplatformCommonInterfaceFqName,
multiplatformCommonOptions,
parentType = commonCompilerInterfaceFqName
)
}
val k2metadataCompilerArgumentsFqName = FqName(K2MetadataCompilerArguments::class.qualifiedName!!)
val multiplatformCommonImplFqName = FqName(multiplatformCommonInterfaceFqName.asString() + "Base")
withPrinterToFile(file(srcDir, multiplatformCommonImplFqName)) {
generateImpl(
multiplatformCommonImplFqName,
multiplatformCommonInterfaceFqName,
k2metadataCompilerArgumentsFqName,
commonOptions + commonCompilerOptions + multiplatformCommonOptions
)
}
val jsDceOptions = generateJsDceOptions(
apiSrcDir,
commonToolOptions,
withPrinterToFile
)
generateJsDceOptionsImpl(
srcDir,
jsDceOptions.optionsName,
commonCompilerOptionsImplFqName,
jsDceOptions.properties,
withPrinterToFile
)
val multiplatformCommonOptions = generateMultiplatformCommonOptions(
apiSrcDir,
commonCompilerOptions,
withPrinterToFile
)
generateMultiplatformCommonOptionsImpl(
srcDir,
multiplatformCommonOptions.optionsName,
commonCompilerOptionsImplFqName,
multiplatformCommonOptions.properties,
withPrinterToFile
)
}
fun main() {
@@ -171,6 +132,314 @@ fun main() {
generateKotlinGradleOptions(::getPrinter)
}
private fun generateKotlinCommonToolOptions(
apiSrcDir: File,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val commonInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerCommonToolOptions")
val commonOptions = gradleOptions<CommonToolArguments>()
val additionalOptions = gradleOptions<AdditionalGradleProperties>()
withPrinterToFile(fileFromFqName(apiSrcDir, commonInterfaceFqName)) {
generateInterface(
commonInterfaceFqName,
commonOptions + additionalOptions
)
}
val deprecatedCommonInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinCommonToolOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedCommonInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedCommonInterfaceFqName,
commonInterfaceFqName,
commonOptions + additionalOptions,
parentType = null,
)
}
println("### Attributes common for JVM, JS, and JS DCE\n")
generateMarkdown(commonOptions + additionalOptions)
return GeneratedOptions(commonInterfaceFqName, deprecatedCommonInterfaceFqName, (commonOptions + additionalOptions))
}
private fun generateKotlinCommonToolOptionsImpl(
srcDir: File,
commonToolOptionsInterfaceFqName: FqName,
options: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): FqName {
val k2CommonToolCompilerArgumentsFqName = FqName(CommonToolArguments::class.qualifiedName!!)
val commonToolImplFqName = FqName("${commonToolOptionsInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, commonToolImplFqName)) {
generateImpl(
commonToolImplFqName,
null,
commonToolOptionsInterfaceFqName,
k2CommonToolCompilerArgumentsFqName,
options,
)
}
return commonToolImplFqName
}
private fun generateKotlinCommonOptions(
apiSrcDir: File,
commonToolGeneratedOptions: GeneratedOptions,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val commonCompilerInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerCommonOptions")
val commonCompilerOptions = gradleOptions<CommonCompilerArguments>()
withPrinterToFile(fileFromFqName(apiSrcDir, commonCompilerInterfaceFqName)) {
generateInterface(
commonCompilerInterfaceFqName,
commonCompilerOptions,
parentType = commonToolGeneratedOptions.optionsName,
)
}
val deprecatedCommonCompilerInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinCommonOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedCommonCompilerInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedCommonCompilerInterfaceFqName,
commonCompilerInterfaceFqName,
commonCompilerOptions,
parentType = commonToolGeneratedOptions.deprecatedOptionsName
)
}
println("\n### Attributes common for JVM and JS\n")
generateMarkdown(commonCompilerOptions)
return GeneratedOptions(commonCompilerInterfaceFqName, deprecatedCommonCompilerInterfaceFqName, commonCompilerOptions)
}
private fun generateKotlinCommonOptionsImpl(
srcDir: File,
commonOptionsInterfaceFqName: FqName,
commonToolImpl: FqName,
options: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): FqName {
val k2CommonCompilerArgumentsFqName = FqName(CommonCompilerArguments::class.qualifiedName!!)
val commonCompilerImplFqName = FqName("${commonOptionsInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, commonCompilerImplFqName)) {
generateImpl(
commonCompilerImplFqName,
commonToolImpl,
commonOptionsInterfaceFqName,
k2CommonCompilerArgumentsFqName,
options,
)
}
return commonCompilerImplFqName
}
private fun generateKotlinJvmOptions(
apiSrcDir: File,
commonCompilerGeneratedOptions: GeneratedOptions,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val jvmInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerJvmOptions")
val jvmOptions = gradleOptions<K2JVMCompilerArguments>()
withPrinterToFile(fileFromFqName(apiSrcDir, jvmInterfaceFqName)) {
generateInterface(
jvmInterfaceFqName,
jvmOptions,
parentType = commonCompilerGeneratedOptions.optionsName,
)
}
val deprecatedJvmInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinJvmOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedJvmInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedJvmInterfaceFqName,
jvmInterfaceFqName,
jvmOptions,
parentType = commonCompilerGeneratedOptions.deprecatedOptionsName
)
}
println("\n### Attributes specific for JVM\n")
generateMarkdown(jvmOptions)
return GeneratedOptions(jvmInterfaceFqName, deprecatedJvmInterfaceFqName, jvmOptions)
}
private fun generateKotlinJvmOptionsImpl(
srcDir: File,
jvmInterfaceFqName: FqName,
commonCompilerImpl: FqName,
jvmOptions: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val k2JvmCompilerArgumentsFqName = FqName(K2JVMCompilerArguments::class.qualifiedName!!)
val jvmImplFqName = FqName("${jvmInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, jvmImplFqName)) {
generateImpl(
jvmImplFqName,
commonCompilerImpl,
jvmInterfaceFqName,
k2JvmCompilerArgumentsFqName,
jvmOptions
)
}
}
private fun generateKotlinJsOptions(
apiSrcDir: File,
commonCompilerOptions: GeneratedOptions,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val jsInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerJsOptions")
val jsOptions = gradleOptions<K2JSCompilerArguments>()
withPrinterToFile(fileFromFqName(apiSrcDir, jsInterfaceFqName)) {
generateInterface(
jsInterfaceFqName,
jsOptions,
parentType = commonCompilerOptions.optionsName,
)
}
val deprecatedJsInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinJsOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedJsInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedJsInterfaceFqName,
jsInterfaceFqName,
jsOptions,
parentType = commonCompilerOptions.deprecatedOptionsName,
)
}
println("\n### Attributes specific for JS\n")
generateMarkdown(jsOptions)
return GeneratedOptions(jsInterfaceFqName, deprecatedJsInterfaceFqName, jsOptions)
}
private fun generateKotlinJsOptionsImpl(
srcDir: File,
jsInterfaceFqName: FqName,
commonCompilerImpl: FqName,
jsOptions: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val k2JsCompilerArgumentsFqName = FqName(K2JSCompilerArguments::class.qualifiedName!!)
val jsImplFqName = FqName("${jsInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, jsImplFqName)) {
generateImpl(
jsImplFqName,
commonCompilerImpl,
jsInterfaceFqName,
k2JsCompilerArgumentsFqName,
jsOptions
)
}
}
private fun generateJsDceOptions(
apiSrcDir: File,
commonToolOptions: GeneratedOptions,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val jsDceInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerJsDceOptions")
val jsDceOptions = gradleOptions<K2JSDceArguments>()
withPrinterToFile(fileFromFqName(apiSrcDir, jsDceInterfaceFqName)) {
generateInterface(
jsDceInterfaceFqName,
jsDceOptions,
parentType = commonToolOptions.optionsName,
)
}
val deprecatedJsDceInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinJsDceOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedJsDceInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedJsDceInterfaceFqName,
jsDceInterfaceFqName,
jsDceOptions,
parentType = commonToolOptions.deprecatedOptionsName,
)
}
println("\n### Attributes specific for JS/DCE\n")
generateMarkdown(jsDceOptions)
return GeneratedOptions(jsDceInterfaceFqName, deprecatedJsDceInterfaceFqName, jsDceOptions)
}
private fun generateJsDceOptionsImpl(
srcDir: File,
jsDceInterfaceFqName: FqName,
commonCompilerImpl: FqName,
jsDceOptions: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val k2JsDceArgumentsFqName = FqName(K2JSDceArguments::class.qualifiedName!!)
val jsDceImplFqName = FqName("${jsDceInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, jsDceImplFqName)) {
generateImpl(
jsDceImplFqName,
commonCompilerImpl,
jsDceInterfaceFqName,
k2JsDceArgumentsFqName,
jsDceOptions
)
}
}
private fun generateMultiplatformCommonOptions(
apiSrcDir: File,
commonCompilerOptions: GeneratedOptions,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
): GeneratedOptions {
val multiplatformCommonInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.CompilerMultiplatformCommonOptions")
val multiplatformCommonOptions = gradleOptions<K2MetadataCompilerArguments>()
withPrinterToFile(fileFromFqName(apiSrcDir, multiplatformCommonInterfaceFqName)) {
generateInterface(
multiplatformCommonInterfaceFqName,
multiplatformCommonOptions,
parentType = commonCompilerOptions.optionsName,
)
}
val deprecatedMultiplatformCommonInterfaceFqName = FqName("$OPTIONS_PACKAGE_PREFIX.KotlinMultiplatformCommonOptions")
withPrinterToFile(fileFromFqName(apiSrcDir, deprecatedMultiplatformCommonInterfaceFqName)) {
generateDeprecatedInterface(
deprecatedMultiplatformCommonInterfaceFqName,
multiplatformCommonInterfaceFqName,
parentType = commonCompilerOptions.deprecatedOptionsName,
properties = multiplatformCommonOptions
)
}
println("\n### Attributes specific for Multiplatform/Common\n")
generateMarkdown(multiplatformCommonOptions)
return GeneratedOptions(multiplatformCommonInterfaceFqName, deprecatedMultiplatformCommonInterfaceFqName, multiplatformCommonOptions)
}
private fun generateMultiplatformCommonOptionsImpl(
srcDir: File,
multiplatformCommonInterfaceFqName: FqName,
commonCompilerImpl: FqName,
multiplatformCommonOptions: List<KProperty1<*, *>>,
withPrinterToFile: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val k2metadataCompilerArgumentsFqName = FqName(K2MetadataCompilerArguments::class.qualifiedName!!)
val multiplatformCommonImplFqName = FqName("${multiplatformCommonInterfaceFqName.asString()}$IMPLEMENTATION_SUFFIX")
withPrinterToFile(fileFromFqName(srcDir, multiplatformCommonImplFqName)) {
generateImpl(
multiplatformCommonImplFqName,
commonCompilerImpl,
multiplatformCommonInterfaceFqName,
k2metadataCompilerArgumentsFqName,
multiplatformCommonOptions
)
}
}
private inline fun <reified T : Any> List<KProperty1<T, *>>.filterToBeDeleted() = filter { prop ->
prop.findAnnotation<GradleDeprecatedOption>()
?.let { LanguageVersion.fromVersionString(it.removeAfter) }
@@ -179,110 +448,237 @@ private inline fun <reified T : Any> List<KProperty1<T, *>>.filterToBeDeleted()
}
private inline fun <reified T : Any> gradleOptions(): List<KProperty1<T, *>> =
T::class.declaredMemberProperties.filter { it.findAnnotation<GradleOption>() != null }.filterToBeDeleted().sortedBy { it.name }
T::class
.declaredMemberProperties
.filter {
it.findAnnotation<GradleOption>() != null
}
.filterToBeDeleted()
.sortedBy { it.name }
internal fun file(baseDir: File, fqName: FqName): File {
internal fun fileFromFqName(baseDir: File, fqName: FqName): File {
val fileRelativePath = fqName.asString().replace(".", "/") + ".kt"
return File(baseDir, fileRelativePath)
}
private fun Printer.generateInterface(type: FqName, properties: List<KProperty1<*, *>>, parentType: FqName? = null) {
private fun Printer.generateInterface(
type: FqName,
properties: List<KProperty1<*, *>>,
parentType: FqName? = null,
) {
val afterType = parentType?.let { " : $it" }
generateDeclaration("interface", type, afterType = afterType) {
for (property in properties) {
println()
generateDoc(property)
generateOptionDeprecation(property)
generatePropertyDeclaration(property)
generatePropertyProvider(property)
}
}
}
private fun Printer.generateDeprecatedInterface(
type: FqName,
compilerOptionType: FqName,
properties: List<KProperty1<*, *>>,
parentType: FqName? = null,
) {
val afterType = parentType?.let { " : $it" }
val modifier = """
@Deprecated("Use ${compilerOptionType.shortName()} instead", level = DeprecationLevel.WARNING)
interface
""".trimIndent()
generateDeclaration(modifier, type, afterType = afterType) {
println("${if (parentType != null) "override " else ""}val options: $compilerOptionType")
properties.forEach {
println()
generatePropertyGetterAndSetter(it)
}
}
}
private fun Printer.generateImpl(
type: FqName,
parentImplFqName: FqName?,
parentType: FqName,
argsType: FqName,
properties: List<KProperty1<*, *>>
) {
generateDeclaration("internal abstract class", type, afterType = ": $parentType") {
fun KProperty1<*, *>.backingField(): String = "${this.name}Field"
val modifiers = "internal abstract class"
val afterType = if (parentImplFqName != null) {
": $parentImplFqName(objectFactory), $parentType"
} else {
": $parentType"
}
generateDeclaration(
modifiers,
type,
constructorDeclaration = "@javax.inject.Inject constructor(\n objectFactory: org.gradle.api.model.ObjectFactory\n)",
afterType = afterType
) {
for (property in properties) {
println()
val propertyType = property.gradleReturnType
if (propertyType.endsWith("?")) {
generateOptionDeprecation(property)
generatePropertyDeclaration(property, modifiers = "override", value = "null")
} else {
val backingField = property.backingField()
val visibilityModified = property.gradleBackingFieldVisibility.name.lowercase(Locale.US)
println("$visibilityModified var $backingField: $propertyType? = null")
generateOptionDeprecation(property)
generatePropertyDeclaration(property, modifiers = "override")
withIndent {
println("get() = $backingField ?: ${property.gradleDefaultValue}")
println("set(value) {")
withIndent { println("$backingField = value") }
println("}")
}
}
generatePropertyProviderImpl(property)
}
println()
println("internal open fun updateArguments(args: $argsType) {")
println("internal fun fillCompilerArguments(args: $argsType) {")
withIndent {
if (parentImplFqName != null) println("super.fillCompilerArguments(args)")
for (property in properties) {
val backingField = if (property.gradleReturnType.endsWith("?")) property.name else property.backingField()
println("$backingField?.let { args.${property.name} = it }")
val defaultValue = property.gradleValues
if (property.name != "freeCompilerArgs") {
val getter = if (property.gradleReturnType.endsWith("?")) ".orNull" else ".get()"
val toArg = defaultValue.toArgumentConverter?.substringAfter("this") ?: ""
println("args.${property.name} = ${property.name}$getter$toArg")
} else {
println("args.freeArgs += ${property.name}.get()")
}
}
addAdditionalJvmArgs(type)
}
println("}")
println()
println("internal fun fillDefaultValues(args: $argsType) {")
withIndent {
if (parentImplFqName != null) println("super.fillDefaultValues(args)")
properties
.filter { it.name != "freeCompilerArgs" }
.forEach {
val defaultValue = it.gradleValues
var value = defaultValue.defaultValue
if (value != "null" && defaultValue.toArgumentConverter != null) {
value = "$value${defaultValue.toArgumentConverter!!.substringAfter("this")}"
}
println("args.${it.name} = $value")
}
addAdditionalJvmArgs(type)
}
println("}")
}
}
println()
println("internal fun $argsType.fillDefaultValues() {")
withIndent {
for (property in properties) {
println("${property.name} = ${property.gradleDefaultValue}")
}
// Adding required 'noStdlib' and 'noReflect' compiler arguments for JVM compilation
// Otherwise compilation via build tools will fail
if (type.shortName().toString() == "KotlinJvmOptionsBase") {
println("noStdlib = true")
println("noReflect = true")
}
private fun Printer.addAdditionalJvmArgs(implType: FqName) {
// Adding required 'noStdlib' and 'noReflect' compiler arguments for JVM compilation
// Otherwise compilation via build tools will fail
if (implType.shortName().toString() == "CompilerJvmOptions$IMPLEMENTATION_SUFFIX") {
println()
println("// Arguments with always default values when used from build tools")
println("args.noStdlib = true")
println("args.noReflect = true")
}
println("}")
}
internal fun Printer.generateDeclaration(
modifiers: String,
type: FqName,
constructorDeclaration: String? = null,
afterType: String? = null,
generateBody: Printer.() -> Unit
) {
println("// DO NOT EDIT MANUALLY!")
println("// Generated by org/jetbrains/kotlin/generators/arguments/GenerateGradleOptions.kt")
println(
"""
// DO NOT EDIT MANUALLY!
// Generated by org/jetbrains/kotlin/generators/arguments/GenerateGradleOptions.kt
// To regenerate run 'generateGradleOptions' task
@file:Suppress("RemoveRedundantQualifierName", "Deprecation", "DuplicatedCode")
""".trimIndent()
)
if (!type.parent().isRoot) {
println("package ${type.parent()}")
println()
}
println("@Suppress(\"DEPRECATION\")")
print("$modifiers ${type.shortName()} ")
afterType?.let { print("$afterType ") }
println("{")
print("$modifiers ${type.shortName()}")
constructorDeclaration?.let { print(" $it ") }
afterType?.let { print("$afterType") }
println(" {")
withIndent {
generateBody()
}
println("}")
}
private fun Printer.generatePropertyDeclaration(property: KProperty1<*, *>, modifiers: String = "", value: String? = null) {
val returnType = property.gradleReturnType
val initialValue = if (value != null) " = $value" else ""
println("$modifiers var ${property.name}: $returnType$initialValue")
private fun Printer.generatePropertyProvider(
property: KProperty1<*, *>,
modifiers: String = ""
) {
if (property.gradleDefaultValue == "null" &&
property.gradleInputType == GradleInputTypes.INPUT
) {
println("@get:org.gradle.api.tasks.Optional")
}
println("@get:${property.gradleInputType}")
println("${modifiers.appendWhitespaceIfNotBlank}val ${property.name}: ${property.gradleLazyReturnType}")
}
private fun Printer.generatePropertyProviderImpl(
property: KProperty1<*, *>,
modifiers: String = ""
) {
generateOptionDeprecation(property)
println(
"override ${modifiers.appendWhitespaceIfNotBlank}val ${property.name}: ${property.gradleLazyReturnType} ="
)
withIndent {
val convention = if (property.gradleDefaultValue != "null") {
".convention(${property.gradleDefaultValue})"
} else {
""
}
println(
"objectFactory${property.gradleLazyReturnTypeInstantiator}$convention"
)
}
}
private fun Printer.generatePropertyGetterAndSetter(
property: KProperty1<*, *>,
modifiers: String = "",
) {
val defaultValue = property.gradleValues
val returnType = property.gradleReturnType
if (defaultValue.type != defaultValue.kotlinOptionsType) {
assert(defaultValue.fromKotlinOptionConverterProp != null)
assert(defaultValue.toKotlinOptionConverterProp != null)
}
if (defaultValue.fromKotlinOptionConverterProp != null) {
println("private val ${defaultValue.kotlinOptionsType}.${property.name}CompilerOption get() = ${defaultValue.fromKotlinOptionConverterProp}")
println()
println("private val ${defaultValue.type}.${property.name}KotlinOption get() = ${defaultValue.toKotlinOptionConverterProp}")
println()
}
generateDoc(property)
generateOptionDeprecation(property)
println("${modifiers.appendWhitespaceIfNotBlank}var ${property.name}: $returnType")
val propGetter = if (returnType.endsWith("?")) ".orNull" else ".get()"
val getter = if (defaultValue.fromKotlinOptionConverterProp != null) {
"$propGetter.${property.name}KotlinOption"
} else {
propGetter
}
val setter = if (defaultValue.toKotlinOptionConverterProp != null) {
".set(value.${property.name}CompilerOption)"
} else {
".set(value)"
}
withIndent {
println("get() = options.${property.name}$getter")
println("set(value) = options.${property.name}$setter")
}
}
private val String.appendWhitespaceIfNotBlank get() = if (isNotBlank()) "$this " else ""
private fun Printer.generateOptionDeprecation(property: KProperty1<*, *>) {
property.findAnnotation<GradleDeprecatedOption>()
?.let { DeprecatedOptionAnnotator.generateOptionAnnotation(it) }
@@ -335,15 +731,6 @@ private val KProperty1<*, *>.gradleValues: DefaultValues
private val KProperty1<*, *>.gradleDefaultValue: String
get() = gradleValues.defaultValue
private val KProperty1<*, *>.gradleBackingFieldVisibility: KVisibility
get() {
val fieldVisibility = findAnnotation<GradleOption>()!!.backingFieldVisibility
require(fieldVisibility != KVisibility.PUBLIC) {
"Backing field should not have public visibility!"
}
return fieldVisibility
}
private val KProperty1<*, *>.gradleReturnType: String
get() {
// Set nullability based on Gradle default value
@@ -354,6 +741,39 @@ private val KProperty1<*, *>.gradleReturnType: String
return type
}
private val KProperty1<*, *>.gradleLazyReturnType: String
get() {
val returnType = gradleValues.type
val classifier = returnType.classifier
return when {
classifier is KClass<*> && classifier == List::class ->
"org.gradle.api.provider.ListProperty<${returnType.arguments.first().type!!.withNullability(false)}>"
classifier is KClass<*> && classifier == Set::class ->
"org.gradle.api.provider.SetProperty<${returnType.arguments.first().type!!.withNullability(false)}>"
classifier is KClass<*> && classifier == Map::class ->
"org.gradle.api.provider.MapProperty<${returnType.arguments[0]}, ${returnType.arguments[1]}"
else -> "org.gradle.api.provider.Property<${returnType.withNullability(false)}>"
}
}
private val KProperty1<*, *>.gradleLazyReturnTypeInstantiator: String
get() {
val returnType = gradleValues.type
val classifier = returnType.classifier
return when {
classifier is KClass<*> && classifier == List::class ->
".listProperty(${returnType.arguments.first().type!!.withNullability(false)}::class.java)"
classifier is KClass<*> && classifier == Set::class ->
".setProperty(${returnType.arguments.first().type!!.withNullability(false)}::class.java)"
classifier is KClass<*> && classifier == Map::class ->
".mapProperty(${returnType.arguments[0]}::class.java, ${returnType.arguments[1]}::class.java)"
else -> ".property(${returnType.withNullability(false)}::class.java)"
}
}
private val KProperty1<*, *>.gradleInputType: String get() =
findAnnotation<GradleOption>()!!.gradleInputType
private inline fun <reified T> KAnnotatedElement.findAnnotation(): T? =
annotations.filterIsInstance<T>().firstOrNull()
@@ -15,7 +15,7 @@ internal fun generateJsMainFunctionExecutionMode(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val modeFqName = FqName("org.jetbrains.kotlin.gradle.dsl.JsMainFunctionExecutionMode")
filePrinter(file(apiDir, modeFqName)) {
filePrinter(fileFromFqName(apiDir, modeFqName)) {
generateDeclaration("enum class", modeFqName, afterType = "(val mode: String)") {
val modes = hashMapOf(
K2JsArgumentConstants::CALL.name to K2JsArgumentConstants.CALL,
@@ -45,7 +45,7 @@ internal fun generateJsModuleKind(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val jsModuleKindFqName = FqName("org.jetbrains.kotlin.gradle.dsl.JsModuleKind")
filePrinter(file(apiDir, jsModuleKindFqName)) {
filePrinter(fileFromFqName(apiDir, jsModuleKindFqName)) {
generateDeclaration("enum class", jsModuleKindFqName, afterType = "(val kind: String)") {
val kinds = hashMapOf(
K2JsArgumentConstants::MODULE_PLAIN.name to K2JsArgumentConstants.MODULE_PLAIN,
@@ -78,7 +78,7 @@ internal fun generateJsSourceMapEmbedMode(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val jsSourceMapEmbedKindFqName = FqName("org.jetbrains.kotlin.gradle.dsl.JsSourceMapEmbedMode")
filePrinter(file(apiDir, jsSourceMapEmbedKindFqName)) {
filePrinter(fileFromFqName(apiDir, jsSourceMapEmbedKindFqName)) {
generateDeclaration("enum class", jsSourceMapEmbedKindFqName, afterType = "(val mode: String)") {
val modes = hashMapOf(
K2JsArgumentConstants::SOURCE_MAP_SOURCE_CONTENT_ALWAYS.name to K2JsArgumentConstants.SOURCE_MAP_SOURCE_CONTENT_ALWAYS,
@@ -109,7 +109,7 @@ internal fun generateJsDiagnosticMode(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val diagnosticModeFqName = FqName("org.jetbrains.kotlin.gradle.dsl.JsDiagnosticMode")
filePrinter(file(apiDir, diagnosticModeFqName)) {
filePrinter(fileFromFqName(apiDir, diagnosticModeFqName)) {
generateDeclaration("enum class", diagnosticModeFqName, afterType = "(val mode: String)") {
val mods = hashMapOf(
K2JsArgumentConstants::RUNTIME_DIAGNOSTIC_EXCEPTION.name to K2JsArgumentConstants.RUNTIME_DIAGNOSTIC_EXCEPTION,
@@ -15,7 +15,7 @@ internal fun generateJvmTarget(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val jvmTargetFqName = FqName("org.jetbrains.kotlin.gradle.dsl.JvmTarget")
filePrinter(file(apiDir, jvmTargetFqName)) {
filePrinter(fileFromFqName(apiDir, jvmTargetFqName)) {
generateDeclaration("enum class", jvmTargetFqName, afterType = "(val target: String)") {
val jvmTargetValues = JvmTarget.values()
val deprecatedJvmTargetValues = JvmTarget.values().subtract(JvmTarget.supportedValues().toSet())
@@ -19,7 +19,7 @@ internal fun generateKotlinVersion(
filePrinter: (targetFile: File, Printer.() -> Unit) -> Unit
) {
val kotlinVersionFqName = FqName("org.jetbrains.kotlin.gradle.dsl.KotlinVersion")
filePrinter(file(apiDir, kotlinVersionFqName)) {
filePrinter(fileFromFqName(apiDir, kotlinVersionFqName)) {
generateDeclaration("enum class", kotlinVersionFqName, afterType = "(val version: String)") {
val languageVersions = LanguageVersion.values()