Skip to content

Add Kotlin 2.0+ support for Gradle and IR compiler plugin (#435) - #487

Merged
KevinSchildhorn merged 4 commits into
touchlab:mainfrom
kareemessam09:feat/kotlin-2-compiler-plugin
Sep 29, 2026
Merged

KevinSchildhorn merged 4 commits into
touchlab:mainfrom
kareemessam09:feat/kotlin-2-compiler-plugin

Conversation

@kareemessam09

Copy link
Copy Markdown
Contributor

Fixes #435
Context: #424 (comment) (cc @samhill303)

Summary

Restores and modernizes the Kermit Gradle plugin and IR compiler plugin for Kotlin 2.0+ (K2) support. In Kermit 1.x, the plugin was used to strip log statements at compile-time, but was disabled during the 2.0 migration due to Kotlin compiler internal changes.

Implementation notes

  • CompilerPluginRegistrar Migration: Replaced deprecated ComponentRegistrar with CompilerPluginRegistrar with supportsK2 = true and pluginId. Extensions are now registered via ExtensionStorage.
  • IR API Updates: Switched from deprecated referenceClass(FqName) to ClassId, and migrated isSubtypeOfClass to org.jetbrains.kotlin.ir.util.isSubtypeOfClass. Added @OptIn(UnsafeDuringIrConstructionAPI::class, DeprecatedForRemovalCompilerApi::class) to handle IR receiver inspection safely.
  • Removed Native Subplugin: Deleted the obsolete kermit-ir-plugin-native module and removed getPluginArtifactForNative() from KermitGradlePlugin, as Kotlin 2.0+ unifies native IR compiler plugins via standard JVM jar artifacts.
  • Service Loader Descriptors: Replaced kapt and auto-service with direct META-INF/services descriptor files for CompilerPluginRegistrar and CommandLineProcessor.
  • Testing Modernization: Updated test framework to dev.zacsweers.kctfork:core:0.12.1 (aligned with the project's Kotlin 2.3 toolchain). Un-ignored and updated all compiler tests, including an active runtime execution test that verifies stripped log lambdas produce zero invocations/side-effects.
  • Zero Impact on Library Code: Existing library modules (kermit, kermit-core, kermit-test, samples) are completely untouched.

Testing

  • Built the project and ran its test suite locally
  • Added or updated tests covering this change
  • Verified by hand

Commands run locally:

  • ./gradlew :kermit-ir-plugin:test (5/5 tests passed, including runtime bytecode execution test)
  • ./gradlew :kermit-gradle-plugin:test (3/3 unit tests passed)
  • ./gradlew :kermit:jvmTest :kermit-core:jvmTest (existing modules pass with zero regressions)
  • ./gradlew :kermit-ir-plugin:ktlintCheck :kermit-gradle-plugin:ktlintCheck (0 lint violations)

Platforms verified

  • Verified: Linux (linux_x64), JVM targets, and Kotlin IR compiler plugin transformations.
  • Skipped: Apple native targets (macos, ios, watchos, tvos) were not built locally due to running on a Linux host without Apple SDKs.

User-facing impact

  • User-facing change

Users on Kotlin 2.0+ can once again apply id("co.touchlab.kermit") in Gradle and configure kermit { stripBelow = StripSeverity.<Level> } to strip log statements at compile time.

Documentation

  • No documentation changes needed
    (Note: Happy to add a documentation page restoring the "Kermit Chisel" guide in website/docs/ if requested).

@faogustavo
faogustavo requested review from faogustavo and a balanced review from Copilot September 8, 2026 14:07
@faogustavo

Copy link
Copy Markdown
Contributor

Thanks for the contribution. I'll try to take some time to review it this week.

Added Copilot for an initial round

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Kotlin 2.0 compatibility is unverified, and a supported non-embeddable Native configuration no longer receives the compiler plugin.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Restores Kermit’s Gradle and IR compiler plugins for modern Kotlin/K2 toolchains.

Changes:

  • Migrates compiler registration and IR APIs to K2-compatible interfaces.
  • Removes the legacy Native-specific plugin artifact.
  • Restores publishing and expands compiler/plugin tests.
File summaries
File Description
settings.gradle.kts Re-enables plugin modules.
plugin/kermit-ir-plugin/src/test/kotlin/co/touchlab/kermit/irplugin/KermitStripperIrPluginTest.kt Restores compiler and runtime tests.
plugin/kermit-ir-plugin/src/test/kotlin/co/touchlab/kermit/irplugin/Helper.kt Updates the test logger fixture.
plugin/kermit-ir-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar Registers the compiler registrar.
plugin/kermit-ir-plugin/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor Registers option processing.
plugin/kermit-ir-plugin/src/main/kotlin/co/touchlab/kermit/irplugin/KermitIrGenerationExtension.kt Simplifies IR transformation setup.
plugin/kermit-ir-plugin/src/main/kotlin/co/touchlab/kermit/irplugin/KermitCompilerPluginRegistrar.kt Migrates registration to K2 APIs.
plugin/kermit-ir-plugin/src/main/kotlin/co/touchlab/kermit/irplugin/KermitCommandLineProcessor.kt Updates compiler API opt-in.
plugin/kermit-ir-plugin/src/main/kotlin/co/touchlab/kermit/irplugin/KermitChiselTransformer.kt Modernizes IR lookup and transformation.
plugin/kermit-ir-plugin/build.gradle.kts Updates testing and publishing.
plugin/kermit-ir-plugin-native/gradle.properties Removes Native artifact metadata.
plugin/kermit-ir-plugin-native/build.gradle.kts Removes the legacy Native module.
plugin/kermit-ir-plugin-native/.gitignore Removes obsolete module configuration.
plugin/kermit-gradle-plugin/src/test/kotlin/co/touchlab/kermit/gradle/KermitGradlePluginTest.kt Adds Gradle plugin unit tests.
plugin/kermit-gradle-plugin/src/main/kotlin/co/touchlab/kermit/gradle/StripSeverity.kt Applies formatting updates.
plugin/kermit-gradle-plugin/src/main/kotlin/co/touchlab/kermit/gradle/KermitGradlePlugin.kt Uses the unified compiler artifact.
plugin/kermit-gradle-plugin/build.gradle.kts Adds tests and publishing configuration.
gradle/libs.versions.toml Adds compiler-testing and publishing aliases.
build.gradle.kts Enables publishing and API exclusions.
Review details
  • Files reviewed: 18/19 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

override fun applyToCompilation(
kotlinCompilation: KotlinCompilation<*>
): Provider<List<SubpluginOption>> {
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
testImplementation(kotlin("test-junit"))
testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
testImplementation("com.github.tschuchortdev:kotlin-compile-testing:1.4.8")
testImplementation("dev.zacsweers.kctfork:core:0.12.1")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use the item from the version catalog?

@kareemessam09

Copy link
Copy Markdown
Contributor Author

Hi @faogustavo, thanks for taking a look! Looking forward to your review whenever you have time this week.

In the meantime, I looked into the points raised by Copilot's automated review:

  1. Kotlin 2.0. Compatibility & pluginId:*

    • I tested the published plugin end-to-end against consumer projects across Kotlin 2.0.0, 2.0.21, 2.1.0, 2.2.0, and 2.3.21. All versions compile cleanly and properly strip log statements and lambdas at runtime.
    • While abstract val pluginId was introduced in Kotlin 2.3.0, implementing it in KermitCompilerPluginRegistrar provides clean forward/backward compatibility: on Kotlin 2.0–2.2 runtimes, the compiler ignores the extra getter method, while on Kotlin 2.3+ it fulfills the required abstract property.
  2. Native getPluginArtifactForNative():

    • In Kotlin 2.0+, kotlin.native.useEmbeddableCompilerJar=true is enabled by default, unifying Native compiler plugins with JVM via getPluginArtifact().
    • JetBrains has officially deprecated getPluginArtifactForNative() in KGP and scheduled its removal in Kotlin 2.4 in favor of this unified embeddable plugin artifact.
  3. Expanded Test Suite (Commit bc070b4):

    • Added runtime bytecode stripping tests for Logger.withTag(...) chaining, Logger subclasses (class CustomLogger : Logger()), and Throwable overloads.
    • Added unit tests for KermitCommandLineProcessor (ARG_STRIP_BELOW configuration and argument validation).
    • Added unit tests for KermitGradlePlugin compilation options mapping.

@kareemessam09
kareemessam09 requested a balanced review from Copilot September 8, 2026 16:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@faogustavo faogustavo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also update any of the samples to include an example of plugin usage?

Comment on lines +282 to +287
@OptIn(ExperimentalCompilerApi::class)
fun compile(sourceFiles: List<SourceFile>, stripBelow: String = "None"): JvmCompilationResult = KotlinCompilation().apply {
sources = sourceFiles
compilerPluginRegistrars = listOf(KermitCompilerPluginRegistrar(stripBelow))
inheritClassPath = true
}.compile()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests seem to only run a JVM compilation. Is there a way to also test a kotlin native compilation?

testImplementation(kotlin("test-junit"))
testImplementation("org.jetbrains.kotlin:kotlin-compiler-embeddable")
testImplementation("com.github.tschuchortdev:kotlin-compile-testing:1.4.8")
testImplementation("dev.zacsweers.kctfork:core:0.12.1")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please use the item from the version catalog?

@kareemessam09

Copy link
Copy Markdown
Contributor Author

Hi @faogustavo, thanks for the review! I've addressed the feedback in the last commit:

  1. Version Catalog:
    Updated plugin/kermit-ir-plugin/build.gradle.kts to reference libs.kctfork and libs.kotlin.compiler.embeddable from libs.versions.toml.

  2. Sample Plugin Usage:
    Updated samples/sample-production to demonstrate plugin usage and log stripping:

    • Configured co.touchlab.kermit in root build.gradle.kts.
    • Applied id("co.touchlab.kermit") in shared/build.gradle.kts with stripBelow = StripSeverity.Info.
    • Updated samples/sample-production/README.md with documentation on configuring the plugin and severity thresholds.
  3. Kotlin/Native Compilation Testing:

    • The unit tests in KermitStripperIrPluginTest use dev.zacsweers.kctfork (Kotlin Compile Testing), which compiles in-memory via K2JVMCompiler. This allows fast in-process testing of the IR tree transformations and reflective execution to verify runtime behavior.
    • Because KermitChiselTransformer is an IrGenerationExtension operating purely on the unified K2 IR frontend before backend code generation, the AST stripping transformation is identical across all targets (JVM, Native, JS, Wasm). kctfork does not support invoking konanc directly because Kotlin/Native requires the host Konan toolchain (LLVM, platform sysroots, linker).
    • To verify the Kotlin/Native pipeline end-to-end with the plugin applied, updating samples/sample-production (which targets iOS: iosX64, iosArm64, iosSimulatorArm64) ensures that every CI run (./ci-test-samples.sh after publishToMavenLocal) compiles the native targets with the Kermit compiler plugin registered and active.

@faogustavo

Copy link
Copy Markdown
Contributor

I think that it's working well. I just tested it locally, and everything works as expected. I'm just waiting CI checks to complete #488 to fix flaky JS tests. If that works, you can rebase this PR, and we should be good to go :)

@kareemessam09

Copy link
Copy Markdown
Contributor Author

Hi @faogustavo,

Sounds good! I'll hold off on rebasing until #488 is merged into main.

In the meantime, I noticed the Windows CI failure on this PR was unrelated to the JS tests—it failed because ktlint was checking generated sources (BuildConfig.kt) due to backslashes () on Windows not matching contains("build/") in the root build.gradle.kts exclusion filter. I just pushed a fix normalizing the path separator via invariantSeparatorsPath, which should get the Windows check green once approved.

Also, as a quick heads up on #488: the Mac failure there was ChromeHeadless has not captured in 60000 ms, killing.—in Karma that specific timeout is governed by config.captureTimeout (defaults to 60000 ms), rather than browserNoActivityTimeout or browserDisconnectTimeout. Increasing captureTimeout in the karma config might help get #488 green!

Let me know once #488 lands and I'll rebase right away.

@kareemessam09

Copy link
Copy Markdown
Contributor Author

@faogustavo any updates? or anything i can add?

@faogustavo

Copy link
Copy Markdown
Contributor

@faogustavo any updates? or anything i can add?

Nothing yet; I'll let you know when we are ready so you can rebase.

@faogustavo

Copy link
Copy Markdown
Contributor

@kareemessam09, you can rebase it now. Once you do, I'll rerun the CI pipelines to run the checks, and hopefully we will be good to go.

Migrate kermit-ir-plugin to Kotlin 2.0+ CompilerPluginRegistrar with supportsK2 enabled.
- Replace ComponentRegistrar with CompilerPluginRegistrar
- Register IR extension via ExtensionStorage and use ClassId and org.jetbrains.kotlin.ir.util.isSubtypeOfClass
- Drop obsolete kermit-ir-plugin-native module (unneeded in Kotlin 2.0+)
- Remove getPluginArtifactForNative from KermitGradlePlugin
- Replace kapt and auto-service with direct META-INF/services declarations
- Modernize test suite with kctfork and add runtime execution verification test
- Add KermitGradlePluginTest unit tests and ensure ktlint passes
@kareemessam09
kareemessam09 force-pushed the feat/kotlin-2-compiler-plugin branch from 78c1c53 to 61c8743 Compare September 15, 2026 20:00
@kareemessam09

Copy link
Copy Markdown
Contributor Author

Rebased and pushed! Tests and ktlint checks passed locally as well. Ready for CI whenever you are. Thanks!

@kareemessam09

Copy link
Copy Markdown
Contributor Author

hi!
Any updates on merging this?

@KevinSchildhorn
KevinSchildhorn merged commit 3a4042f into touchlab:main Sep 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gradle plugin not published for Kotlin 2.0

4 participants