Skip to content

Commit 8a471ca

Browse files
committed
Support new CompilerPluginRegistrar in 1.8.0 and K2 plugins
1 parent bff434c commit 8a471ca

File tree

11 files changed

+203
-138
lines changed

11 files changed

+203
-138
lines changed

core/src/main/kotlin/com/tschuchort/compiletesting/AbstractKotlinCompilation.kt

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
1111
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
1212
import org.jetbrains.kotlin.cli.js.K2JSCompiler
1313
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
14+
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
1415
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
16+
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
1517
import org.jetbrains.kotlin.config.Services
1618
import org.jetbrains.kotlin.load.java.JvmAbi
1719
import org.jetbrains.kotlin.util.ServiceLoaderLite
1820
import java.io.File
1921
import java.io.OutputStream
2022
import java.io.PrintStream
21-
import java.lang.reflect.InvocationTargetException
22-
import java.lang.reflect.ReflectPermission
2323
import java.net.URI
2424
import java.nio.file.Files
2525
import java.nio.file.Paths
@@ -29,6 +29,7 @@ import java.nio.file.Paths
2929
* functionality. Should not be used outside of this library as it is an
3030
* implementation detail.
3131
*/
32+
@ExperimentalCompilerApi
3233
abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal constructor() {
3334
/** Working directory for the compilation */
3435
var workingDir: File by default {
@@ -49,10 +50,29 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
4950
*/
5051
var pluginClasspaths: List<File> = emptyList()
5152

53+
54+
@Suppress("DEPRECATION")
55+
@Deprecated(
56+
"Renamed to componentRegistrars due to introduction of CompilerPluginRegistrar",
57+
ReplaceWith("componentRegistrars"),
58+
DeprecationLevel.ERROR
59+
)
60+
var compilerPlugins: List<ComponentRegistrar> = emptyList()
61+
62+
63+
64+
/**
65+
* Legacy compiler plugins that should be added to the compilation.
66+
* This option will be removed in the future; you should migrate to [CompilerPluginRegistrar].
67+
*/
68+
@Suppress("DEPRECATION")
69+
@Deprecated("Deprecated in Kotlin compiler. Migrate to compilerPluginRegistrars instead")
70+
var componentRegistrars: List<ComponentRegistrar> = emptyList()
71+
5272
/**
5373
* Compiler plugins that should be added to the compilation
5474
*/
55-
var compilerPlugins: List<ComponentRegistrar> = emptyList()
75+
var compilerPluginRegistrars: List<CompilerPluginRegistrar> = emptyList()
5676

5777
/**
5878
* Commandline processors for compiler plugins that should be added to the compilation
@@ -115,6 +135,9 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
115135
HostEnvironment.kotlinStdLibCommonJar
116136
}
117137

138+
/** Enable support for the new K2 compiler. */
139+
var supportsK2 = false
140+
118141
// Directory for input source files
119142
protected val sourcesDir get() = workingDir.resolve("sources")
120143

@@ -188,11 +211,14 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
188211
* To avoid that the annotation processors are executed twice,
189212
* the list is set to empty
190213
*/
191-
MainComponentRegistrar.threadLocalParameters.set(
192-
MainComponentRegistrar.ThreadLocalParameters(
214+
@Suppress("DEPRECATION")
215+
MainComponentAndPluginRegistrar.threadLocalParameters.set(
216+
MainComponentAndPluginRegistrar.ThreadLocalParameters(
193217
listOf(),
194218
KaptOptions.Builder(),
195-
compilerPlugins
219+
componentRegistrars,
220+
compilerPluginRegistrars,
221+
supportsK2
196222
)
197223
)
198224

@@ -207,14 +233,15 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
207233
} else {
208234
emptyList()
209235
}
236+
@Suppress("DEPRECATION")
210237
args.pluginClasspaths = (args.pluginClasspaths ?: emptyArray()) +
211238
/** The resources path contains the MainComponentRegistrar and MainCommandLineProcessor which will
212239
be found by the Kotlin compiler's service loader. We add it only when the user has actually given
213240
us ComponentRegistrar instances to be loaded by the MainComponentRegistrar because the experimental
214241
K2 compiler doesn't support plugins yet. This way, users of K2 can prevent MainComponentRegistrar
215-
from being loaded and crashing K2 by setting both [compilerPlugins] and [commandLineProcessors] to
216-
the emptyList. */
217-
if (compilerPlugins.isNotEmpty() || commandLineProcessors.isNotEmpty())
242+
from being loaded and crashing K2 by setting both [componentRegistrars] and [commandLineProcessors]
243+
and [compilerPluginRegistrars] to the emptyList. */
244+
if (componentRegistrars.union(compilerPluginRegistrars).union(commandLineProcessors).isNotEmpty())
218245
arrayOf(getResourcesPath())
219246
else emptyArray()
220247
}
@@ -229,7 +256,7 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
229256
}
230257

231258
protected fun getResourcesPath(): String {
232-
val resourceName = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar"
259+
val resourceName = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar"
233260
return this::class.java.classLoader.getResources(resourceName)
234261
.asSequence()
235262
.mapNotNull { url ->
@@ -241,9 +268,9 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
241268
}.toAbsolutePath()
242269
}
243270
.find { resourcesPath ->
244-
ServiceLoaderLite.findImplementations(ComponentRegistrar::class.java, listOf(resourcesPath.toFile()))
245-
.any { implementation -> implementation == MainComponentRegistrar::class.java.name }
246-
}?.toString() ?: throw AssertionError("Could not get path to ComponentRegistrar service from META-INF")
271+
ServiceLoaderLite.findImplementations(CompilerPluginRegistrar::class.java, listOf(resourcesPath.toFile()))
272+
.any { implementation -> implementation == MainComponentAndPluginRegistrar::class.java.name }
273+
}?.toString() ?: throw AssertionError("Could not get path to CompilerPluginRegistrar service from META-INF")
247274
}
248275

249276
/** Searches compiler log for known errors that are hard to debug for the user */
@@ -298,7 +325,6 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
298325

299326
internal fun convertKotlinExitCode(code: ExitCode) = when(code) {
300327
ExitCode.OK -> KotlinCompilation.ExitCode.OK
301-
ExitCode.OOM_ERROR,
302328
ExitCode.INTERNAL_ERROR -> KotlinCompilation.ExitCode.INTERNAL_ERROR
303329
ExitCode.COMPILATION_ERROR -> KotlinCompilation.ExitCode.COMPILATION_ERROR
304330
ExitCode.SCRIPT_EXECUTION_ERROR -> KotlinCompilation.ExitCode.SCRIPT_EXECUTION_ERROR

core/src/main/kotlin/com/tschuchort/compiletesting/KaptComponentRegistrar.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ import org.jetbrains.kotlin.kapt3.base.LoadedProcessors
4343
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
4444
import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
4545
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
46+
import org.jetbrains.kotlin.kapt3.util.doOpenInternalPackagesIfRequired
4647
import org.jetbrains.kotlin.psi.KtFile
4748
import org.jetbrains.kotlin.resolve.BindingTrace
4849
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
4950
import org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension
5051
import java.io.File
5152

53+
@Suppress("DEPRECATION")
5254
internal class KaptComponentRegistrar(
5355
private val processors: List<IncrementalProcessor>,
5456
private val kaptOptions: KaptOptions.Builder
@@ -58,6 +60,8 @@ internal class KaptComponentRegistrar(
5860
if (processors.isEmpty())
5961
return
6062

63+
doOpenInternalPackagesIfRequired()
64+
6165
val contentRoots = configuration[CLIConfigurationKeys.CONTENT_ROOTS] ?: emptyList()
6266

6367
val optionsBuilder = kaptOptions.apply {

core/src/main/kotlin/com/tschuchort/compiletesting/KotlinCompilation.kt

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,9 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
9191
/** Don't generate not-null assertions on parameters of methods accessible from Java */
9292
var noParamAssertions: Boolean = false
9393

94-
/** Generate nullability assertions for non-null Java expressions */
95-
@Deprecated("Removed in latest Kotlin version")
96-
var strictJavaNullabilityAssertions: Boolean? = null
97-
9894
/** Disable optimizations */
9995
var noOptimize: Boolean = false
10096

101-
/**
102-
* Normalize constructor calls (disable: don't normalize; enable: normalize),
103-
* default is 'disable' in language version 1.2 and below, 'enable' since language version 1.3
104-
*
105-
* {disable|enable}
106-
*/
107-
@Deprecated("Removed in latest Kotlin version")
108-
var constructorCallNormalizationMode: String? = null
10997

11098
/** Assert calls behaviour {always-enable|always-disable|jvm|legacy} */
11199
var assertionsMode: String? = JVMAssertionsMode.DEFAULT.description
@@ -119,9 +107,6 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
119107
/** Use type table in metadata serialization */
120108
var useTypeTable: Boolean = false
121109

122-
/** Allow Kotlin runtime libraries of incompatible versions in the classpath */
123-
@Deprecated("Removed in latest Kotlin version")
124-
var skipRuntimeVersionCheck: Boolean? = null
125110

126111
/** Path to JSON file to dump Java to Kotlin declaration mappings */
127112
var declarationsOutputPath: File? = null
@@ -144,9 +129,6 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
144129
*/
145130
var supportCompatqualCheckerFrameworkAnnotations: String? = null
146131

147-
/** Do not throw NPE on explicit 'equals' call for null receiver of platform boxed primitive type */
148-
@Deprecated("Removed in latest Kotlin version")
149-
var noExceptionOnExplicitEqualsForBoxedNull: Boolean? = null
150132

151133
/** Allow to use '@JvmDefault' annotation for JVM default method support.
152134
* {disable|enable|compatibility}
@@ -330,17 +312,8 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
330312
args.noParamAssertions = noParamAssertions
331313
args.noReceiverAssertions = noReceiverAssertions
332314

333-
// TODO: Remove after kotlin 1.6.30
334-
if(strictJavaNullabilityAssertions != null)
335-
args.trySetDeprecatedOption("strictJavaNullabilityAssertions", strictJavaNullabilityAssertions)
336-
337315
args.noOptimize = noOptimize
338316

339-
// TODO: Remove after kotlin 1.6.30
340-
if(constructorCallNormalizationMode != null)
341-
args.trySetDeprecatedOption("constructorCallNormalizationMode", constructorCallNormalizationMode)
342-
343-
344317
if(assertionsMode != null)
345318
args.assertionsMode = assertionsMode
346319

@@ -370,14 +343,6 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
370343
if(scriptResolverEnvironment.isNotEmpty())
371344
args.scriptResolverEnvironment = scriptResolverEnvironment.map { (key, value) -> "$key=\"$value\"" }.toTypedArray()
372345

373-
// TODO: Remove after kotlin 1.6.30
374-
if(noExceptionOnExplicitEqualsForBoxedNull != null)
375-
args.trySetDeprecatedOption("noExceptionOnExplicitEqualsForBoxedNull", noExceptionOnExplicitEqualsForBoxedNull)
376-
377-
// TODO: Remove after kotlin 1.6.30
378-
if(skipRuntimeVersionCheck != null)
379-
args.trySetDeprecatedOption("skipRuntimeVersionCheck", skipRuntimeVersionCheck)
380-
381346
args.javaPackagePrefix = javaPackagePrefix
382347
args.suppressMissingBuiltinsError = suppressMissingBuiltinsError
383348
}
@@ -418,11 +383,14 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
418383
* any parameters that change between compilations
419384
*
420385
*/
421-
MainComponentRegistrar.threadLocalParameters.set(
422-
MainComponentRegistrar.ThreadLocalParameters(
386+
@Suppress("DEPRECATION")
387+
MainComponentAndPluginRegistrar.threadLocalParameters.set(
388+
MainComponentAndPluginRegistrar.ThreadLocalParameters(
423389
annotationProcessors.map { IncrementalProcessor(it, DeclaredProcType.NON_INCREMENTAL, kaptLogger) },
424390
kaptOptions,
425-
compilerPlugins
391+
componentRegistrars,
392+
compilerPluginRegistrars,
393+
supportsK2
426394
)
427395
)
428396

@@ -653,7 +621,7 @@ class KotlinCompilation : AbstractKotlinCompilation<K2JVMCompilerArguments>() {
653621
return makeResult(exitCode)
654622
}
655623
} finally {
656-
MainComponentRegistrar.threadLocalParameters.remove()
624+
MainComponentAndPluginRegistrar.threadLocalParameters.remove()
657625
}
658626

659627
// step 3: compile Kotlin files

core/src/main/kotlin/com/tschuchort/compiletesting/KotlinJsCompilation.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class KotlinJsCompilation : AbstractKotlinCompilation<K2JSCompilerArguments>() {
1919
var irProduceKlibFile: Boolean = false
2020

2121
/** Generates JS file using IR backend. Also disables pre-IR backend */
22-
var irProduceJs: Boolean = false
22+
var irProduceJs: Boolean = true
2323

2424
/** Perform experimental dead code elimination */
2525
var irDce: Boolean = false
@@ -28,7 +28,7 @@ class KotlinJsCompilation : AbstractKotlinCompilation<K2JSCompilerArguments>() {
2828
var irDcePrintReachabilityInfo: Boolean = false
2929

3030
/** Disables pre-IR backend */
31-
var irOnly: Boolean = false
31+
var irOnly: Boolean = true
3232

3333
/** Specify a compilation module name for IR backend */
3434
var irModuleName: String? = null
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2010-2016 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.tschuchort.compiletesting
18+
19+
import com.google.auto.service.AutoService
20+
import org.jetbrains.kotlin.base.kapt3.KaptOptions
21+
import org.jetbrains.kotlin.com.intellij.mock.MockProject
22+
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
23+
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
24+
import org.jetbrains.kotlin.config.CompilerConfiguration
25+
import org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor
26+
27+
@Suppress("DEPRECATION")
28+
@AutoService(ComponentRegistrar::class, CompilerPluginRegistrar::class)
29+
internal class MainComponentAndPluginRegistrar : ComponentRegistrar, CompilerPluginRegistrar() {
30+
31+
override val supportsK2: Boolean
32+
get() = safeGetThreadLocalParameters(logCallerName = "supportsK2")?.supportsK2 ?: false
33+
34+
// Handle unset parameters gracefully because this plugin may be accidentally called by other tools that
35+
// discover it on the classpath (for example the kotlin jupyter kernel).
36+
private fun safeGetThreadLocalParameters(logCallerName: String): ThreadLocalParameters? {
37+
val params = threadLocalParameters.get()
38+
if (params == null) {
39+
System.err.println("WARNING: ${MainComponentAndPluginRegistrar::class.simpleName}::$logCallerName accessed before thread local parameters have been set.")
40+
}
41+
42+
return params
43+
}
44+
45+
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
46+
val parameters = safeGetThreadLocalParameters(logCallerName = "registerExtensions") ?: return
47+
48+
parameters.compilerPluginRegistrars.forEach { pluginRegistrar ->
49+
with(pluginRegistrar) {
50+
registerExtensions(configuration)
51+
}
52+
}
53+
}
54+
55+
// Legacy plugins
56+
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
57+
val parameters = safeGetThreadLocalParameters(logCallerName = "registerProjectComponents") ?: return
58+
59+
/*
60+
* The order of registering plugins here matters. If the kapt plugin is registered first, then
61+
* it will be executed first and any changes made to the AST by later plugins won't apply to the
62+
* generated stub files and thus won't be visible to any annotation processors. So we decided
63+
* to register third-party plugins before kapt and hope that it works, although we don't
64+
* know for sure if that is the correct way.
65+
*/
66+
parameters.componentRegistrars.forEach { componentRegistrar ->
67+
componentRegistrar.registerProjectComponents(project, configuration)
68+
}
69+
70+
KaptComponentRegistrar(parameters.processors, parameters.kaptOptions)
71+
.registerProjectComponents(project, configuration)
72+
}
73+
74+
75+
companion object {
76+
/** This compiler plugin is instantiated by K2JVMCompiler using
77+
* a service locator. So we can't just pass parameters to it easily.
78+
* Instead, we need to use a thread-local global variable to pass
79+
* any parameters that change between compilations
80+
*/
81+
val threadLocalParameters: ThreadLocal<ThreadLocalParameters> = ThreadLocal()
82+
}
83+
84+
data class ThreadLocalParameters(
85+
val processors: List<IncrementalProcessor>,
86+
val kaptOptions: KaptOptions.Builder,
87+
@Deprecated("Remove once ComponentRegistrar is deprecated with error")
88+
val componentRegistrars: List<ComponentRegistrar>,
89+
val compilerPluginRegistrars: List<CompilerPluginRegistrar>,
90+
val supportsK2: Boolean
91+
)
92+
}

0 commit comments

Comments
 (0)