Skip to content

Commit

Permalink
Added kotlin support (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
germanosin authored Dec 24, 2024
1 parent 0f72240 commit 81e5208
Show file tree
Hide file tree
Showing 25 changed files with 4,116 additions and 26 deletions.
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies {
implementation("org.pkl-lang:pkl-commons-cli:${pklVersion}")
implementation("org.pkl-lang:pkl-commons:${pklVersion}")
implementation("com.palantir.javapoet:javapoet:0.6.0")
implementation("com.squareup:kotlinpoet:1.6.0")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.10")

testApi("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testApi("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
Expand All @@ -27,6 +29,10 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

testImplementation("org.assertj:assertj-core:3.26.3")

testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10")
testRuntimeOnly("org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.10")
testRuntimeOnly("org.jetbrains.kotlin:kotlin-script-util:1.7.10")
}

gradlePlugin {
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/io/hpkl/gradle/PklPojoExtension.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.hpkl.gradle

import io.hpkl.gradle.spec.JavaCodeGenSpec
import io.hpkl.gradle.spec.KotlinCodeGenSpec
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.model.ObjectFactory
Expand All @@ -11,9 +12,18 @@ open class PklPojoExtension @Inject constructor(objects: ObjectFactory) {
var javaCodeGenerators: NamedDomainObjectContainer<JavaCodeGenSpec>
= objects.domainObjectContainer(JavaCodeGenSpec::class.java)

var kotlinCodeGenerators: NamedDomainObjectContainer<KotlinCodeGenSpec>
= objects.domainObjectContainer(KotlinCodeGenSpec::class.java)

fun javaCodeGenerators(
action: Action<NamedDomainObjectContainer<JavaCodeGenSpec>>
) {
action.execute(javaCodeGenerators)
}

fun kotlinCodeGenerators(
action: Action<NamedDomainObjectContainer<KotlinCodeGenSpec>>
) {
action.execute(kotlinCodeGenerators)
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/io/hpkl/gradle/PklPojoGradlePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ class PklPojoGradlePlugin : Plugin<Project> {
)

project.tasks.findByName("compileJava")?.dependsOn(task.name)
project.tasks.findByName("compileKotlin")?.dependsOn(task.name)
}
}
9 changes: 5 additions & 4 deletions src/main/kotlin/io/hpkl/gradle/cli/CliJavaCodeGenerator.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.hpkl.gradle.cli

import io.hpkl.gradle.codegen.JavaCodeGenerator
import io.hpkl.gradle.codegen.JavaCodeGeneratorException
import io.hpkl.gradle.codegen.java.JavaCodeGenerator
import io.hpkl.gradle.codegen.java.JavaCodeGeneratorException
import org.pkl.commons.cli.CliCommand
import org.pkl.commons.cli.CliException
import org.pkl.commons.createParentDirectories
Expand All @@ -18,8 +18,9 @@ class CliJavaCodeGenerator(private val options: CliJavaCodeGeneratorOptions)
try {
builder.build().use { evaluator ->
for (moduleUri in options.base.normalizedSourceModules) {
val schema = evaluator.evaluateSchema(ModuleSource.uri(moduleUri))
val codeGenerator = JavaCodeGenerator(schema, options.toJavaCodeGeneratorOptions())
val moduleSource = ModuleSource.uri(moduleUri)
val schema = evaluator.evaluateSchema(moduleSource)
val codeGenerator = JavaCodeGenerator(schema, moduleSource, options.toJavaCodeGeneratorOptions())
try {
for ((fileName, fileContents) in codeGenerator.output) {
val outputFile = options.outputDir.resolve(fileName)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.hpkl.gradle.cli

import io.hpkl.gradle.codegen.JavaCodeGeneratorOptions
import io.hpkl.gradle.codegen.java.JavaCodeGeneratorOptions
import org.pkl.commons.cli.CliBaseOptions
import java.nio.file.Path

Expand Down
43 changes: 43 additions & 0 deletions src/main/kotlin/io/hpkl/gradle/cli/CliKotlinCodeGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.hpkl.gradle.cli

import io.hpkl.gradle.codegen.kotlin.KotlinCodeGenerator
import io.hpkl.gradle.codegen.kotlin.KotlinCodeGeneratorException
import org.pkl.commons.cli.CliCommand
import org.pkl.commons.cli.CliException
import org.pkl.commons.createParentDirectories
import org.pkl.commons.writeString
import org.pkl.core.Closeables
import org.pkl.core.ModuleSource
import java.io.IOException

class CliKotlinCodeGenerator(private val options: CliKotlinCodeGeneratorOptions)
: CliCommand(options.base) {

override fun doRun() {
val builder = evaluatorBuilder()
try {
builder.build().use { evaluator ->
for (moduleUri in options.base.normalizedSourceModules) {
val moduleSource = ModuleSource.uri(moduleUri)
val schema = evaluator.evaluateSchema(moduleSource)
val codeGenerator = KotlinCodeGenerator(schema, moduleSource, options.toKotlinCodeGeneratorOptions())
try {
for ((fileName, fileContents) in codeGenerator.output) {
val outputFile = options.outputDir.resolve(fileName)
try {
outputFile.createParentDirectories().writeString(fileContents)
} catch (e: IOException) {
throw CliException("I/O error writing file `$outputFile`.\nCause: ${e.message}")
}
}
} catch (e: KotlinCodeGeneratorException) {
throw CliException(e.message!!)
}
}
}
} finally {
Closeables.closeQuietly(builder.moduleKeyFactories)
Closeables.closeQuietly(builder.resourceReaders)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.hpkl.gradle.cli

import io.hpkl.gradle.codegen.java.JavaCodeGeneratorOptions
import io.hpkl.gradle.codegen.kotlin.KotlinCodeGeneratorOptions
import org.pkl.commons.cli.CliBaseOptions
import java.nio.file.Path

class CliKotlinCodeGeneratorOptions (
/** Base options shared between CLI commands. */
val base: CliBaseOptions,

/** The directory where generated source code is placed. */
val outputDir: Path,

/** The characters to use for indenting generated source code. */
val indent: String = " ",

val durationClass : String,

val dataSizeClass : String,

val durationUnitClass : String,

val dataSizeUnitClass : String,

val mutableObjects: Boolean = false,

/** Whether to generate Javadoc based on doc comments for Pkl modules, classes, and properties. */
val generateKdoc: Boolean = false,

/** Whether to generate config classes for use with Spring Boot. */
val generateSpringBootConfig: Boolean = false,

val springConfigAnnotation: String = "SpringConfigProperties",

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

/**
* A rename mapping for class names.
*
* When you need to have Java class or package names different from the default names derived from
* Pkl module names, you can define a rename mapping, where the key is a prefix of the original
* Pkl module name, and the value is the desired replacement.
*/
val renames: Map<String, String> = emptyMap(),

val generateAnnotationClasses: Boolean = false,

val setDefaultValues: Boolean = false,

val durationClassConverter: String? = null,

val dataSizeConverter: String? = null
) {
@Suppress("DeprecatedCallableAddReplaceWith")
@Deprecated("deprecated without replacement")
fun toKotlinCodegenOptions() = toKotlinCodeGeneratorOptions()

internal fun toKotlinCodeGeneratorOptions() =
KotlinCodeGeneratorOptions(
indent = indent,
durationClass = durationClass,
durationUnitClass = durationUnitClass,
dataSizeClass = dataSizeClass,
dataSizeUnitClass =dataSizeUnitClass,
generateKdoc = generateKdoc,
mutableObjects = mutableObjects,
generateSpringBootConfig = generateSpringBootConfig,
springConfigAnnotation = springConfigAnnotation,
implementSerializable = implementSerializable,
renames = renames,
generateAnnotationClasses = generateAnnotationClasses,
setDefaultValues = setDefaultValues,
baseCliBaseOptions = base,
durationClassConverter = durationClassConverter,
dataSizeConverter = dataSizeConverter
)
}
38 changes: 27 additions & 11 deletions src/main/kotlin/io/hpkl/gradle/codegen/DefaultValueReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,47 @@ import org.pkl.core.PClass

class DefaultValueReader {

fun findFefaultValues(options: CliBaseOptions, moduleSource: ModuleSource, pClass: PClass?): Map<String, Any>? {
val runner = Runner(options, moduleSource, pClass)
fun findDefaultValues(options: CliBaseOptions,
moduleSource: ModuleSource,
pClass: PClass,
isModuleClass: Boolean): Map<String, Any>? {
val runner = Runner(options, moduleSource, pClass, isModuleClass)
runner.run()
return runner.properties
}

class Runner(options: CliBaseOptions,
val moduleSource: ModuleSource,
private val pClass: PClass?) : CliCommand(options) {
private val pClass: PClass,
private val isModuleClass: Boolean) : CliCommand(options) {

var properties: Map<String, Any>? = null

override fun doRun() {
val builder = evaluatorBuilder()
try {
builder.build().use { evaluator ->
properties = evaluator.evaluateExpression(
moduleSource,
String.format(
"""
try {
properties = evaluator.evaluateExpression(
moduleSource,
if (pClass.isModuleClass) {
String.format(
"""
import("pkl:reflect").Class(module.getClass())
.properties.map((k,v) -> Pair(k, v.defaultValue))
""".trimIndent()
)
} else {
String.format(
"""
import("pkl:reflect").Class(%s).properties.map((k,v) -> Pair(k, v.defaultValue))
""".trimIndent(),
pClass?.simpleName ?: "module.getClass()"
)
) as Map<String, Any>
""".trimIndent(), pClass.simpleName
)
}
) as Map<String, Any>
} catch (t: Throwable) {
// TODO: Skip evaluate errors
}
}
} finally {
Closeables.closeQuietly(builder.moduleKeyFactories)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.hpkl.gradle.codegen
package io.hpkl.gradle.codegen.java

import com.palantir.javapoet.*
import io.hpkl.gradle.codegen.DefaultValueReader
import org.pkl.commons.NameMapper
import org.pkl.core.*
import org.pkl.core.util.CodeGeneratorUtils
Expand All @@ -12,13 +13,14 @@ class JavaCodeGeneratorException(message: String) : RuntimeException(message)

class JavaCodeGenerator(
private val schema: ModuleSchema,
private val moduleSource: ModuleSource,
private val codegenOptions: JavaCodeGeneratorOptions
) {

private val nameMapper = NameMapper(codegenOptions.renames)
private val defaultValueReader = DefaultValueReader()

private val defaultValueGenerator: DefaultValueGenerator = DefaultValueGenerator(
private val defaultValueGenerator: JavaValueGenerator = JavaValueGenerator(
codegenOptions, nameMapper
)

Expand Down Expand Up @@ -524,10 +526,11 @@ class JavaCodeGenerator(

val defaultValues : Map<String, Any>? =
if (codegenOptions.setDefaultValues)
defaultValueReader.findFefaultValues(
defaultValueReader.findDefaultValues(
codegenOptions.baseCliBaseOptions,
ModuleSource.uri(schema.moduleUri),
if (!isModuleClass) pClass else null
moduleSource,
pClass,
isModuleClass
)
else null

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.hpkl.gradle.codegen
package io.hpkl.gradle.codegen.java

import org.pkl.commons.cli.CliBaseOptions

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.hpkl.gradle.codegen
package io.hpkl.gradle.codegen.java

import com.palantir.javapoet.ClassName
import com.palantir.javapoet.CodeBlock
Expand All @@ -7,7 +7,7 @@ import org.pkl.core.*
import org.pkl.core.util.CodeGeneratorUtils
import java.util.*

class DefaultValueGenerator(
class JavaValueGenerator(
private val options: JavaCodeGeneratorOptions,
private val nameMapper: NameMapper
) {
Expand Down
Loading

0 comments on commit 81e5208

Please sign in to comment.