From 3ca440636e6fdc420b77839b77f91af7a80dbf10 Mon Sep 17 00:00:00 2001 From: Guillermo Mazzola Date: Thu, 28 Dec 2023 15:50:18 +0100 Subject: [PATCH] Exposed `expression` API and generics `*/?` basic support --- README.md | 9 +++--- demo-project/groovy-gen-kotlin/build.gradle | 2 ++ demo-project/groovy/build.gradle | 2 ++ .../demos/groovy/BuildConfigBaseTest.java | 2 ++ demo-project/kts-gen-java/build.gradle.kts | 14 +++----- demo-project/kts/build.gradle.kts | 10 +++--- .../buildconfig/BuildConfigClassSpec.kt | 32 +++++++++++++++++-- .../buildconfig/BuildConfigTypeUtils.kt | 24 +++++++++----- .../generators/BuildConfigJavaGenerator.kt | 2 ++ .../generators/BuildConfigKotlinGenerator.kt | 2 ++ .../kotlin/dsl/BuildConfigClassSpecDSL.kt | 21 ++++++++++++ 11 files changed, 89 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 09d1209..7cbcf0f 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ Designed for KTS scripts, with *experimental* support for Kotlin's **multi-platf On your `build.gradle.kts` add: ```kotlin plugins { - id("org.jetbrains.kotlin.jvm") version embeddedKotlinVersion - id("com.github.gmazzo.buildconfig") version + id("org.jetbrains.kotlin.jvm") version "" + id("com.github.gmazzo.buildconfig") version "" } buildConfig { @@ -25,9 +25,7 @@ buildConfig { buildConfigField("FEATURE_ENABLED", true) buildConfigField("MAGIC_NUMBERS", intArrayOf(1, 2, 3, 4)) buildConfigField("STRING_LIST", arrayOf("a", "b", "c")) - buildConfigField>("MAP") { - expression("mapOf(\"a\" to 1, \"b\" to 2)") - } + buildConfigField>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)")) buildConfigField("com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", "SomeData(\"a\", 1)") } @@ -67,6 +65,7 @@ buildConfig { buildConfigField(int[], "MAGIC_NUMBERS", [1, 2, 3]) buildConfigField('List', "STRING_LIST", ["a", "b", "c"]) buildConfigField("java.util.Map", "MAP", "java.util.Map.of(\"a\", 1, \"b\", 2)") + buildConfigField(Map.class, "GENERIC_MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) buildConfigField("com.github.gmazzo.buildconfig.demos.groovy.SomeData", "DATA", "new SomeData(\"a\", 1)") } ``` diff --git a/demo-project/groovy-gen-kotlin/build.gradle b/demo-project/groovy-gen-kotlin/build.gradle index a3f4cdb..8aff56e 100644 --- a/demo-project/groovy-gen-kotlin/build.gradle +++ b/demo-project/groovy-gen-kotlin/build.gradle @@ -170,6 +170,8 @@ buildConfig { "MAP_PROVIDER", provider { "mapOf(\"a\" to 1, \"b\" to 2)" } ) + buildConfigField(Map.class, "MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)")) + buildConfigField(Map.class, "MAP_GENERIC_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) buildConfigField( "com.github.gmazzo.buildconfig.demos.groovy.SomeData", "DATA", diff --git a/demo-project/groovy/build.gradle b/demo-project/groovy/build.gradle index 2b2b535..379dd22 100644 --- a/demo-project/groovy/build.gradle +++ b/demo-project/groovy/build.gradle @@ -170,6 +170,8 @@ buildConfig { "MAP_PROVIDER", provider { "java.util.Map.of(\"a\", 1, \"b\", 2)" } ) + buildConfigField(Map.class, "MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) + buildConfigField(Map.class, "MAP_GENERIC_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) buildConfigField( "com.github.gmazzo.buildconfig.demos.groovy.SomeData", "DATA", diff --git a/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java b/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java index 0a4387f..b89f258 100644 --- a/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java +++ b/demo-project/groovy/src/testFixtures/java/com/github/gmazzo/buildconfig/demos/groovy/BuildConfigBaseTest.java @@ -176,6 +176,8 @@ public void testBooleans() { public void testCustomTypes() { assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP); assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_PROVIDER); + assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC); + assertEquals(Map.of("a", 1, "b", 2), BuildConfig.MAP_GENERIC_PROVIDER); assertEquals(new SomeData("a", 1), BuildConfig.DATA); assertEquals(new SomeData("a", 1), BuildConfig.DATA_PROVIDER); } diff --git a/demo-project/kts-gen-java/build.gradle.kts b/demo-project/kts-gen-java/build.gradle.kts index 172c9a0..5b83eb6 100644 --- a/demo-project/kts-gen-java/build.gradle.kts +++ b/demo-project/kts-gen-java/build.gradle.kts @@ -157,16 +157,10 @@ buildConfig { buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) }) // custom formats with expressions, including Map and custom types - buildConfigField( - "java.util.Map", - "MAP", - "java.util.Map.of(\"a\", 1, \"b\", 2)" - ) - buildConfigField( - "java.util.Map", - "MAP_PROVIDER", - provider { "java.util.Map.of(\"a\", 1, \"b\", 2)" } - ) + buildConfigField>("MAP", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) + buildConfigField>("MAP_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) + buildConfigField>("MAP_GENERIC", expression("java.util.Map.of(\"a\", 1, \"b\", 2)")) + buildConfigField>("MAP_GENERIC_PROVIDER", provider { expression("java.util.Map.of(\"a\", 1, \"b\", 2)") }) buildConfigField( "com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", diff --git a/demo-project/kts/build.gradle.kts b/demo-project/kts/build.gradle.kts index 25b394d..d6ec977 100644 --- a/demo-project/kts/build.gradle.kts +++ b/demo-project/kts/build.gradle.kts @@ -172,12 +172,10 @@ buildConfig { buildConfigField("BOOLEAN_SET_PROVIDER", provider { setOf(true, null, false) }) // custom formats with expressions, including Map and custom types - buildConfigField>("MAP") { - expression("mapOf(\"a\" to 1, \"b\" to 2)") - } - buildConfigField>("MAP_PROVIDER") { - expression("mapOf(\"a\" to 1, \"b\" to 2)") - } + buildConfigField>("MAP", expression("mapOf(\"a\" to 1, \"b\" to 2)")) + buildConfigField>("MAP_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) + buildConfigField>("MAP_GENERIC", expression("mapOf(\"a\" to 1, \"b\" to 2)")) + buildConfigField>("MAP_GENERIC_PROVIDER", provider { expression("mapOf(\"a\" to 1, \"b\" to 2)") }) buildConfigField( "com.github.gmazzo.buildconfig.demos.kts.SomeData", "DATA", diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigClassSpec.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigClassSpec.kt index 8e9b0e3..6a3be93 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigClassSpec.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigClassSpec.kt @@ -72,6 +72,15 @@ interface BuildConfigClassSpec : Named { it.value(castToType(value, type) as Serializable?) } + fun buildConfigField( + type: Class<*>, + name: String, + expression: BuildConfigValue.Expression + ) = buildConfigField(name) { + it.type(type) + it.value.value(expression) + } + /* Because of erasure types on Groovy, this method is call for two use cases: - when `buildConfigField('File', 'NAME', provider { 'File("aFile")' })` is called @@ -87,7 +96,13 @@ interface BuildConfigClassSpec : Named { ) = buildConfigField(name) { it.type(type) it.value - .value(value.map { v -> if (v is String) BuildConfigValue.Expression(v) else BuildConfigValue.Literal(v) }) + .value(value.map { v -> + when (v) { + is BuildConfigValue -> v + is String -> BuildConfigValue.Expression(v) + else -> BuildConfigValue.Literal(v) + } + }) .disallowChanges() } @@ -97,7 +112,17 @@ interface BuildConfigClassSpec : Named { value: Provider, ) = buildConfigField(name) { it.type(type) - it.value(value.map { v -> castToType(v, type) as Serializable }) + it.value.value(value.map { v -> + when (v) { + is BuildConfigValue -> v + is String -> when (type) { + String::class.java -> BuildConfigValue.Literal(v) + else -> BuildConfigValue.Expression(v) + } + + else -> BuildConfigValue.Literal(castToType(v, type) as Serializable) + } + }) } fun buildConfigField( @@ -110,4 +135,7 @@ interface BuildConfigClassSpec : Named { } } + fun expression(expression: String) = + BuildConfigValue.Expression(expression) + } diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt index 9ef21a7..c52b153 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/BuildConfigTypeUtils.kt @@ -41,25 +41,32 @@ private val Type.genericName: String else -> error("Unsupported type: $this") } -private val Type.isArray get() = when (this) { - is Class<*> -> isArray - is GenericArrayType -> true - else -> false -} +private val Type.isArray + get() = when (this) { + is Class<*> -> isArray + is GenericArrayType -> true + else -> false + } -internal fun nameOf(type: Type): BuildConfigType = when(type) { +internal fun nameOf(type: Type): BuildConfigType = when (type) { is Class<*> -> BuildConfigType( className = type.genericName, - typeArguments = type.typeParameters.map { nameOf(it.genericDeclaration) }, + typeArguments = type.typeParameters.map { + nameOf(checkNotNull(it.bounds.singleOrNull()) { + "Types with complex parameters bounds are not supported: $type" + }) + }, nullable = false, array = type.isArray ) + is ParameterizedType -> BuildConfigType( className = type.rawType.genericName, typeArguments = type.actualTypeArguments.map { nameOf(it) }, nullable = false, array = false ) + is GenericArrayType -> BuildConfigType( className = type.genericComponentType.genericName, typeArguments = checkNotNull(type.genericComponentType as? ParameterizedType) { @@ -68,6 +75,7 @@ internal fun nameOf(type: Type): BuildConfigType = when(type) { nullable = false, array = true ) + else -> error("Unsupported type: $type") } @@ -77,7 +85,7 @@ internal fun nameOf(type: KType): BuildConfigType { return BuildConfigType( className = type.javaType.genericName, - typeArguments = targetType.arguments.map { nameOf(it.type!!) }, + typeArguments = targetType.arguments.map { it.type?.let(::nameOf) ?: nameOf("*") }, nullable = targetType.isMarkedNullable, array = isArray ) diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt index 30ecd67..9f38719 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigJavaGenerator.kt @@ -12,6 +12,7 @@ import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.WildcardTypeName import org.gradle.api.logging.Logging import org.gradle.api.tasks.Input import javax.lang.model.element.Modifier @@ -35,6 +36,7 @@ data class BuildConfigJavaGenerator( "string" -> STRING "list" -> LIST "set" -> SET + "*", "?" -> WildcardTypeName.get(Object::class.java) else -> ClassName.bestGuess(className) } if (typeArguments.isNotEmpty()) type = ParameterizedTypeName.get( diff --git a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt index 5a5a29f..85ea8e2 100644 --- a/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt +++ b/plugin/src/main/kotlin/com/github/gmazzo/buildconfig/generators/BuildConfigKotlinGenerator.kt @@ -30,6 +30,7 @@ import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.SET import com.squareup.kotlinpoet.SHORT import com.squareup.kotlinpoet.SHORT_ARRAY +import com.squareup.kotlinpoet.STAR import com.squareup.kotlinpoet.STRING import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.TypeSpec @@ -105,6 +106,7 @@ data class BuildConfigKotlinGenerator( "string" -> STRING "list" -> LIST "set" -> SET + "*", "?" -> STAR else -> ClassName.bestGuess(kotlinClassName) } if (typeArguments.isNotEmpty()) diff --git a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt index 719f1d6..3edfbed 100644 --- a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt +++ b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/BuildConfigClassSpecDSL.kt @@ -3,6 +3,7 @@ package org.gradle.kotlin.dsl import com.github.gmazzo.buildconfig.BuildConfigClassSpec import com.github.gmazzo.buildconfig.BuildConfigDsl import com.github.gmazzo.buildconfig.BuildConfigField +import com.github.gmazzo.buildconfig.BuildConfigValue import org.gradle.api.Action import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.provider.Provider @@ -36,6 +37,26 @@ inline fun BuildConfigClassSpec.buildConfigField( it.value(value) }) +@BuildConfigDsl +@JvmName("buildConfigFieldExpression") +inline fun BuildConfigClassSpec.buildConfigField( + name: String, + expression: BuildConfigValue.Expression, +): NamedDomainObjectProvider = buildConfigField(name, Action { + it.type(typeOf()) + it.value.set(expression) +}) + +@BuildConfigDsl +@JvmName("buildConfigFieldExpression") +inline fun BuildConfigClassSpec.buildConfigField( + name: String, + expression: Provider, +): NamedDomainObjectProvider = buildConfigField(name, Action { + it.type(typeOf()) + it.value.set(expression) +}) + @BuildConfigDsl @JvmName("buildConfigFieldArray") inline fun BuildConfigClassSpec.buildConfigField(