[CLI] Introduce new compiler arguments for registering compiler plugins

With new syntax each plugin should be registered in separate argument with syntax
`-Xcompiler-plugin=classpath1,classpath2[=argument1=value1,argument2=value2]`
This commit is contained in:
Dmitriy Novozhilov
2022-06-29 15:02:58 +03:00
committed by teamcity
parent 8919703448
commit 928416c9c5
39 changed files with 361 additions and 67 deletions
@@ -170,6 +170,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
protected fun loadPlugins(paths: KotlinPaths?, arguments: A, configuration: CompilerConfiguration): ExitCode {
val pluginClasspaths = arguments.pluginClasspaths.orEmpty().toMutableList()
val pluginOptions = arguments.pluginOptions.orEmpty().toMutableList()
val pluginConfigurations = arguments.pluginConfigurations.orEmpty().toMutableList()
val messageCollector = configuration.getNotNull(MESSAGE_COLLECTOR_KEY)
for (classpath in pluginClasspaths) {
@@ -178,6 +179,25 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
}
}
if (pluginConfigurations.isNotEmpty() && (pluginClasspaths.isNotEmpty() || pluginOptions.isNotEmpty())) {
val message = buildString {
appendLine("Mixing legacy and modern plugin arguments is prohibited. Please use only one syntax")
appendLine("Legacy arguments:")
if (pluginClasspaths.isNotEmpty()) {
appendLine(" -Xplugin=${pluginClasspaths.joinToString(",")}")
}
pluginOptions.forEach {
appendLine(" -P $it")
}
appendLine("Modern arguments:")
pluginConfigurations.forEach {
appendLine(" -Xcompiler-plugin=$it")
}
}
messageCollector.report(ERROR, message)
return INTERNAL_ERROR
}
if (!arguments.disableDefaultScriptingPlugin) {
pluginOptions.addPlatformOptions(arguments)
val explicitOrLoadedScriptingPlugin =
@@ -192,7 +212,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
pluginClasspaths.addAll(0, jars.map { it.canonicalPath })
} else {
messageCollector.report(
CompilerMessageSeverity.LOGGING,
LOGGING,
"Scripting plugin will not be loaded: not all required jars are present in the classpath (missing files: $missingJars)"
)
}
@@ -200,7 +220,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
} else {
pluginOptions.add("plugin:kotlin.scripting:disable=true")
}
return PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, configuration)
return PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, pluginConfigurations, configuration)
}
private fun tryLoadScriptingPluginFromCurrentClassLoader(configuration: CompilerConfiguration, pluginOptions: List<String>): Boolean =
@@ -18,8 +18,11 @@ package org.jetbrains.kotlin.cli.jvm.plugins
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
import org.jetbrains.kotlin.cli.plugins.extractPluginClasspathAndOptions
import org.jetbrains.kotlin.cli.plugins.processCompilerPluginOptions
import org.jetbrains.kotlin.cli.plugins.processCompilerPluginsOptions
import org.jetbrains.kotlin.compiler.plugin.*
import org.jetbrains.kotlin.config.CompilerConfiguration
@@ -29,44 +32,113 @@ import java.net.URLClassLoader
object PluginCliParser {
@JvmStatic
fun loadPluginsSafe(pluginClasspaths: Array<String>?, pluginOptions: Array<String>?, configuration: CompilerConfiguration): ExitCode =
loadPluginsSafe(pluginClasspaths?.asIterable(), pluginOptions?.asIterable(), configuration)
fun loadPluginsSafe(
pluginClasspaths: Array<String>?,
pluginOptions: Array<String>?,
pluginConfigurations: Array<String>?,
configuration: CompilerConfiguration
): ExitCode {
return loadPluginsSafe(
pluginClasspaths?.toList() ?: emptyList(),
pluginOptions?.toList() ?: emptyList(),
pluginConfigurations?.toList() ?: emptyList(),
configuration
)
}
@JvmStatic
fun loadPluginsSafe(
pluginClasspaths: Iterable<String>?,
pluginOptions: Iterable<String>?,
pluginClasspaths: Collection<String>,
pluginOptions: Collection<String>,
pluginConfigurations: Collection<String>,
configuration: CompilerConfiguration
): ExitCode {
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
try {
PluginCliParser.loadPlugins(pluginClasspaths, pluginOptions, configuration)
loadPluginsLegacyStyle(pluginClasspaths, pluginOptions, configuration)
loadPluginsModernStyle(pluginConfigurations, configuration)
return ExitCode.OK
} catch (e: PluginProcessingException) {
messageCollector.report(CompilerMessageSeverity.ERROR, e.message!!)
} catch (e: PluginCliOptionProcessingException) {
val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options)
messageCollector.report(CompilerMessageSeverity.ERROR, message)
return ExitCode.INTERNAL_ERROR
} catch (e: CliOptionProcessingException) {
messageCollector.report(CompilerMessageSeverity.ERROR, e.message!!)
return ExitCode.INTERNAL_ERROR
} catch (t: Throwable) {
MessageCollectorUtil.reportException(messageCollector, t)
return ExitCode.INTERNAL_ERROR
}
return ExitCode.OK
return ExitCode.INTERNAL_ERROR
}
class RegisteredPluginInfo(
@Suppress("DEPRECATION") val componentRegistrar: ComponentRegistrar?,
val compilerPluginRegistrar: CompilerPluginRegistrar?,
val commandLineProcessor: CommandLineProcessor?,
val pluginOptions: List<CliOptionValue>
)
@Suppress("DEPRECATION")
private fun loadRegisteredPluginsInfo(rawPluginConfigurations: Iterable<String>): List<RegisteredPluginInfo> {
val pluginConfigurations = extractPluginClasspathAndOptions(rawPluginConfigurations)
val pluginInfos = pluginConfigurations.map { pluginConfiguration ->
val classLoader = createClassLoader(pluginConfiguration.classpath)
val componentRegistrars = ServiceLoaderLite.loadImplementations(ComponentRegistrar::class.java, classLoader)
val compilerPluginRegistrars = ServiceLoaderLite.loadImplementations(CompilerPluginRegistrar::class.java, classLoader)
fun multiplePluginsErrorMessage(pluginObjects: List<Any>): String {
return buildString {
append("Multiple plugins found in given classpath: ")
val extensionNames = pluginObjects.mapNotNull { it::class.qualifiedName }
appendLine(extensionNames.joinToString(", "))
append(" Plugin configuration is: ${pluginConfiguration.rawArgument}")
}
}
when (componentRegistrars.size + compilerPluginRegistrars.size) {
0 -> throw PluginProcessingException("No plugins found in given classpath: ${pluginConfiguration.classpath.joinToString(",")}")
1 -> {}
else -> throw PluginProcessingException(multiplePluginsErrorMessage(componentRegistrars + compilerPluginRegistrars))
}
val commandLineProcessor = ServiceLoaderLite.loadImplementations(CommandLineProcessor::class.java, classLoader)
if (commandLineProcessor.size > 1) {
throw PluginProcessingException(multiplePluginsErrorMessage(commandLineProcessor))
}
RegisteredPluginInfo(
componentRegistrars.firstOrNull(),
compilerPluginRegistrars.firstOrNull(),
commandLineProcessor.firstOrNull(),
pluginConfiguration.options
)
}
return pluginInfos
}
private fun loadPluginsModernStyle(rawPluginConfigurations: Iterable<String>?, configuration: CompilerConfiguration) {
if (rawPluginConfigurations == null) return
val pluginInfos = loadRegisteredPluginsInfo(rawPluginConfigurations)
for (pluginInfo in pluginInfos) {
pluginInfo.componentRegistrar?.let {
@Suppress("DEPRECATION")
configuration.add(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, it)
}
pluginInfo.compilerPluginRegistrar?.let { configuration.add(CompilerPluginRegistrar.COMPILER_PLUGIN_REGISTRARS, it) }
if (pluginInfo.pluginOptions.isEmpty()) continue
val commandLineProcessor = pluginInfo.commandLineProcessor ?: throw RuntimeException() // TODO: proper exception
processCompilerPluginOptions(commandLineProcessor, pluginInfo.pluginOptions, configuration)
}
}
@JvmStatic
@Suppress("DEPRECATION")
fun loadPlugins(pluginClasspaths: Iterable<String>?, pluginOptions: Iterable<String>?, configuration: CompilerConfiguration) {
val classLoader = URLClassLoader(
pluginClasspaths
?.map { File(it).toURI().toURL() }
?.toTypedArray()
?: emptyArray(),
this::class.java.classLoader
)
private fun loadPluginsLegacyStyle(
pluginClasspaths: Iterable<String>?,
pluginOptions: Iterable<String>?,
configuration: CompilerConfiguration
) {
val classLoader = createClassLoader(pluginClasspaths ?: emptyList())
val componentRegistrars = ServiceLoaderLite.loadImplementations(ComponentRegistrar::class.java, classLoader)
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
@@ -86,4 +158,8 @@ object PluginCliParser {
processCompilerPluginsOptions(configuration, pluginOptions, commandLineProcessors)
}
private fun createClassLoader(classpath: Iterable<String>): URLClassLoader {
return URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray(), this::class.java.classLoader)
}
}