Skip to content

Commit 60d8f24

Browse files
committed
Cocoon compiler plugin skeleton.
1 parent ce21a77 commit 60d8f24

File tree

15 files changed

+315
-1
lines changed

15 files changed

+315
-1
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import io.gitlab.arturbosch.detekt.Detekt
2+
import org.gradle.kotlin.dsl.withType
3+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
4+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
5+
6+
plugins {
7+
alias(libs.plugins.kotlin.jvm)
8+
alias(libs.plugins.detekt)
9+
}
10+
11+
group = "io.github.reactivecircus.cocoon"
12+
version = "0.1.0"
13+
14+
kotlin {
15+
explicitApi()
16+
}
17+
18+
tasks.withType<KotlinCompile>().configureEach {
19+
compilerOptions {
20+
jvmTarget.set(JvmTarget.JVM_11)
21+
optIn.add("org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi")
22+
}
23+
}
24+
25+
tasks.withType<JavaCompile>().configureEach {
26+
sourceCompatibility = JavaVersion.VERSION_11.toString()
27+
targetCompatibility = JavaVersion.VERSION_11.toString()
28+
}
29+
30+
detekt {
31+
source.from(files("src/"))
32+
config.from(files("$rootDir/../detekt.yml"))
33+
buildUponDefaultConfig = true
34+
allRules = true
35+
parallel = true
36+
}
37+
38+
tasks.withType<Detekt>().configureEach {
39+
jvmTarget = JvmTarget.JVM_11.target
40+
reports {
41+
xml.required.set(false)
42+
txt.required.set(false)
43+
sarif.required.set(false)
44+
md.required.set(false)
45+
}
46+
}
47+
48+
dependencies {
49+
// enable Ktlint formatting
50+
add("detektPlugins", libs.plugin.detektFormatting)
51+
52+
compileOnly(libs.kotlin.compiler)
53+
compileOnly(libs.kotlin.stblib)
54+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
kotlin.stdlib.default.dependency=false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package io.github.reactivecircus.cocoon.compiler
2+
3+
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
4+
import org.jetbrains.kotlin.compiler.plugin.CliOption
5+
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
6+
import org.jetbrains.kotlin.config.CompilerConfiguration
7+
import org.jetbrains.kotlin.config.CompilerConfigurationKey
8+
9+
public class CocoonCommandLineProcessor : CommandLineProcessor {
10+
11+
override val pluginId: String = "io.github.reactivecircus.cocoon.compiler"
12+
13+
@Suppress("MaxLineLength")
14+
override val pluginOptions: Collection<AbstractCliOption> = listOf(
15+
CliOption(
16+
optionName = CompilerOptions.Annotation.toString(),
17+
valueDescription = "Fully qualified annotation class name",
18+
description = "The fully qualified name of the annotation to be used for marking functions for transformation.",
19+
),
20+
CliOption(
21+
optionName = CompilerOptions.WrappingFunction.toString(),
22+
valueDescription = "Fully qualified name of the higher-order function to be used for wrapping the transformed function's body.",
23+
description = "The fully qualified name of the function to be used for wrapping the transformed function.",
24+
),
25+
)
26+
27+
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
28+
when (option.optionName) {
29+
CompilerOptions.Annotation.toString() -> configuration.put(CompilerOptions.Annotation, value)
30+
CompilerOptions.WrappingFunction.toString() -> configuration.put(CompilerOptions.WrappingFunction, value)
31+
else -> throw IllegalArgumentException("Unknown plugin option: ${option.optionName}")
32+
}
33+
}
34+
35+
internal object CompilerOptions {
36+
val Annotation = CompilerConfigurationKey<String>("annotation")
37+
val WrappingFunction = CompilerConfigurationKey<String>("wrappingFunction")
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.github.reactivecircus.cocoon.compiler
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
4+
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
5+
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
6+
import org.jetbrains.kotlin.config.CommonConfigurationKeys
7+
import org.jetbrains.kotlin.config.CompilerConfiguration
8+
9+
public class CocoonCompilerPluginRegistrar : CompilerPluginRegistrar() {
10+
11+
override val supportsK2: Boolean = true
12+
13+
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
14+
val annotationString = requireNotNull(
15+
configuration.get(CocoonCommandLineProcessor.CompilerOptions.Annotation)
16+
)
17+
val annotationClassId = annotationString.toClassId()
18+
19+
val wrappingFunctionString = requireNotNull(
20+
configuration.get(CocoonCommandLineProcessor.CompilerOptions.WrappingFunction)
21+
)
22+
val wrappingFunctionCallableId = wrappingFunctionString.toCallableId()
23+
24+
val messageCollector = configuration.get(
25+
CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY,
26+
MessageCollector.NONE
27+
)
28+
29+
IrGenerationExtension.registerExtension(
30+
extension = CocoonIrGenerationExtension(
31+
annotationName = annotationClassId,
32+
wrappingFunctionName = wrappingFunctionCallableId,
33+
messageCollector = messageCollector,
34+
)
35+
)
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.github.reactivecircus.cocoon.compiler
2+
3+
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
4+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
5+
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
6+
import org.jetbrains.kotlin.name.CallableId
7+
import org.jetbrains.kotlin.name.ClassId
8+
9+
internal class CocoonFunctionTransformer(
10+
private val pluginContext: IrPluginContext,
11+
private val messageCollector: MessageCollector,
12+
private val annotationName: ClassId,
13+
private val wrappingFunctionName: CallableId,
14+
) : IrElementTransformerVoidWithContext() {
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.github.reactivecircus.cocoon.compiler
2+
3+
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
4+
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
5+
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
6+
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
7+
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
8+
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
9+
import org.jetbrains.kotlin.name.CallableId
10+
import org.jetbrains.kotlin.name.ClassId
11+
12+
internal class CocoonIrGenerationExtension(
13+
private val annotationName: ClassId,
14+
private val wrappingFunctionName: CallableId,
15+
private val messageCollector: MessageCollector,
16+
) : IrGenerationExtension {
17+
@OptIn(UnsafeDuringIrConstructionAPI::class)
18+
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
19+
if (pluginContext.referenceClass(annotationName) == null) {
20+
messageCollector.report(CompilerMessageSeverity.ERROR, "Could not find annotation class <$annotationName>.")
21+
return
22+
}
23+
if (pluginContext.referenceFunctions(wrappingFunctionName).isEmpty()) {
24+
messageCollector.report(
25+
CompilerMessageSeverity.ERROR,
26+
"Could not find wrapping function <$wrappingFunctionName>.",
27+
)
28+
return
29+
}
30+
moduleFragment.transform(
31+
CocoonFunctionTransformer(pluginContext, messageCollector, annotationName, wrappingFunctionName),
32+
null,
33+
)
34+
}
35+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.github.reactivecircus.cocoon.compiler
2+
3+
import org.jetbrains.kotlin.name.CallableId
4+
import org.jetbrains.kotlin.name.ClassId
5+
import org.jetbrains.kotlin.name.FqName
6+
7+
internal fun String.toClassId(): ClassId =
8+
FqName(this).run { ClassId(parent(), shortName()) }
9+
10+
internal fun String.toCallableId(): CallableId =
11+
FqName(this).run { CallableId(parent(), shortName()) }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.github.reactivecircus.cocoon.compiler.CocoonCommandLineProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.github.reactivecircus.cocoon.compiler.CocoonCompilerPluginRegistrar
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import io.gitlab.arturbosch.detekt.Detekt
2+
import org.gradle.kotlin.dsl.withType
3+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
4+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
5+
6+
plugins {
7+
`kotlin-dsl`
8+
alias(libs.plugins.detekt)
9+
}
10+
11+
gradlePlugin {
12+
plugins {
13+
register("cocoon") {
14+
id = "io.github.reactivecircus.cocoon"
15+
implementationClass = "io.github.reactivecircus.cocoon.gradle.CocoonPlugin"
16+
}
17+
}
18+
}
19+
20+
kotlin {
21+
explicitApi()
22+
}
23+
24+
tasks.withType<KotlinCompile>().configureEach {
25+
compilerOptions {
26+
jvmTarget.set(JvmTarget.JVM_11)
27+
}
28+
}
29+
30+
tasks.withType<JavaCompile>().configureEach {
31+
sourceCompatibility = JavaVersion.VERSION_11.toString()
32+
targetCompatibility = JavaVersion.VERSION_11.toString()
33+
}
34+
35+
detekt {
36+
source.from(files("src/"))
37+
config.from(files("$rootDir/../detekt.yml"))
38+
buildUponDefaultConfig = true
39+
allRules = true
40+
parallel = true
41+
}
42+
43+
tasks.withType<Detekt>().configureEach {
44+
jvmTarget = JvmTarget.JVM_11.target
45+
reports {
46+
xml.required.set(false)
47+
txt.required.set(false)
48+
sarif.required.set(false)
49+
md.required.set(false)
50+
}
51+
}
52+
53+
dependencies {
54+
// enable Ktlint formatting
55+
add("detektPlugins", libs.plugin.detektFormatting)
56+
57+
compileOnly(libs.plugin.kotlin)
58+
}

0 commit comments

Comments
 (0)