Improve build logic for Kotlin (#1520)

- Enforce Kotlin version via resolution rule (replaces BOM)
  - fail if kotlin-stdlib/kotlin-reflect exceed target version
- Replace kotlin-stdlib-jdk8 with kotlin-stdlib (jdk7/8 are now shims)
- Port pkl-core annotation processor to Java (with Codex)
- removes kotlin-stdlib from its compile classpath for better dependency
hygiene (Java module)
- Downgrade clikt for Kotlin 2.2 compatibility
- Upgrade kotlinx-serialization

---------

Co-authored-by: Daniel Chao <dan.chao@apple.com>
This commit is contained in:
odenix
2026-04-15 17:02:42 +01:00
committed by GitHub
parent 2e0b4a3a97
commit 04a9cc90d2
24 changed files with 558 additions and 456 deletions
+2 -5
View File
@@ -25,7 +25,6 @@ org.graalvm.truffle:truffle-api:25.0.1=compileClasspath,generatorCompileClasspat
org.graalvm.truffle:truffle-dsl-processor:25.0.1=annotationProcessor
org.jetbrains.kotlin:abi-tools-api:2.3.20=kotlinInternalAbiValidation
org.jetbrains.kotlin:abi-tools:2.3.20=kotlinInternalAbiValidation
org.jetbrains.kotlin:kotlin-bom:2.2.21=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-build-tools-api:2.3.20=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-compat:2.3.20=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:kotlin-build-tools-cri-impl:2.3.20=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
@@ -48,9 +47,7 @@ org.jetbrains.kotlin:kotlin-scripting-common:2.3.20=kotlinCompilerPluginClasspat
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.3.20=kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.3.20=kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-scripting-jvm:2.3.20=kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest
org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.21=generatorCompileClasspath,generatorRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21=generatorCompileClasspath,generatorRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=generatorCompileClasspath,generatorRuntimeClasspath,swiftExportClasspathResolvable,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.2.21=swiftExportClasspathResolvable,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.3.20=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath
org.jetbrains.kotlin:kotlin-tooling-core:2.3.20=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath
org.jetbrains.kotlin:swift-export-embeddable:2.2.21=swiftExportClasspathResolvable
@@ -58,7 +55,7 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinBuildToolsApiClass
org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3=swiftExportClasspathResolvable
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3=swiftExportClasspathResolvable
org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3=swiftExportClasspathResolvable
org.jetbrains:annotations:13.0=generatorCompileClasspath,generatorRuntimeClasspath,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath,swiftExportClasspathResolvable,testCompileClasspath,testRuntimeClasspath
org.jetbrains:annotations:13.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathGenerator,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath,swiftExportClasspathResolvable,testCompileClasspath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:6.0.3=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.0.3=testCompileClasspath,testRuntimeClasspath
+2 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,6 @@ dependencies {
add("generatorImplementation", libs.javaPoet)
add("generatorImplementation", libs.truffleApi)
add("generatorImplementation", libs.kotlinStdLib)
javaExecutableConfiguration(project(":pkl-cli", "javaExecutable"))
}
@@ -72,7 +71,7 @@ publishing {
Core implementation of the Pkl configuration language.
Includes Java APIs for embedding the language into JVM applications,
and for building libraries and tools on top of the language.
"""
"""
.trimIndent()
)
}
@@ -0,0 +1,272 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.generator;
import com.oracle.truffle.api.dsl.GeneratedBy;
import com.palantir.javapoet.ClassName;
import com.palantir.javapoet.JavaFile;
import com.palantir.javapoet.MethodSpec;
import com.palantir.javapoet.TypeSpec;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
/**
* Generates a subclass of {@code org.pkl.core.stdlib.registry.ExternalMemberRegistry} for each
* stdlib module and a factory to instantiate them. Generated classes are written to {@code
* generated/truffle/org/pkl/core/stdlib/registry}.
*
* <p>Inputs:
*
* <ul>
* <li>Generated Truffle node classes for stdlib members. These classes are located in subpackages
* of {@code org.pkl.core.stdlib} and identified via their {@code @GeneratedBy} annotations.
* <li>{@code @PklName} annotations on handwritten node classes from which Truffle node classes
* are generated.
* </ul>
*/
public final class MemberRegistryGenerator extends AbstractProcessor {
private static final String TRUFFLE_NODE_CLASS_SUFFIX = "NodeGen";
private static final String TRUFFLE_NODE_FACTORY_SUFFIX = "NodesFactory";
private static final String STDLIB_PACKAGE_NAME = "org.pkl.core.stdlib";
private static final String REGISTRY_PACKAGE_NAME = STDLIB_PACKAGE_NAME + ".registry";
private static final String MODULE_PACKAGE_NAME = "org.pkl.core.module";
private static final ClassName EXTERNAL_MEMBER_REGISTRY_CLASS_NAME =
ClassName.get(REGISTRY_PACKAGE_NAME, "ExternalMemberRegistry");
private static final ClassName EMPTY_MEMBER_REGISTRY_CLASS_NAME =
ClassName.get(REGISTRY_PACKAGE_NAME, "EmptyMemberRegistry");
private static final ClassName MEMBER_REGISTRY_FACTORY_CLASS_NAME =
ClassName.get(REGISTRY_PACKAGE_NAME, "MemberRegistryFactory");
private static final ClassName MODULE_KEY_CLASS_NAME =
ClassName.get(MODULE_PACKAGE_NAME, "ModuleKey");
private static final ClassName MODULE_KEYS_CLASS_NAME =
ClassName.get(MODULE_PACKAGE_NAME, "ModuleKeys");
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(GeneratedBy.class.getName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_17;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (annotations.isEmpty()) {
return true;
}
var nodeClassesByPackage = collectNodeClasses(roundEnv);
generateRegistryClasses(nodeClassesByPackage);
generateRegistryFactoryClass(nodeClassesByPackage.keySet());
return true;
}
private Map<PackageElement, List<TypeElement>> collectNodeClasses(RoundEnvironment roundEnv) {
var nodeClasses = new ArrayList<TypeElement>();
for (var element : roundEnv.getElementsAnnotatedWith(GeneratedBy.class)) {
if (!(element instanceof TypeElement typeElement)) {
continue;
}
if (!typeElement.getQualifiedName().toString().startsWith(STDLIB_PACKAGE_NAME)) {
continue;
}
if (!typeElement.getSimpleName().toString().endsWith(TRUFFLE_NODE_CLASS_SUFFIX)) {
continue;
}
nodeClasses.add(typeElement);
}
nodeClasses.sort(
Comparator.comparing(
(TypeElement element) -> {
var enclosingElement = element.getEnclosingElement();
return enclosingElement.getKind() == ElementKind.PACKAGE
? ""
: enclosingElement.getSimpleName().toString();
})
.thenComparing(element -> element.getSimpleName().toString()));
var result = new LinkedHashMap<PackageElement, List<TypeElement>>();
for (var nodeClass : nodeClasses) {
var pkg = processingEnv.getElementUtils().getPackageOf(nodeClass);
result.computeIfAbsent(pkg, ignored -> new ArrayList<>()).add(nodeClass);
}
return result;
}
private void generateRegistryClasses(
Map<PackageElement, List<TypeElement>> nodeClassesByPackage) {
for (var entry : nodeClassesByPackage.entrySet()) {
generateRegistryClass(entry.getKey(), entry.getValue());
}
}
private void generateRegistryClass(PackageElement pkg, List<TypeElement> nodeClasses) {
var pklModuleName = getAnnotatedPklName(pkg);
if (pklModuleName == null) {
pklModuleName = pkg.getSimpleName().toString();
}
var pklModuleNameCapitalized = capitalize(pklModuleName);
var registryClassName =
ClassName.get(REGISTRY_PACKAGE_NAME, pklModuleNameCapitalized + "MemberRegistry");
TypeSpec.Builder registryClass =
TypeSpec.classBuilder(registryClassName)
.addJavadoc("Generated by {@link $L}.\n", getClass().getName())
.addModifiers(Modifier.FINAL)
.superclass(EXTERNAL_MEMBER_REGISTRY_CLASS_NAME);
var constructor = MethodSpec.constructorBuilder();
for (var nodeClass : nodeClasses) {
var enclosingClass = nodeClass.getEnclosingElement();
var pklClassName = getAnnotatedPklName(enclosingClass);
if (pklClassName == null) {
pklClassName =
stripSuffix(enclosingClass.getSimpleName().toString(), TRUFFLE_NODE_FACTORY_SUFFIX);
}
var pklMemberName = getAnnotatedPklName(nodeClass);
if (pklMemberName == null) {
pklMemberName =
stripSuffix(nodeClass.getSimpleName().toString(), TRUFFLE_NODE_CLASS_SUFFIX);
}
String pklMemberNameQualified;
if (pklClassName.equals(pklModuleNameCapitalized)) {
pklMemberNameQualified = "pkl." + pklModuleName + "#" + pklMemberName;
} else {
pklMemberNameQualified = "pkl." + pklModuleName + "#" + pklClassName + "." + pklMemberName;
}
registryClass.addOriginatingElement(nodeClass);
constructor.addStatement("register($S, $T::create)", pklMemberNameQualified, nodeClass);
}
registryClass.addMethod(constructor.build());
writeJavaFile(REGISTRY_PACKAGE_NAME, registryClass.build());
}
private void generateRegistryFactoryClass(Collection<PackageElement> packages) {
var registryFactoryClass =
TypeSpec.classBuilder(MEMBER_REGISTRY_FACTORY_CLASS_NAME)
.addJavadoc("Generated by {@link $L}.\n", getClass().getName())
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
registryFactoryClass.addMethod(
MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build());
MethodSpec.Builder getMethod =
MethodSpec.methodBuilder("get")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(MODULE_KEY_CLASS_NAME, "moduleKey")
.returns(EXTERNAL_MEMBER_REGISTRY_CLASS_NAME)
.beginControlFlow("if (!$T.isStdLibModule(moduleKey))", MODULE_KEYS_CLASS_NAME)
.addStatement("return $T.INSTANCE", EMPTY_MEMBER_REGISTRY_CLASS_NAME)
.endControlFlow()
.beginControlFlow("switch (moduleKey.getUri().getSchemeSpecificPart())");
for (var pkg : packages) {
var pklModuleName = getAnnotatedPklName(pkg);
if (pklModuleName == null) {
pklModuleName = pkg.getSimpleName().toString();
}
var pklModuleNameCapitalized = capitalize(pklModuleName);
var registryClassName =
ClassName.get(REGISTRY_PACKAGE_NAME, pklModuleNameCapitalized + "MemberRegistry");
registryFactoryClass.addOriginatingElement(pkg);
getMethod.addCode("case $S:\n", pklModuleName);
getMethod.addStatement(" return new $T()", registryClassName);
}
getMethod
.addCode("default:\n")
.addStatement(" return $T.INSTANCE", EMPTY_MEMBER_REGISTRY_CLASS_NAME)
.endControlFlow();
registryFactoryClass.addMethod(getMethod.build());
writeJavaFile(REGISTRY_PACKAGE_NAME, registryFactoryClass.build());
}
private String getAnnotatedPklName(Element element) {
for (var annotation : element.getAnnotationMirrors()) {
var annotationName = annotation.getAnnotationType().asElement().getSimpleName().toString();
if (annotationName.equals("PklName")) {
return firstAnnotationValue(annotation).getValue().toString();
}
if (annotationName.equals("GeneratedBy")) {
var value = firstAnnotationValue(annotation).getValue();
if (value instanceof TypeMirror typeMirror) {
var generatedByElement = processingEnv.getTypeUtils().asElement(typeMirror);
if (generatedByElement != null) {
return getAnnotatedPklName(generatedByElement);
}
}
}
}
return null;
}
private static AnnotationValue firstAnnotationValue(AnnotationMirror annotation) {
return annotation.getElementValues().values().iterator().next();
}
private void writeJavaFile(String packageName, TypeSpec typeSpec) {
try {
JavaFile.builder(packageName, typeSpec).build().writeTo(processingEnv.getFiler());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String stripSuffix(String value, String suffix) {
return value.endsWith(suffix) ? value.substring(0, value.length() - suffix.length()) : value;
}
private static String capitalize(String value) {
if (value.isEmpty()) {
return value;
}
return Character.toUpperCase(value.charAt(0)) + value.substring(1);
}
}
@@ -1,191 +0,0 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.core.generator
import com.oracle.truffle.api.dsl.GeneratedBy
import com.palantir.javapoet.ClassName
import com.palantir.javapoet.JavaFile
import com.palantir.javapoet.MethodSpec
import com.palantir.javapoet.TypeSpec
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.*
import javax.lang.model.type.TypeMirror
/**
* Generates a subclass of `org.pkl.core.stdlib.registry.ExternalMemberRegistry` for each stdlib
* module and a factory to instantiate them. Generated classes are written to
* `generated/truffle/org/pkl/core/stdlib/registry`.
*
* Inputs:
* - Generated Truffle node classes for stdlib members. These classes are located in subpackages of
* `org.pkl.core.stdlib` and identified via their `@GeneratedBy` annotations.
* - `@PklName` annotations on handwritten node classes from which Truffle node classes are
* generated.
*/
class MemberRegistryGenerator : AbstractProcessor() {
private val truffleNodeClassSuffix = "NodeGen"
private val truffleNodeFactorySuffix = "NodesFactory"
private val stdLibPackageName: String = "org.pkl.core.stdlib"
private val registryPackageName: String = "$stdLibPackageName.registry"
private val modulePackageName: String = "org.pkl.core.module"
private val externalMemberRegistryClassName: ClassName =
ClassName.get(registryPackageName, "ExternalMemberRegistry")
private val emptyMemberRegistryClassName: ClassName =
ClassName.get(registryPackageName, "EmptyMemberRegistry")
private val memberRegistryFactoryClassName: ClassName =
ClassName.get(registryPackageName, "MemberRegistryFactory")
private val moduleKeyClassName: ClassName = ClassName.get(modulePackageName, "ModuleKey")
private val moduleKeysClassName: ClassName = ClassName.get(modulePackageName, "ModuleKeys")
override fun getSupportedAnnotationTypes(): Set<String> = setOf(GeneratedBy::class.java.name)
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.RELEASE_17
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
if (annotations.isEmpty()) return true
val nodeClassesByPackage = collectNodeClasses(roundEnv)
generateRegistryClasses(nodeClassesByPackage)
generateRegistryFactoryClass(nodeClassesByPackage.keys)
return true
}
private fun collectNodeClasses(roundEnv: RoundEnvironment) =
roundEnv
.getElementsAnnotatedWith(GeneratedBy::class.java)
.asSequence()
.filterIsInstance<TypeElement>()
.filter { it.qualifiedName.toString().startsWith(stdLibPackageName) }
.filter { it.simpleName.toString().endsWith(truffleNodeClassSuffix) }
.sortedWith(
compareBy(
{
if (it.enclosingElement.kind == ElementKind.PACKAGE) ""
else it.enclosingElement.simpleName.toString()
},
{ it.simpleName.toString() },
)
)
.groupBy { processingEnv.elementUtils.getPackageOf(it) }
private fun generateRegistryClasses(
nodeClassesByPackage: Map<PackageElement, List<TypeElement>>
) {
for ((pkg, nodeClasses) in nodeClassesByPackage) {
generateRegistryClass(pkg, nodeClasses)
}
}
private fun generateRegistryClass(pkg: PackageElement, nodeClasses: List<TypeElement>) {
val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString()
val pklModuleNameCapitalized = pklModuleName.capitalize()
val registryClassName =
ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry")
val registryClass =
TypeSpec.classBuilder(registryClassName)
.addJavadoc("Generated by {@link ${this::class.qualifiedName}}.")
.addModifiers(Modifier.FINAL)
.superclass(externalMemberRegistryClassName)
val registryClassConstructor = MethodSpec.constructorBuilder()
for (nodeClass in nodeClasses) {
val enclosingClass = nodeClass.enclosingElement
val pklClassName =
getAnnotatedPklName(enclosingClass)
?: enclosingClass.simpleName.toString().removeSuffix(truffleNodeFactorySuffix)
val pklMemberName =
getAnnotatedPklName(nodeClass)
?: nodeClass.simpleName.toString().removeSuffix(truffleNodeClassSuffix)
val pklMemberNameQualified =
when (pklClassName) {
// By convention, the top-level class containing node classes
// for *module* members is named `<SimpleModuleName>Nodes`.
// Example: `BaseNodes` for pkl.base
pklModuleNameCapitalized -> "pkl.$pklModuleName#$pklMemberName"
else -> "pkl.$pklModuleName#$pklClassName.$pklMemberName"
}
registryClass.addOriginatingElement(nodeClass)
registryClassConstructor.addStatement(
"register(\$S, \$T::create)",
pklMemberNameQualified,
nodeClass,
)
}
registryClass.addMethod(registryClassConstructor.build())
val javaFile = JavaFile.builder(registryPackageName, registryClass.build()).build()
javaFile.writeTo(processingEnv.filer)
}
private fun generateRegistryFactoryClass(packages: Collection<PackageElement>) {
val registryFactoryClass =
TypeSpec.classBuilder(memberRegistryFactoryClassName)
.addJavadoc("Generated by {@link ${this::class.qualifiedName}}.")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
val registryFactoryConstructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE)
registryFactoryClass.addMethod(registryFactoryConstructor.build())
val registryFactoryGetMethod =
MethodSpec.methodBuilder("get")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(moduleKeyClassName, "moduleKey")
.returns(externalMemberRegistryClassName)
.beginControlFlow("if (!\$T.isStdLibModule(moduleKey))", moduleKeysClassName)
.addStatement("return \$T.INSTANCE", emptyMemberRegistryClassName)
.endControlFlow()
.beginControlFlow("switch (moduleKey.getUri().getSchemeSpecificPart())")
for (pkg in packages) {
val pklModuleName = getAnnotatedPklName(pkg) ?: pkg.simpleName.toString()
val pklModuleNameCapitalized = pklModuleName.capitalize()
val registryClassName =
ClassName.get(registryPackageName, "${pklModuleNameCapitalized}MemberRegistry")
// declare dependency on package-info.java (for `@PklName`)
registryFactoryClass.addOriginatingElement(pkg)
registryFactoryGetMethod
.addCode("case \$S:\n", pklModuleName)
.addStatement(" return new \$T()", registryClassName)
}
registryFactoryGetMethod
.addCode("default:\n")
.addStatement(" return \$T.INSTANCE", emptyMemberRegistryClassName)
.endControlFlow()
registryFactoryClass.addMethod(registryFactoryGetMethod.build())
val javaFile = JavaFile.builder(registryPackageName, registryFactoryClass.build()).build()
javaFile.writeTo(processingEnv.filer)
}
private fun getAnnotatedPklName(element: Element): String? {
for (annotation in element.annotationMirrors) {
when (annotation.annotationType.asElement().simpleName.toString()) {
"PklName" -> return annotation.elementValues.values.iterator().next().value.toString()
"GeneratedBy" -> {
val annotationValue = annotation.elementValues.values.first().value as TypeMirror
return getAnnotatedPklName(processingEnv.typeUtils.asElement(annotationValue))
}
}
}
return null
}
private fun String.capitalize(): String = replaceFirstChar { it.titlecaseChar() }
}