Skip to content

Commit

Permalink
Support JVM META-INF generation
Browse files Browse the repository at this point in the history
  • Loading branch information
whyoleg committed Jan 27, 2025
1 parent f7116d1 commit 373eea4
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,32 @@
package dev.whyoleg.sweetspi.compiler

import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.*
import kotlin.io.path.*

@OptIn(ExperimentalCompilerApi::class)
class SweetCommandLineProcessor : CommandLineProcessor {
override val pluginId: String = "dev.whyoleg.sweetspi"
override val pluginOptions: Collection<CliOption> = emptyList()
override val pluginOptions: Collection<CliOption> = listOf(
RESOURCES_PATH
)

override fun processOption(
option: AbstractCliOption,
value: String,
configuration: CompilerConfiguration,
): Unit = when (option.optionName) {
RESOURCES_PATH.optionName -> configuration.put(SweetConfigurationKeys.RESOURCES_PATH, Path(value))
else -> error("Unknown plugin option: ${option.optionName}")
}

companion object {
private val RESOURCES_PATH = CliOption(
optionName = "resourcesPath",
valueDescription = "<path>",
description = SweetConfigurationKeys.RESOURCES_PATH.toString(),
required = true,
allowMultipleOccurrences = false,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class SweetCompilerPluginRegistrar : CompilerPluginRegistrar() {

override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
FirExtensionRegistrarAdapter.registerExtension(SweetFirExtensionRegistrar())
IrGenerationExtension.registerExtension(SweetIrGenerationExtension(configuration.irMessageLogger))
IrGenerationExtension.registerExtension(
SweetIrGenerationExtension(
configuration.irMessageLogger,
configuration.getNotNull(SweetConfigurationKeys.RESOURCES_PATH)
)
)
}
}
12 changes: 12 additions & 0 deletions sweetspi-compiler-plugin/src/main/kotlin/SweetConfigurationKeys.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2025 Oleg Yukhnevich. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.whyoleg.sweetspi.compiler

import org.jetbrains.kotlin.config.*
import java.nio.file.*

object SweetConfigurationKeys {
val RESOURCES_PATH = CompilerConfigurationKey<Path>("Path to resources dir")
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.platform.jvm.*
import java.nio.file.*
import kotlin.io.path.*

private val SweetOrigin: IrDeclarationOrigin = IrDeclarationOriginImpl("SWEET_SPI")

Expand All @@ -38,6 +40,7 @@ private val SweetOrigin: IrDeclarationOrigin = IrDeclarationOriginImpl("SWEET_SP
@OptIn(UnsafeDuringIrConstructionAPI::class)
class SweetIrGenerationExtension(
private val logger: IrMessageLogger,
private val resourcesPath: Path,
) : IrGenerationExtension {

override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
Expand All @@ -60,17 +63,12 @@ class SweetIrGenerationExtension(
}.associateBy(IrClass::classIdOrFail) // should be called ONLY after `addChild`

// handle @ServiceProvider
moduleFragment.files.forEach { file ->
val serviceProviders = moduleFragment.files.flatMap { file ->
file.declarations.flatMap { declaration ->
val serviceTypes = findDeclaredServiceTypes(declaration)
?.ifEmpty { resolveServiceTypes(declaration) }
?: return@flatMap emptyList()

// messageCollector.report(
// CompilerMessageSeverity.WARNING,
// "$declaration: ${serviceTypes.joinToString { it.classFqName!!.asString() }}"
// )

// TODO: add checkers for invalid combinations
when (declaration) {
is IrClass -> buildJvmServiceProviderImplClasses(
Expand Down Expand Up @@ -102,7 +100,17 @@ class SweetIrGenerationExtension(
}
else -> emptyList()
}
}.forEach(file::addChild)
}.onEach(file::addChild)
}.groupBy { it.superTypes.single() }

// drop all resources first
resourcesPath.toFile().deleteRecursively()
serviceProviders.forEach { (superType, providers) ->
resourcesPath.resolve(
"META-INF/services/${superType.classOrFail.owner.kotlinFqName.asString()}"
).createParentDirectories().writeText(
providers.joinToString("\n") { it.kotlinFqName.asString() }
)
}
}

Expand Down
21 changes: 19 additions & 2 deletions sweetspi-gradle-plugin/src/main/kotlin/SweetSpiPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,25 @@ public abstract class SweetSpiPlugin @Inject constructor(
)

override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true

override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
// TODO: for JVM we should provide folder for resources
return kotlinCompilation.target.project.provider { emptyList() }
val project = kotlinCompilation.target.project

val resourcesPath = project.layout.buildDirectory.dir(
"generated/sweetspi/${kotlinCompilation.defaultSourceSet.name}/resources"
)
// cross-link compilation and resource processing tasks via resources dir output
kotlinCompilation.compileTaskProvider.configure {
it.outputs.dir(resourcesPath).withPropertyName("sweetspi.resourcesPath")
}
kotlinCompilation.defaultSourceSet.resources.srcDir(
kotlinCompilation.compileTaskProvider.map { resourcesPath.get() }
)

return project.provider {
listOf(
SubpluginOption("resourcesPath", resourcesPath.get().toString())
)
}
}
}

This file was deleted.

0 comments on commit 373eea4

Please sign in to comment.