Skip to content

Commit

Permalink
feat(compiler): Allow multiple plugins of the same type in a project
Browse files Browse the repository at this point in the history
Separate the generation of the factory classes from the generation of
the service loader files, as the service loader files must contain all
factories of a specfic type, and KSP's `CodeGenerator` cannot append to
previously generated files.

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.com>
  • Loading branch information
mnonnenmacher committed Sep 2, 2024
1 parent 90accbb commit 35d18a6
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 19 deletions.
20 changes: 2 additions & 18 deletions plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
package org.ossreviewtoolkit.plugins.compiler

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies

import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
Expand All @@ -39,9 +38,9 @@ import org.ossreviewtoolkit.plugins.api.PluginOptionType
import org.ossreviewtoolkit.plugins.api.Secret

class PluginFactoryGenerator(private val codeGenerator: CodeGenerator) {
fun generate(pluginSpec: PluginSpec) {
fun generate(pluginSpec: PluginSpec): ServiceLoaderSpec {
val generatedFactory = generateFactoryClass(pluginSpec)
generateServiceLoaderFile(pluginSpec, generatedFactory)
return ServiceLoaderSpec(pluginSpec, generatedFactory)
}

/**
Expand Down Expand Up @@ -188,19 +187,4 @@ class PluginFactoryGenerator(private val codeGenerator: CodeGenerator) {
""".trimIndent()
)
}.build()

/**
* Generate a service loader file for the [generatedFactory].
*/
private fun generateServiceLoaderFile(pluginSpec: PluginSpec, generatedFactory: TypeSpec) {
codeGenerator.createNewFileByPath(
dependencies = Dependencies(aggregating = true, *listOfNotNull(pluginSpec.containingFile).toTypedArray()),
path = "META-INF/services/${pluginSpec.factory.qualifiedName}",
extensionName = ""
).use { output ->
output.writer().use { writer ->
writer.write("${pluginSpec.packageName}.${generatedFactory.name}\n")
}
}
}
}
7 changes: 6 additions & 1 deletion plugins/compiler/src/main/kotlin/PluginProcessor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class PluginProcessor(codeGenerator: CodeGenerator) : SymbolProcessor {
private val specFactory = PluginSpecFactory()
private val factoryGenerator = PluginFactoryGenerator(codeGenerator)
private val jsonGenerator = JsonSpecGenerator(codeGenerator)
private val serviceLoaderGenerator = ServiceLoaderGenerator(codeGenerator)

/**
* Process all classes annotated with [OrtPlugin] to generate plugin factories for them.
Expand All @@ -54,6 +55,8 @@ class PluginProcessor(codeGenerator: CodeGenerator) : SymbolProcessor {
"Could not get qualified name of OrtPlugin annotation."
}

val serviceLoaderSpecs = mutableListOf<ServiceLoaderSpec>()

resolver.getSymbolsWithAnnotation(ortPluginClassName).forEach { pluginClass ->
require(pluginClass is KSClassDeclaration) {
"Annotated element $pluginClass is not a class."
Expand All @@ -68,10 +71,12 @@ class PluginProcessor(codeGenerator: CodeGenerator) : SymbolProcessor {
checkExtendsPluginClass(pluginClass, pluginParentClass)

val pluginSpec = specFactory.create(pluginAnnotation, pluginClass, pluginFactoryClass)
factoryGenerator.generate(pluginSpec)
serviceLoaderSpecs += factoryGenerator.generate(pluginSpec)
jsonGenerator.generate(pluginSpec)
}

serviceLoaderGenerator.generate(serviceLoaderSpecs)

invoked = true

return emptyList()
Expand Down
9 changes: 9 additions & 0 deletions plugins/compiler/src/main/kotlin/PluginSpec.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package org.ossreviewtoolkit.plugins.compiler
import com.google.devtools.ksp.symbol.KSFile

import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeSpec

import org.ossreviewtoolkit.plugins.api.PluginDescriptor

Expand Down Expand Up @@ -52,3 +53,11 @@ data class PluginFactorySpec(
val typeName: TypeName,
val qualifiedName: String
)

/**
* A specification for a service loader, used to generate service loader files.
*/
data class ServiceLoaderSpec(
val pluginSpec: PluginSpec,
val factory: TypeSpec
)
49 changes: 49 additions & 0 deletions plugins/compiler/src/main/kotlin/ServiceLoaderGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2024 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
*
* 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.
*
* SPDX-License-Identifier: Apache-2.0
* License-Filename: LICENSE
*/

package org.ossreviewtoolkit.plugins.compiler

import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies

/**
* A class to generate service loader files for plugin factories.
*/
class ServiceLoaderGenerator(private val codeGenerator: CodeGenerator) {
fun generate(serviceLoaderSpecs: List<ServiceLoaderSpec>) {
serviceLoaderSpecs.groupBy { it.pluginSpec.factory.qualifiedName }.forEach { (factoryName, specs) ->
val containingFiles = specs.mapNotNull { it.pluginSpec.containingFile }

codeGenerator.createNewFileByPath(
dependencies = Dependencies(
aggregating = true,
*containingFiles.toTypedArray()
),
path = "META-INF/services/$factoryName",
extensionName = ""
).use { output ->
output.writer().use { writer ->
specs.forEach { spec ->
writer.write("${spec.pluginSpec.packageName}.${spec.factory.name}\n")
}
}
}
}
}
}

0 comments on commit 35d18a6

Please sign in to comment.