mirror of
https://github.com/apple/pkl.git
synced 2026-04-23 00:38:37 +02:00
Add setting for Kotlin package to codegen (#194)
This commit is contained in:
@@ -29,6 +29,9 @@ data class CliKotlinCodeGeneratorOptions(
|
|||||||
/** The characters to use for indenting generated source code. */
|
/** The characters to use for indenting generated source code. */
|
||||||
val indent: String = " ",
|
val indent: String = " ",
|
||||||
|
|
||||||
|
/** Kotlin package to use for generated code; if none is provided, the root package is used. */
|
||||||
|
val kotlinPackage: String = "",
|
||||||
|
|
||||||
/** Whether to generate Kdoc based on doc comments for Pkl modules, classes, and properties. */
|
/** Whether to generate Kdoc based on doc comments for Pkl modules, classes, and properties. */
|
||||||
val generateKdoc: Boolean = false,
|
val generateKdoc: Boolean = false,
|
||||||
|
|
||||||
@@ -39,5 +42,11 @@ data class CliKotlinCodeGeneratorOptions(
|
|||||||
val implementSerializable: Boolean = false
|
val implementSerializable: Boolean = false
|
||||||
) {
|
) {
|
||||||
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
|
fun toKotlinCodegenOptions(): KotlinCodegenOptions =
|
||||||
KotlinCodegenOptions(indent, generateKdoc, generateSpringBootConfig, implementSerializable)
|
KotlinCodegenOptions(
|
||||||
|
indent = indent,
|
||||||
|
generateKdoc = generateKdoc,
|
||||||
|
generateSpringBootConfig = generateSpringBootConfig,
|
||||||
|
implementSerializable = implementSerializable,
|
||||||
|
kotlinPackage = kotlinPackage,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ data class KotlinCodegenOptions(
|
|||||||
/** The characters to use for indenting generated Kotlin code. */
|
/** The characters to use for indenting generated Kotlin code. */
|
||||||
val indent: String = " ",
|
val indent: String = " ",
|
||||||
|
|
||||||
|
/** Kotlin package to use for generated code; if none is provided, the root package is used. */
|
||||||
|
val kotlinPackage: String = "",
|
||||||
|
|
||||||
/** Whether to generate KDoc based on doc comments for Pkl modules, classes, and properties. */
|
/** Whether to generate KDoc based on doc comments for Pkl modules, classes, and properties. */
|
||||||
val generateKdoc: Boolean = false,
|
val generateKdoc: Boolean = false,
|
||||||
|
|
||||||
@@ -111,6 +114,9 @@ class KotlinCodeGenerator(
|
|||||||
append("lin/${relativeOutputPathFor(moduleSchema.moduleName)}")
|
append("lin/${relativeOutputPathFor(moduleSchema.moduleName)}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val kotlinPackage: String?
|
||||||
|
get() = options.kotlinPackage.ifEmpty { null }
|
||||||
|
|
||||||
val kotlinFile: String
|
val kotlinFile: String
|
||||||
get() {
|
get() {
|
||||||
if (moduleSchema.moduleUri.scheme == "pkl") {
|
if (moduleSchema.moduleUri.scheme == "pkl") {
|
||||||
@@ -123,6 +129,7 @@ class KotlinCodeGenerator(
|
|||||||
|
|
||||||
val hasProperties = pModuleClass.properties.any { !it.value.isHidden }
|
val hasProperties = pModuleClass.properties.any { !it.value.isHidden }
|
||||||
val isGenerateClass = hasProperties || pModuleClass.isOpen || pModuleClass.isAbstract
|
val isGenerateClass = hasProperties || pModuleClass.isOpen || pModuleClass.isAbstract
|
||||||
|
val packagePrefix = kotlinPackage?.let { "$it." } ?: ""
|
||||||
val moduleType =
|
val moduleType =
|
||||||
if (isGenerateClass) {
|
if (isGenerateClass) {
|
||||||
generateTypeSpec(pModuleClass, moduleSchema)
|
generateTypeSpec(pModuleClass, moduleSchema)
|
||||||
@@ -172,7 +179,9 @@ class KotlinCodeGenerator(
|
|||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||||
|
|
||||||
val fileSpec = FileSpec.builder(packageName, moduleTypeName).indent(options.indent)
|
val packagePath =
|
||||||
|
if (packagePrefix.isNotBlank()) "$packagePrefix.$packageName" else packageName
|
||||||
|
val fileSpec = FileSpec.builder(packagePath, moduleTypeName).indent(options.indent)
|
||||||
|
|
||||||
for (typeAlias in moduleSchema.typeAliases.values) {
|
for (typeAlias in moduleSchema.typeAliases.values) {
|
||||||
if (typeAlias.aliasedType is PType.Alias) {
|
if (typeAlias.aliasedType is PType.Alias) {
|
||||||
@@ -638,10 +647,14 @@ class KotlinCodeGenerator(
|
|||||||
val index = moduleName.lastIndexOf(".")
|
val index = moduleName.lastIndexOf(".")
|
||||||
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
val packageName = if (index == -1) "" else moduleName.substring(0, index)
|
||||||
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
val moduleTypeName = moduleName.substring(index + 1).replaceFirstChar { it.titlecaseChar() }
|
||||||
|
val packagePrefix = kotlinPackage?.let { "$it." } ?: ""
|
||||||
|
val renderedPackage =
|
||||||
|
if (packagePrefix.isNotBlank()) "$packagePrefix.$packageName" else packageName
|
||||||
|
|
||||||
return if (isModuleClass) {
|
return if (isModuleClass) {
|
||||||
ClassName(packageName, moduleTypeName)
|
ClassName(renderedPackage, moduleTypeName)
|
||||||
} else {
|
} else {
|
||||||
ClassName(packageName, moduleTypeName, simpleName)
|
ClassName(renderedPackage, moduleTypeName, simpleName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ class KotlinCodeGeneratorTest {
|
|||||||
pklCode: String,
|
pklCode: String,
|
||||||
generateKdoc: Boolean = false,
|
generateKdoc: Boolean = false,
|
||||||
generateSpringBootConfig: Boolean = false,
|
generateSpringBootConfig: Boolean = false,
|
||||||
implementSerializable: Boolean = false
|
implementSerializable: Boolean = false,
|
||||||
|
kotlinPackage: String? = null,
|
||||||
): String {
|
): String {
|
||||||
|
|
||||||
val module = Evaluator.preconfigured().evaluateSchema(ModuleSource.text(pklCode))
|
val module = Evaluator.preconfigured().evaluateSchema(ModuleSource.text(pklCode))
|
||||||
@@ -144,7 +145,8 @@ class KotlinCodeGeneratorTest {
|
|||||||
KotlinCodegenOptions(
|
KotlinCodegenOptions(
|
||||||
generateKdoc = generateKdoc,
|
generateKdoc = generateKdoc,
|
||||||
generateSpringBootConfig = generateSpringBootConfig,
|
generateSpringBootConfig = generateSpringBootConfig,
|
||||||
implementSerializable = implementSerializable
|
implementSerializable = implementSerializable,
|
||||||
|
kotlinPackage = kotlinPackage ?: "",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return generator.kotlinFile
|
return generator.kotlinFile
|
||||||
@@ -553,6 +555,92 @@ class KotlinCodeGeneratorTest {
|
|||||||
assertCompilesSuccessfully(kotlinCode)
|
assertCompilesSuccessfully(kotlinCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `custom kotlin package prefix`() {
|
||||||
|
val kotlinCode =
|
||||||
|
generateKotlinCode(
|
||||||
|
"""
|
||||||
|
module my.mod
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
age: Int
|
||||||
|
hobbies: List<String>
|
||||||
|
friends: Map<String, Person>
|
||||||
|
sibling: Person?
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
kotlinPackage = "cool.pkg.path",
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEqualTo(
|
||||||
|
"""
|
||||||
|
package cool.pkg.path.my
|
||||||
|
|
||||||
|
import kotlin.Long
|
||||||
|
import kotlin.String
|
||||||
|
import kotlin.collections.List
|
||||||
|
import kotlin.collections.Map
|
||||||
|
|
||||||
|
object Mod {
|
||||||
|
data class Person(
|
||||||
|
val name: String,
|
||||||
|
val age: Long,
|
||||||
|
val hobbies: List<String>,
|
||||||
|
val friends: Map<String, Person>,
|
||||||
|
val sibling: Person?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
kotlinCode
|
||||||
|
)
|
||||||
|
|
||||||
|
assertCompilesSuccessfully(kotlinCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `empty kotlin package prefix`() {
|
||||||
|
val kotlinCode =
|
||||||
|
generateKotlinCode(
|
||||||
|
"""
|
||||||
|
module my.mod
|
||||||
|
|
||||||
|
class Person {
|
||||||
|
name: String
|
||||||
|
age: Int
|
||||||
|
hobbies: List<String>
|
||||||
|
friends: Map<String, Person>
|
||||||
|
sibling: Person?
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
kotlinPackage = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEqualTo(
|
||||||
|
"""
|
||||||
|
package my
|
||||||
|
|
||||||
|
import kotlin.Long
|
||||||
|
import kotlin.String
|
||||||
|
import kotlin.collections.List
|
||||||
|
import kotlin.collections.Map
|
||||||
|
|
||||||
|
object Mod {
|
||||||
|
data class Person(
|
||||||
|
val name: String,
|
||||||
|
val age: Long,
|
||||||
|
val hobbies: List<String>,
|
||||||
|
val friends: Map<String, Person>,
|
||||||
|
val sibling: Person?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
kotlinCode
|
||||||
|
)
|
||||||
|
|
||||||
|
assertCompilesSuccessfully(kotlinCode)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `recursive types`() {
|
fun `recursive types`() {
|
||||||
val kotlinCode =
|
val kotlinCode =
|
||||||
|
|||||||
@@ -196,12 +196,15 @@ public class PklPlugin implements Plugin<Project> {
|
|||||||
configureCodeGenSpec(spec);
|
configureCodeGenSpec(spec);
|
||||||
|
|
||||||
spec.getGenerateKdoc().convention(false);
|
spec.getGenerateKdoc().convention(false);
|
||||||
|
spec.getKotlinPackage().convention("");
|
||||||
|
|
||||||
createModulesTask(KotlinCodeGenTask.class, spec)
|
createModulesTask(KotlinCodeGenTask.class, spec)
|
||||||
.configure(
|
.configure(
|
||||||
task -> {
|
task -> {
|
||||||
configureCodeGenTask(task, spec);
|
configureCodeGenTask(task, spec);
|
||||||
task.getGenerateKdoc().set(spec.getGenerateKdoc());
|
task.getGenerateKdoc().set(spec.getGenerateKdoc());
|
||||||
|
task.getIndent().set(spec.getIndent());
|
||||||
|
task.getKotlinPackage().set(spec.getKotlinPackage());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,5 +19,9 @@ import org.gradle.api.provider.Property;
|
|||||||
|
|
||||||
/** Configuration options for Kotlin code generators. Documented in user manual. */
|
/** Configuration options for Kotlin code generators. Documented in user manual. */
|
||||||
public interface KotlinCodeGenSpec extends CodeGenSpec {
|
public interface KotlinCodeGenSpec extends CodeGenSpec {
|
||||||
|
Property<String> getIndent();
|
||||||
|
|
||||||
|
Property<String> getKotlinPackage();
|
||||||
|
|
||||||
Property<Boolean> getGenerateKdoc();
|
Property<Boolean> getGenerateKdoc();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ public abstract class KotlinCodeGenTask extends CodeGenTask {
|
|||||||
@Input
|
@Input
|
||||||
public abstract Property<Boolean> getGenerateKdoc();
|
public abstract Property<Boolean> getGenerateKdoc();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public abstract Property<String> getKotlinPackage();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRunTask() {
|
protected void doRunTask() {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
@@ -35,6 +38,7 @@ public abstract class KotlinCodeGenTask extends CodeGenTask {
|
|||||||
getCliBaseOptions(),
|
getCliBaseOptions(),
|
||||||
getProject().file(getOutputDir()).toPath(),
|
getProject().file(getOutputDir()).toPath(),
|
||||||
getIndent().get(),
|
getIndent().get(),
|
||||||
|
getKotlinPackage().get(),
|
||||||
getGenerateKdoc().get(),
|
getGenerateKdoc().get(),
|
||||||
getGenerateSpringBootConfig().get(),
|
getGenerateSpringBootConfig().get(),
|
||||||
getImplementSerializable().get()))
|
getImplementSerializable().get()))
|
||||||
|
|||||||
@@ -65,7 +65,22 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
assertThat(personClassFile).exists()
|
assertThat(personClassFile).exists()
|
||||||
assertThat(addressClassFile).exists()
|
assertThat(addressClassFile).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `compile generated code with custom kotlin package`() {
|
||||||
|
writeBuildFile(kotlinPackage = "my.cool.pkl.pkg")
|
||||||
|
writePklFile()
|
||||||
|
runTask("compileKotlin")
|
||||||
|
|
||||||
|
val classesDir = testProjectDir.resolve("build/classes/kotlin/main")
|
||||||
|
val moduleClassFile = classesDir.resolve("my/cool/pkl/pkg/org/Mod.class")
|
||||||
|
val personClassFile = classesDir.resolve("my/cool/pkl/pkg/org/Mod\$Person.class")
|
||||||
|
val addressClassFile = classesDir.resolve("my/cool/pkl/pkg/org/Mod\$Address.class")
|
||||||
|
assertThat(moduleClassFile).exists()
|
||||||
|
assertThat(personClassFile).exists()
|
||||||
|
assertThat(addressClassFile).exists()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `no source modules`() {
|
fun `no source modules`() {
|
||||||
writeFile(
|
writeFile(
|
||||||
@@ -88,7 +103,7 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
assertThat(result.output).contains("No source modules specified.")
|
assertThat(result.output).contains("No source modules specified.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeBuildFile() {
|
private fun writeBuildFile(kotlinPackage: String? = null) {
|
||||||
val kotlinVersion = "1.6.0"
|
val kotlinVersion = "1.6.0"
|
||||||
|
|
||||||
writeFile(
|
writeFile(
|
||||||
@@ -125,6 +140,7 @@ class KotlinCodeGeneratorsTest : AbstractTest() {
|
|||||||
sourceModules = ["mod.pkl"]
|
sourceModules = ["mod.pkl"]
|
||||||
outputDir = file("build/generated")
|
outputDir = file("build/generated")
|
||||||
settingsModule = "pkl:settings"
|
settingsModule = "pkl:settings"
|
||||||
|
${kotlinPackage?.let { "kotlinPackage = \"$it\"" } ?: ""}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user