Skip to content

Commit

Permalink
Implement support for KotlinX Serialization
Browse files Browse the repository at this point in the history
This change adds support for a new code-gen argument,
`implementKSerializable`, which results in the annotation
`kotlinx.serialization.Serializable` being added to `data`
classes during codegen.

Relates to discussion #185

- feat(codegen): add support for kotlin `Serializable` annotation
- feat(gradle): add `implementKSerializable` argument
- test(codegen): add test for KotlinX serialization support
- test(codegen): add test for both Java and KotlinX serialization
- test(gradle): add test for compiling with KotlinX serialization

Signed-off-by: Sam Gammon <[email protected]>
  • Loading branch information
sgammon committed Feb 20, 2024
1 parent 44158f8 commit 073c121
Show file tree
Hide file tree
Showing 20 changed files with 281 additions and 9 deletions.
1 change: 1 addition & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {
implementation(libs.kotlinPlugin) {
exclude(module = "kotlin-android-extensions")
}
implementation(libs.kotlinPluginSerialization)
implementation(libs.shadowPlugin)
}

Expand Down
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/pklKotlinLibrary.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id("pklJavaLibrary")

kotlin("jvm")
kotlin("plugin.serialization")
}

val buildInfo = project.extensions.getByType<BuildInfo>()
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ kotlinCompilerEmbeddable = { group = "org.jetbrains.kotlin", name = "kotlin-comp
kotlinScriptingCompilerEmbeddable = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-compiler-embeddable", version.ref = "kotlin" }
kotlinScriptUtil = { group = "org.jetbrains.kotlin", name = "kotlin-script-util", version.ref = "kotlin" }
kotlinPlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
kotlinPluginSerialization = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" }
kotlinPoet = { group = "com.squareup", name = "kotlinpoet", version.ref = "kotlinPoet" }
kotlinReflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
kotlinStdLib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" }
kotlinxHtml = { group = "org.jetbrains.kotlinx", name = "kotlinx-html-jvm", version.ref = "kotlinxHtml" }
kotlinxSerializationCore = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinxSerialization" }
kotlinxSerializationJson = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" }
log4j12Api = { group = "org.apache.logging.log4j", name = "log4j-1.2-api", version.ref = "log4j" }
nuValidator = { group = "nu.validator", name = "validator", version.ref = "nuValidator" }
Expand Down
6 changes: 6 additions & 0 deletions pkl-cli/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.10=kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-native-utils:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-project-model:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-reflect:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-reflect:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-script-runtime:1.6.10=spotless1316845978
Expand All @@ -44,6 +47,7 @@ org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspat
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-serialization:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10=spotless1316845978
Expand All @@ -52,6 +56,8 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=apiDependenciesMetadata,compileCl
org.jetbrains.kotlin:kotlin-stdlib:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-test:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-tooling-core:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-util-io:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains:annotations:13.0=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,spotless1316845978,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jline:jline-native:3.25.1=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jline:jline-reader:3.25.1=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
Expand Down
6 changes: 6 additions & 0 deletions pkl-codegen-java/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.10=kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-native-utils:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-project-model:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-reflect:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-reflect:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-script-runtime:1.6.10=spotless1316845978
Expand All @@ -40,6 +43,7 @@ org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspat
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-serialization:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10=spotless1316845978
Expand All @@ -48,6 +52,8 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=apiDependenciesMetadata,compileCl
org.jetbrains.kotlin:kotlin-stdlib:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-test:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-tooling-core:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-util-io:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains:annotations:13.0=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,spotless1316845978,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.junit.jupiter:junit-jupiter-engine:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
Expand Down
9 changes: 9 additions & 0 deletions pkl-codegen-kotlin/gradle.lockfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,k
org.jetbrains.kotlin:kotlin-daemon-client:1.7.10=testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.10=kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-native-utils:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-project-model:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-reflect:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-reflect:1.7.10=compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-script-runtime:1.6.10=spotless1316845978
Expand All @@ -42,6 +45,7 @@ org.jetbrains.kotlin:kotlin-scripting-common:1.7.10=kotlinCompilerPluginClasspat
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlin:kotlin-serialization:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10=spotless1316845978
Expand All @@ -50,7 +54,12 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10=apiDependenciesMetadata,compileCl
org.jetbrains.kotlin:kotlin-stdlib:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-stdlib:1.7.10=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlin:kotlin-test:1.6.10=spotless1316845978
org.jetbrains.kotlin:kotlin-tooling-core:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-util-io:1.7.10=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.0=testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.5.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.5.0=compileClasspath,default,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.0=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath
org.jetbrains:annotations:13.0=apiDependenciesMetadata,compileClasspath,default,implementationDependenciesMetadata,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,spotless1316845978,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.junit.jupiter:junit-jupiter-api:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
org.junit.jupiter:junit-jupiter-engine:5.10.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata
Expand Down
4 changes: 4 additions & 0 deletions pkl-codegen-kotlin/pkl-codegen-kotlin.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ dependencies {

implementation(libs.kotlinPoet)
implementation(libs.kotlinReflect)
implementation(libs.kotlinxSerializationCore) {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
}

testImplementation(project(":pkl-config-kotlin"))
testImplementation(project(":pkl-commons-test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ data class CliKotlinCodeGeneratorOptions(
val generateSpringBootConfig: Boolean = false,

/** Whether to make generated classes implement [java.io.Serializable] */
val implementSerializable: Boolean = false
val implementSerializable: Boolean = false,

/** Whether to annotate generated data classes with [kotlinx.serialization.Serializable] */
val implementKSerializable: Boolean = false
) {
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
KotlinCodegenOptions(
Expand All @@ -48,5 +51,6 @@ data class CliKotlinCodeGeneratorOptions(
generateSpringBootConfig = generateSpringBootConfig,
implementSerializable = implementSerializable,
kotlinPackage = kotlinPackage,
implementKSerializable = implementKSerializable,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import java.io.StringWriter
import java.net.URI
import java.util.*
import kotlinx.serialization.Serializable
import org.pkl.core.*
import org.pkl.core.util.CodeGeneratorUtils

Expand All @@ -37,7 +38,10 @@ data class KotlinCodegenOptions(
val generateSpringBootConfig: Boolean = false,

/** Whether to make generated classes implement [java.io.Serializable] */
val implementSerializable: Boolean = false
val implementSerializable: Boolean = false,

/** Whether to annotate data classes with [kotlinx.serialization.Serializable] */
val implementKSerializable: Boolean = false,
)

class KotlinCodeGeneratorException(message: String) : RuntimeException(message)
Expand Down Expand Up @@ -138,7 +142,14 @@ class KotlinCodeGenerator(
}

for (pClass in moduleSchema.classes.values) {
moduleType.addType(generateTypeSpec(pClass, moduleSchema).ensureSerializable().build())
moduleType.addType(
generateTypeSpec(pClass, moduleSchema)
.apply {
ensureSerializable()
ensureKSerializable()
}
.build()
)
}

// generate append method for module classes w/o parent class; reuse in subclasses and nested
Expand Down Expand Up @@ -522,6 +533,18 @@ class KotlinCodeGenerator(
else generateRegularClass()
}

private fun TypeSpec.Builder.ensureKSerializable(): TypeSpec.Builder {
if (!options.implementKSerializable) {
return this
}

val spec = AnnotationSpec.builder(Serializable::class).build()
if (!this.annotationSpecs.contains(spec)) {
addAnnotation(spec)
}
return this
}

private fun TypeSpec.Builder.ensureSerializable(): TypeSpec.Builder {
if (!options.implementSerializable) {
return this
Expand Down
Loading

0 comments on commit 073c121

Please sign in to comment.