diff options
| author | Ahmed El Khazari <ahmed.el.khazari@qt.io> | 2025-01-31 16:50:48 +0200 |
|---|---|---|
| committer | Ville Voutilainen <ville.voutilainen@qt.io> | 2025-04-15 05:59:30 +0000 |
| commit | 19767ce3c7407584ae49a4f291e41b0294533a75 (patch) | |
| tree | 60862a6d90dd37f4548b4fa896d53c6f7373ddf9 | |
| parent | 0c4f42b5a301b5bba1ce30d13edbf58f848981a4 (diff) | |
Introduce GenerationProcessorAPI for generation workflows
This commit introduces the implementation of the GenerationProcessorAPI,
which provides a unified and extensible interface for orchestrating the
generation workflows.
- Introduced the GenerationProcessorAPI to expose a comprehensive set of
methods for:
- Initiating generation processes
- Configuring generation parameters
- Integrated the GenerationProcessorAPIImpl with the underlying
Processors and Generators
Task-number: QTTA-271
Change-Id: I62af129ad77004b4b4c1df85c4614e97d2cc971d
Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
5 files changed, 285 insertions, 95 deletions
diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/JennyAnnotationProcessor.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/JennyAnnotationProcessor.kt index aabbcba..ca17f3e 100644 --- a/compiler/src/main/java/io/github/landerlyoung/jenny/JennyAnnotationProcessor.kt +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/JennyAnnotationProcessor.kt @@ -1,132 +1,119 @@ -/** +/* + * Copyright (C) 2024 The Qt Company Ltd. * Copyright 2016 landerlyoung@gmail.com - * - * 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 - * - * http://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. + * SPDX-License-Identifier: Apache-2.0 */ + package io.github.landerlyoung.jenny +import io.github.landerlyoung.jenny.api.GenerationProcessorAPI +import io.github.landerlyoung.jenny.api.GenerationProcessorFactory +import io.github.landerlyoung.jenny.utils.AnnotationResolver + import javax.annotation.processing.AbstractProcessor -import javax.annotation.processing.Filer import javax.annotation.processing.Messager import javax.annotation.processing.ProcessingEnvironment import javax.annotation.processing.RoundEnvironment + import javax.lang.model.SourceVersion import javax.lang.model.element.TypeElement import javax.lang.model.type.MirroredTypesException -import javax.lang.model.util.Elements import javax.lang.model.util.Types + import javax.tools.Diagnostic -/** - * Author: landerlyoung@gmail.com - * Date: 2014-12-16 - * Time: 19:42 - * Life with passion. Code with creativity! - */ class JennyAnnotationProcessor : AbstractProcessor() { - private lateinit var mMessager: Messager - private lateinit var mTypeUtils: Types - private lateinit var mElementsUtils: Elements - private lateinit var mFiler: Filer - private lateinit var mConfigurations: Configurations + + private lateinit var generationProcessor: GenerationProcessorAPI + private lateinit var jennyConfigurations: JennyProcessorConfiguration + + private lateinit var messager: Messager + private lateinit var typeUtils: Types @Synchronized override fun init(processingEnv: ProcessingEnvironment) { super.init(processingEnv) - mMessager = processingEnv.messager - mTypeUtils = processingEnv.typeUtils - mElementsUtils = processingEnv.elementUtils - mFiler = processingEnv.filer - mConfigurations = Configurations.fromOptions(processingEnv.options) + messager = processingEnv.messager + typeUtils = processingEnv.typeUtils + jennyConfigurations = JennyProcessorConfiguration.fromOptions(processingEnv.options) + + messager.printMessage(Diagnostic.Kind.NOTE, "Jenny configured with:${jennyConfigurations}") - mMessager.printMessage(Diagnostic.Kind.NOTE, "Jenny configured with:${mConfigurations}") + generationProcessor = GenerationProcessorFactory.create(jennyConfigurations) } override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { if (roundEnv.errorRaised() - || roundEnv.processingOver() - || !annotations.any { it.qualifiedName.toString() in SUPPORTED_ANNOTATIONS }) return false + || roundEnv.processingOver() + || !annotations.any { it.qualifiedName.toString() in SUPPORTED_ANNOTATIONS } + ) return false try { - val env = Environment(mMessager, mTypeUtils, mElementsUtils, mFiler, mConfigurations) - - generateNativeGlueCode(roundEnv, env) - val proxyClasses = generateNativeProxy(roundEnv, env) - generateFusionProxyHeader(env, proxyClasses) - generateJniHelper(env) + generateNativeGlueCode(roundEnv) + generateNativeProxy(roundEnv) } catch (e: Throwable) { - mMessager.printMessage(Diagnostic.Kind.ERROR, "Jenny failed to process ${e.javaClass.name} ${e.message}") + messager.printMessage(Diagnostic.Kind.ERROR, "Jenny failed to process ${e.javaClass.name} ${e.message}") } return true } - private fun generateNativeGlueCode(roundEnv: RoundEnvironment, env: Environment): List<CppClass> { - // classify annotations by class + private fun generateNativeGlueCode(roundEnv: RoundEnvironment) { return roundEnv.getElementsAnnotatedWith(NativeClass::class.java) - .filterIsInstance<TypeElement>() - .map { - NativeGlueGenerator(env, it).doGenerate() - } + .filterIsInstance<TypeElement>() + .forEach { + generationProcessor.setGlueNamespace(jennyConfigurations.glueNamespace) + generationProcessor.generateGlue(it) + } } - private fun generateNativeProxy(roundEnv: RoundEnvironment, env: Environment): List<CppClass> { - val classes = mutableListOf<CppClass>() + private fun generateNativeProxy(roundEnv: RoundEnvironment) { - classes += roundEnv.getElementsAnnotatedWith(NativeProxy::class.java) - .map { - val config = NativeProxyGenerator.NativeProxyConfig( - (it.getAnnotation(NativeProxy::class.java) - ?: AnnotationResolver.getDefaultImplementation(NativeProxy::class.java))) - NativeProxyGenerator(env, it as TypeElement, config).doGenerate() - } + roundEnv.getElementsAnnotatedWith(NativeProxy::class.java) + .forEach { + val annotation = it.getAnnotation(NativeProxy::class.java) + ?: AnnotationResolver.getDefaultImplementation(NativeProxy::class.java) - classes += (roundEnv.getElementsAnnotatedWith(NativeProxyForClasses::class.java) - .asSequence() - .map { it.getAnnotation(NativeProxyForClasses::class.java) } + val proxyConfiguration = jennyConfigurations.provideProxyConfiguration().copy( + namespace = annotation.namespace, + allFields = annotation.allFields, + allMethods = annotation.allMethods, + onlyPublicMethod = false, + ) + generationProcessor.configureProxy(proxyConfiguration) + generationProcessor.generateProxy(it as TypeElement) + } + + (roundEnv.getElementsAnnotatedWith(NativeProxyForClasses::class.java) + .asSequence() + .map { it.getAnnotation(NativeProxyForClasses::class.java) } + roundEnv.getElementsAnnotatedWith(NativeProxyForClasses.RepeatContainer::class.java) - .asSequence() - .flatMap { it.getAnnotationsByType(NativeProxyForClasses::class.java).asSequence() } - ) - .toCollection(mutableSetOf()) - .flatMap { annotation -> - try { - annotation.classes - throw AssertionError("unreachable") - } catch (e: MirroredTypesException) { - e.typeMirrors - }.map { - val clazz = mTypeUtils.asElement(it) as TypeElement - - val config = NativeProxyGenerator.NativeProxyConfig( - allMethods = true, allFields = true, namespace = annotation.namespace, onlyPublic = true) - - NativeProxyGenerator(env, clazz, config).doGenerate() + .asSequence() + .flatMap { + it.getAnnotationsByType(NativeProxyForClasses::class.java).asSequence() } + ) + .toCollection(mutableSetOf()) + .forEach { annotation -> + try { + annotation.classes + throw AssertionError("unreachable") + } catch (e: MirroredTypesException) { + e.typeMirrors + }.forEach { + + val proxyConfiguration = jennyConfigurations.provideProxyConfiguration().copy( + namespace = annotation.namespace, + allFields = true, + allMethods = true, + onlyPublicMethod = true + ) + + generationProcessor.configureProxy(proxyConfiguration) + generationProcessor.generateProxy(typeUtils.asElement(it) as TypeElement) } - - return classes - } - - private fun generateJniHelper(env: Environment) { - env.createOutputFile(Constants.JENNY_GEN_DIR_PROXY, Constants.JENNY_JNI_HELPER_H_NAME).use { - it.write(Constants.JENNY_JNI_HELPER_H_CONTENT.toByteArray(Charsets.UTF_8)) - } - } - - private fun generateFusionProxyHeader(env: Environment, proxyClasses: Collection<CppClass>) { - FusionProxyGenerator(env, proxyClasses).generate() + } } override fun getSupportedSourceVersion(): SourceVersion { @@ -138,17 +125,17 @@ class JennyAnnotationProcessor : AbstractProcessor() { } override fun getSupportedOptions(): Set<String> { - return Configurations.ALL_OPTIONS + return JennyProcessorConfiguration.configurationOptions } companion object { private val SUPPORTED_ANNOTATIONS: Set<String> = setOf( - NativeClass::class.java.name, - NativeCode::class.java.name, - NativeFieldProxy::class.java.name, - NativeMethodProxy::class.java.name, - NativeProxy::class.java.name, - NativeProxyForClasses::class.java.name + NativeClass::class.java.name, + NativeCode::class.java.name, + NativeFieldProxy::class.java.name, + NativeMethodProxy::class.java.name, + NativeProxy::class.java.name, + NativeProxyForClasses::class.java.name ) } } diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/JennyProcessorConfiguration.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/JennyProcessorConfiguration.kt new file mode 100644 index 0000000..f519d4f --- /dev/null +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/JennyProcessorConfiguration.kt @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2025 The Qt Company Ltd. + * Copyright 2016 landerlyoung@gmail.com + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.github.landerlyoung.jenny + +import io.github.landerlyoung.jenny.model.JennyProxyConfiguration +import io.github.landerlyoung.jenny.provider.ProviderConfiguration + +/** + * @param outputDirectory full path where the code is generated + * @param glueNamespace name space for the C++ glue files (optional) + * @param useTemplates flag to set the providers to use templates code + * @param templateDirectory full path of custom templates + * @param templateBuildDirectory full path of where the compiled templates is generated + * @param threadSafe add mutex for C++ proxy files + * @param useJniHelper flag to use JniHelper file + * @param headerOnlyProxy only header file (.h) is generated + * @param errorLoggerFunction Custom Error Logging function + * @param fusionProxyHeaderName Custom Fusion header file name + */ + +data class JennyProcessorConfiguration( + val outputDirectory: String, + val glueNamespace: String = "", + val useTemplates: Boolean = true, + val useQjniTemplates: Boolean = false, + val templateDirectory: String? = null, + val templateBuildDirectory: String? = null, + val threadSafe: Boolean = true, + val useJniHelper: Boolean = false, + val headerOnlyProxy: Boolean = false, + val errorLoggerFunction: String = "", + val fusionProxyHeaderName: String = "", +) { + fun provideProxyConfiguration(): JennyProxyConfiguration { + return JennyProxyConfiguration( + namespace = "", + threadSafe = threadSafe, + useJniHelper = useJniHelper, + headerOnlyProxy = headerOnlyProxy, + allFields = true, + allMethods = true, + onlyPublicMethod = true, + errorLoggingFunction = errorLoggerFunction, + fusionProxyHeaderName = fusionProxyHeaderName + ) + } + + fun provideTemplateConfiguration(): ProviderConfiguration { + return ProviderConfiguration(useTemplates, useQjniTemplates, templateDirectory, templateBuildDirectory) + } + + companion object { + private const val PREFIX = "jenny." + // Constructing Keys + + /** + * external error log function + * void (function_type)(JNIEnv* env, const char* error); + */ + private val ERROR_LOGGER_FUNCTION = PREFIX + JennyProcessorConfiguration::errorLoggerFunction.name + + private val OUTPUT_DIRECTORY = PREFIX + JennyProcessorConfiguration::outputDirectory.name + + private val FUSION_PROXY_HEADER_NAME = PREFIX + JennyProcessorConfiguration::fusionProxyHeaderName.name + + private val USE_TEMPLATES = PREFIX + JennyProcessorConfiguration::useTemplates.name + private val USE_QJNI_TEMPLATES = PREFIX + JennyProcessorConfiguration::useQjniTemplates.name + private val TEMPLATE_DIRECTORY = PREFIX + JennyProcessorConfiguration::templateDirectory.name + private val TEMPLATE_BUILD_DIRECTORY = PREFIX + JennyProcessorConfiguration::templateBuildDirectory.name + + private val HEADER_ONLY_PROXY = PREFIX + JennyProcessorConfiguration::headerOnlyProxy.name + private val USE_JNI_HELPER = PREFIX + JennyProcessorConfiguration::useJniHelper.name + private val THREAD_SAFE = PREFIX + JennyProcessorConfiguration::threadSafe.name + + val configurationOptions: Set<String> + get() = setOf( + THREAD_SAFE, + ERROR_LOGGER_FUNCTION, + USE_TEMPLATES, + USE_QJNI_TEMPLATES, + TEMPLATE_DIRECTORY, + TEMPLATE_BUILD_DIRECTORY, + OUTPUT_DIRECTORY, + FUSION_PROXY_HEADER_NAME, + HEADER_ONLY_PROXY, + USE_JNI_HELPER, + ) + + fun fromOptions(options: Map<String, String>) = JennyProcessorConfiguration( + outputDirectory = options[OUTPUT_DIRECTORY] ?: "src/main/cpp/gen", + useTemplates = options[USE_TEMPLATES] == true.toString(), + useQjniTemplates = options[USE_QJNI_TEMPLATES] == true.toString(), + templateDirectory = options[TEMPLATE_DIRECTORY], + templateBuildDirectory = options[TEMPLATE_BUILD_DIRECTORY], + threadSafe = options[THREAD_SAFE] == true.toString(), + useJniHelper = options[USE_JNI_HELPER] == true.toString(), + headerOnlyProxy = options[HEADER_ONLY_PROXY] == true.toString(), + errorLoggerFunction = options[ERROR_LOGGER_FUNCTION] ?: "", + fusionProxyHeaderName = options[FUSION_PROXY_HEADER_NAME] ?: "JennyFusionProxy.h" + ) + } +} diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPI.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPI.kt new file mode 100644 index 0000000..f1d29dd --- /dev/null +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPI.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 The Qt Company Ltd. + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.github.landerlyoung.jenny.api + +import io.github.landerlyoung.jenny.JennyProcessorConfiguration +import io.github.landerlyoung.jenny.model.JennyProxyConfiguration + +interface GenerationProcessorAPI { + /** + * Generates native glue code for the specified class. + * @param input the class for which to generate glue code. + */ + fun generateGlue(input: Any) + + /** + * Sets the namespace for glue code generation. + * @param namespace the namespace to use for glue code. + */ + fun setGlueNamespace(namespace: String) + + /** + * Generates proxy code for the specified class. + * @param input the class for which to generate proxy code. + */ + fun generateProxy(input: Any) + + /** + * Configures proxy generation settings. + * @param proxyConfiguration the configuration object for proxy generation. + */ + fun configureProxy(proxyConfiguration: JennyProxyConfiguration) + + /** + * Configures processor settings. + * @param configuration the configuration object for the processor. + */ + fun configureProcessor(configuration: JennyProcessorConfiguration) +} + diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPIImpl.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPIImpl.kt new file mode 100644 index 0000000..d323952 --- /dev/null +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorAPIImpl.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2025 The Qt Company Ltd. + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.github.landerlyoung.jenny.api + +import io.github.landerlyoung.jenny.JennyProcessorConfiguration +import io.github.landerlyoung.jenny.model.JennyProxyConfiguration +import io.github.landerlyoung.jenny.processor.NativeGlueProcessor +import io.github.landerlyoung.jenny.processor.NativeProxyProcessor + +internal class GenerationProcessorAPIImpl( + jennyProcessorConfiguration: JennyProcessorConfiguration, +) : GenerationProcessorAPI { + + private val nativeGlueProcessor = NativeGlueProcessor(jennyProcessorConfiguration.outputDirectory) + + private val nativeProxyProcessor = NativeProxyProcessor( + outputDirectory = jennyProcessorConfiguration.outputDirectory, + providerConfiguration = jennyProcessorConfiguration.provideTemplateConfiguration(), + proxyConfiguration = jennyProcessorConfiguration.provideProxyConfiguration() + ) + + override fun generateGlue(input: Any) = nativeGlueProcessor.process(input) + + override fun setGlueNamespace(namespace: String) = nativeGlueProcessor.setNamespace(namespace) + + override fun generateProxy(input: Any) = nativeProxyProcessor.process(input) + + override fun configureProxy(proxyConfiguration: JennyProxyConfiguration) = + nativeProxyProcessor.applyConfiguration(proxyConfiguration) + + override fun configureProcessor(configuration: JennyProcessorConfiguration) { + nativeProxyProcessor.apply { + applyConfiguration(configuration.provideProxyConfiguration()) + setOutputTargetPath(configuration.outputDirectory) + } + nativeGlueProcessor.setOutputTargetPath(configuration.outputDirectory) + } +} diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorFactory.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorFactory.kt new file mode 100644 index 0000000..02a0992 --- /dev/null +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/api/GenerationProcessorFactory.kt @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2025 The Qt Company Ltd. + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.github.landerlyoung.jenny.api + +import io.github.landerlyoung.jenny.JennyProcessorConfiguration + +object GenerationProcessorFactory { + fun create(configuration: JennyProcessorConfiguration): GenerationProcessorAPI { + return GenerationProcessorAPIImpl(configuration) + } +} |
