[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:
committed by
teamcity
parent
8919703448
commit
928416c9c5
+7
-3
@@ -20,6 +20,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
|||||||
private val serialVersionUID = 0L
|
private val serialVersionUID = 0L
|
||||||
|
|
||||||
const val PLUGIN_OPTION_FORMAT = "plugin:<pluginId>:<optionName>=<value>"
|
const val PLUGIN_OPTION_FORMAT = "plugin:<pluginId>:<optionName>=<value>"
|
||||||
|
const val PLUGIN_DECLARATION_FORMAT = "<path>[=<optionName>=<value>]"
|
||||||
|
|
||||||
const val WARN = "warn"
|
const val WARN = "warn"
|
||||||
const val ERROR = "error"
|
const val ERROR = "error"
|
||||||
@@ -70,9 +71,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
|||||||
@Argument(value = "-script", description = "Evaluate the given Kotlin script (*.kts) file")
|
@Argument(value = "-script", description = "Evaluate the given Kotlin script (*.kts) file")
|
||||||
var script: Boolean by FreezableVar(false)
|
var script: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
@Argument(value = "-P", valueDescription = PLUGIN_OPTION_FORMAT, description = "Pass an option to a plugin")
|
|
||||||
var pluginOptions: Array<String>? by FreezableVar(null)
|
|
||||||
|
|
||||||
@Argument(
|
@Argument(
|
||||||
value = "-opt-in",
|
value = "-opt-in",
|
||||||
deprecatedName = "-Xopt-in",
|
deprecatedName = "-Xopt-in",
|
||||||
@@ -107,6 +105,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
|||||||
@Argument(value = "-Xplugin", valueDescription = "<path>", description = "Load plugins from the given classpath")
|
@Argument(value = "-Xplugin", valueDescription = "<path>", description = "Load plugins from the given classpath")
|
||||||
var pluginClasspaths: Array<String>? by FreezableVar(null)
|
var pluginClasspaths: Array<String>? by FreezableVar(null)
|
||||||
|
|
||||||
|
@Argument(value = "-P", valueDescription = PLUGIN_OPTION_FORMAT, description = "Pass an option to a plugin")
|
||||||
|
var pluginOptions: Array<String>? by FreezableVar(null)
|
||||||
|
|
||||||
|
@Argument(value = "-Xcompiler-plugin", valueDescription = "<path1>,<path2>:<optionName>=<value>,<optionName>=<value>", description = "Register compiler plugin", delimiter = "")
|
||||||
|
var pluginConfigurations: Array<String>? by FreezableVar(null)
|
||||||
|
|
||||||
@Argument(value = "-Xmulti-platform", description = "Enable experimental language support for multi-platform projects")
|
@Argument(value = "-Xmulti-platform", description = "Enable experimental language support for multi-platform projects")
|
||||||
var multiPlatform: Boolean by FreezableVar(false)
|
var multiPlatform: Boolean by FreezableVar(false)
|
||||||
|
|
||||||
|
|||||||
+60
-27
@@ -10,46 +10,79 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|||||||
import org.jetbrains.kotlin.compiler.plugin.*
|
import org.jetbrains.kotlin.compiler.plugin.*
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
|
||||||
|
data class PluginClasspathAndOptions(
|
||||||
|
val rawArgument: String,
|
||||||
|
val classpath: List<String>,
|
||||||
|
val options: List<CliOptionValue>
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val regularDelimiter = ","
|
||||||
|
private const val classpathOptionsDelimiter = "="
|
||||||
|
|
||||||
|
fun extractPluginClasspathAndOptions(pluginConfigurations: Iterable<String>): List<PluginClasspathAndOptions> {
|
||||||
|
return pluginConfigurations.map { extractPluginClasspathAndOptions(it)}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extractPluginClasspathAndOptions(pluginConfiguration: String): PluginClasspathAndOptions {
|
||||||
|
val rawClasspath = pluginConfiguration.substringBefore(classpathOptionsDelimiter)
|
||||||
|
val rawOptions = pluginConfiguration.substringAfter(classpathOptionsDelimiter, missingDelimiterValue = "")
|
||||||
|
val classPath = rawClasspath.split(regularDelimiter)
|
||||||
|
val options = rawOptions.takeIf { it.isNotBlank() }
|
||||||
|
?.split(regularDelimiter)
|
||||||
|
?.mapNotNull { parseModernPluginOption(it) }
|
||||||
|
?: emptyList()
|
||||||
|
return PluginClasspathAndOptions(pluginConfiguration, classPath, options)
|
||||||
|
}
|
||||||
|
|
||||||
fun processCompilerPluginsOptions(
|
fun processCompilerPluginsOptions(
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
pluginOptions: Iterable<String>?,
|
pluginOptions: Iterable<String>?,
|
||||||
commandLineProcessors: List<CommandLineProcessor>
|
commandLineProcessors: List<CommandLineProcessor>
|
||||||
) {
|
) {
|
||||||
val optionValuesByPlugin = pluginOptions?.map(::parsePluginOption)?.groupBy {
|
val optionValuesByPlugin = pluginOptions?.map(::parseLegacyPluginOption)?.groupBy {
|
||||||
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
||||||
it.pluginId
|
it.pluginId
|
||||||
} ?: mapOf()
|
} ?: mapOf()
|
||||||
|
|
||||||
for (processor in commandLineProcessors) {
|
for (processor in commandLineProcessors) {
|
||||||
val declaredOptions = processor.pluginOptions.associateBy { it.optionName }
|
@Suppress("UNCHECKED_CAST")
|
||||||
val optionsToValues = MultiMap<AbstractCliOption, CliOptionValue>()
|
processCompilerPluginOptions(processor, optionValuesByPlugin[processor.pluginId].orEmpty() as List<CliOptionValue>, configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (optionValue in optionValuesByPlugin[processor.pluginId].orEmpty()) {
|
fun processCompilerPluginOptions(
|
||||||
val option = declaredOptions[optionValue!!.optionName]
|
processor: CommandLineProcessor,
|
||||||
?: throw CliOptionProcessingException("Unsupported plugin option: $optionValue")
|
pluginOptions: List<CliOptionValue>,
|
||||||
optionsToValues.putValue(option, optionValue)
|
configuration: CompilerConfiguration
|
||||||
|
) {
|
||||||
|
val declaredOptions = processor.pluginOptions.associateBy { it.optionName }
|
||||||
|
val optionsToValues = MultiMap<AbstractCliOption, CliOptionValue>()
|
||||||
|
|
||||||
|
for (optionValue in pluginOptions) {
|
||||||
|
val option = declaredOptions[optionValue.optionName]
|
||||||
|
?: throw CliOptionProcessingException("Unsupported plugin option: $optionValue")
|
||||||
|
optionsToValues.putValue(option, optionValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (option in processor.pluginOptions) {
|
||||||
|
val values = optionsToValues[option]
|
||||||
|
if (option.required && values.isEmpty()) {
|
||||||
|
throw PluginCliOptionProcessingException(
|
||||||
|
processor.pluginId,
|
||||||
|
processor.pluginOptions,
|
||||||
|
"Required plugin option not present: ${processor.pluginId}:${option.optionName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!option.allowMultipleOccurrences && values.size > 1) {
|
||||||
|
throw PluginCliOptionProcessingException(
|
||||||
|
processor.pluginId,
|
||||||
|
processor.pluginOptions,
|
||||||
|
"Multiple values are not allowed for plugin option ${processor.pluginId}:${option.optionName}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (option in processor.pluginOptions) {
|
for (value in values) {
|
||||||
val values = optionsToValues[option]
|
processor.processOption(option, value.value, configuration)
|
||||||
if (option.required && values.isEmpty()) {
|
|
||||||
throw PluginCliOptionProcessingException(
|
|
||||||
processor.pluginId,
|
|
||||||
processor.pluginOptions,
|
|
||||||
"Required plugin option not present: ${processor.pluginId}:${option.optionName}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!option.allowMultipleOccurrences && values.size > 1) {
|
|
||||||
throw PluginCliOptionProcessingException(
|
|
||||||
processor.pluginId,
|
|
||||||
processor.pluginOptions,
|
|
||||||
"Multiple values are not allowed for plugin option ${processor.pluginId}:${option.optionName}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (value in values) {
|
|
||||||
processor.processOption(option, value.value, configuration)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -634,5 +634,5 @@ fun loadPluginsForTests(configuration: CompilerConfiguration): ExitCode {
|
|||||||
PathUtil.KOTLIN_SCRIPTING_PLUGIN_CLASSPATH_JARS.mapNotNull { File(libPath, it) }.partition { it.exists() }
|
PathUtil.KOTLIN_SCRIPTING_PLUGIN_CLASSPATH_JARS.mapNotNull { File(libPath, it) }.partition { it.exists() }
|
||||||
pluginClasspaths = jars.map { it.canonicalPath } + pluginClasspaths
|
pluginClasspaths = jars.map { it.canonicalPath } + pluginClasspaths
|
||||||
|
|
||||||
return PluginCliParser.loadPluginsSafe(pluginClasspaths, mutableListOf(), configuration)
|
return PluginCliParser.loadPluginsSafe(pluginClasspaths, listOf(), listOf(), configuration)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
|
|||||||
protected fun loadPlugins(paths: KotlinPaths?, arguments: A, configuration: CompilerConfiguration): ExitCode {
|
protected fun loadPlugins(paths: KotlinPaths?, arguments: A, configuration: CompilerConfiguration): ExitCode {
|
||||||
val pluginClasspaths = arguments.pluginClasspaths.orEmpty().toMutableList()
|
val pluginClasspaths = arguments.pluginClasspaths.orEmpty().toMutableList()
|
||||||
val pluginOptions = arguments.pluginOptions.orEmpty().toMutableList()
|
val pluginOptions = arguments.pluginOptions.orEmpty().toMutableList()
|
||||||
|
val pluginConfigurations = arguments.pluginConfigurations.orEmpty().toMutableList()
|
||||||
val messageCollector = configuration.getNotNull(MESSAGE_COLLECTOR_KEY)
|
val messageCollector = configuration.getNotNull(MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
for (classpath in pluginClasspaths) {
|
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) {
|
if (!arguments.disableDefaultScriptingPlugin) {
|
||||||
pluginOptions.addPlatformOptions(arguments)
|
pluginOptions.addPlatformOptions(arguments)
|
||||||
val explicitOrLoadedScriptingPlugin =
|
val explicitOrLoadedScriptingPlugin =
|
||||||
@@ -192,7 +212,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
|
|||||||
pluginClasspaths.addAll(0, jars.map { it.canonicalPath })
|
pluginClasspaths.addAll(0, jars.map { it.canonicalPath })
|
||||||
} else {
|
} else {
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
CompilerMessageSeverity.LOGGING,
|
LOGGING,
|
||||||
"Scripting plugin will not be loaded: not all required jars are present in the classpath (missing files: $missingJars)"
|
"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 {
|
} else {
|
||||||
pluginOptions.add("plugin:kotlin.scripting:disable=true")
|
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 =
|
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.CLIConfigurationKeys
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
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.CompilerMessageSeverity
|
||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
|
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.cli.plugins.processCompilerPluginsOptions
|
||||||
import org.jetbrains.kotlin.compiler.plugin.*
|
import org.jetbrains.kotlin.compiler.plugin.*
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
@@ -29,44 +32,113 @@ import java.net.URLClassLoader
|
|||||||
|
|
||||||
object PluginCliParser {
|
object PluginCliParser {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadPluginsSafe(pluginClasspaths: Array<String>?, pluginOptions: Array<String>?, configuration: CompilerConfiguration): ExitCode =
|
fun loadPluginsSafe(
|
||||||
loadPluginsSafe(pluginClasspaths?.asIterable(), pluginOptions?.asIterable(), configuration)
|
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
|
@JvmStatic
|
||||||
fun loadPluginsSafe(
|
fun loadPluginsSafe(
|
||||||
pluginClasspaths: Iterable<String>?,
|
pluginClasspaths: Collection<String>,
|
||||||
pluginOptions: Iterable<String>?,
|
pluginOptions: Collection<String>,
|
||||||
|
pluginConfigurations: Collection<String>,
|
||||||
configuration: CompilerConfiguration
|
configuration: CompilerConfiguration
|
||||||
): ExitCode {
|
): ExitCode {
|
||||||
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (e: PluginCliOptionProcessingException) {
|
||||||
val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options)
|
val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options)
|
||||||
messageCollector.report(CompilerMessageSeverity.ERROR, message)
|
messageCollector.report(CompilerMessageSeverity.ERROR, message)
|
||||||
return ExitCode.INTERNAL_ERROR
|
|
||||||
} catch (e: CliOptionProcessingException) {
|
} catch (e: CliOptionProcessingException) {
|
||||||
messageCollector.report(CompilerMessageSeverity.ERROR, e.message!!)
|
messageCollector.report(CompilerMessageSeverity.ERROR, e.message!!)
|
||||||
return ExitCode.INTERNAL_ERROR
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
MessageCollectorUtil.reportException(messageCollector, t)
|
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
|
@JvmStatic
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun loadPlugins(pluginClasspaths: Iterable<String>?, pluginOptions: Iterable<String>?, configuration: CompilerConfiguration) {
|
private fun loadPluginsLegacyStyle(
|
||||||
val classLoader = URLClassLoader(
|
pluginClasspaths: Iterable<String>?,
|
||||||
pluginClasspaths
|
pluginOptions: Iterable<String>?,
|
||||||
?.map { File(it).toURI().toURL() }
|
configuration: CompilerConfiguration
|
||||||
?.toTypedArray()
|
) {
|
||||||
?: emptyArray(),
|
val classLoader = createClassLoader(pluginClasspaths ?: emptyList())
|
||||||
this::class.java.classLoader
|
|
||||||
)
|
|
||||||
|
|
||||||
val componentRegistrars = ServiceLoaderLite.loadImplementations(ComponentRegistrar::class.java, classLoader)
|
val componentRegistrars = ServiceLoaderLite.loadImplementations(ComponentRegistrar::class.java, classLoader)
|
||||||
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
||||||
|
|
||||||
@@ -86,4 +158,8 @@ object PluginCliParser {
|
|||||||
|
|
||||||
processCompilerPluginsOptions(configuration, pluginOptions, commandLineProcessors)
|
processCompilerPluginsOptions(configuration, pluginOptions, commandLineProcessors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createClassLoader(classpath: Iterable<String>): URLClassLoader {
|
||||||
|
return URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray(), this::class.java.classLoader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -144,10 +144,11 @@ class IncrementalFirJvmCompilerRunner(
|
|||||||
if (collector.hasErrors()) return ExitCode.COMPILATION_ERROR to emptyList()
|
if (collector.hasErrors()) return ExitCode.COMPILATION_ERROR to emptyList()
|
||||||
|
|
||||||
// -- plugins
|
// -- plugins
|
||||||
val pluginClasspaths: Iterable<String> = args.pluginClasspaths?.asIterable() ?: emptyList()
|
val pluginClasspaths = args.pluginClasspaths?.toList() ?: emptyList()
|
||||||
val pluginOptions = args.pluginOptions?.toMutableList() ?: ArrayList()
|
val pluginOptions = args.pluginOptions?.toMutableList() ?: ArrayList()
|
||||||
|
val pluginConfigurations = args.pluginConfigurations?.toList() ?: emptyList()
|
||||||
// TODO: add scripting support when ready in FIR
|
// TODO: add scripting support when ready in FIR
|
||||||
val pluginLoadResult = PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, configuration)
|
val pluginLoadResult = PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, pluginConfigurations, configuration)
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult to emptyList()
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult to emptyList()
|
||||||
// -- /plugins
|
// -- /plugins
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ class PluginCliOptionProcessingException(
|
|||||||
cause: Throwable? = null
|
cause: Throwable? = null
|
||||||
) : CliOptionProcessingException(message, cause)
|
) : CliOptionProcessingException(message, cause)
|
||||||
|
|
||||||
|
class PluginProcessingException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
|
||||||
|
|
||||||
fun cliPluginUsageString(pluginId: String, options: Collection<AbstractCliOption>): String {
|
fun cliPluginUsageString(pluginId: String, options: Collection<AbstractCliOption>): String {
|
||||||
val LEFT_INDENT = 2
|
val LEFT_INDENT = 2
|
||||||
val MAX_OPTION_WIDTH = 26
|
val MAX_OPTION_WIDTH = 26
|
||||||
@@ -73,7 +75,7 @@ data class CliOptionValue(
|
|||||||
override fun toString() = "$pluginId:$optionName=$value"
|
override fun toString() = "$pluginId:$optionName=$value"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun parsePluginOption(argumentValue: String): CliOptionValue? {
|
fun parseLegacyPluginOption(argumentValue: String): CliOptionValue? {
|
||||||
val pattern = Pattern.compile("""^plugin:([^:]*):([^=]*)=(.*)$""")
|
val pattern = Pattern.compile("""^plugin:([^:]*):([^=]*)=(.*)$""")
|
||||||
val matcher = pattern.matcher(argumentValue)
|
val matcher = pattern.matcher(argumentValue)
|
||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
@@ -83,6 +85,16 @@ fun parsePluginOption(argumentValue: String): CliOptionValue? {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun parseModernPluginOption(argumentValue: String): CliOptionValue? {
|
||||||
|
val pattern = Pattern.compile("""^([^=]*)=(.*)$""")
|
||||||
|
val matcher = pattern.matcher(argumentValue)
|
||||||
|
if (matcher.matches()) {
|
||||||
|
return CliOptionValue("<NO_ID>", matcher.group(1), matcher.group(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
fun getPluginOptionString(pluginId: String, key: String, value: String): String {
|
fun getPluginOptionString(pluginId: String, key: String, value: String): String {
|
||||||
return "plugin:$pluginId:$key=$value"
|
return "plugin:$pluginId:$key=$value"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -20,5 +20,5 @@ fun loadScriptingPlugin(configuration: CompilerConfiguration) {
|
|||||||
KOTLIN_SCRIPTING_JVM_JAR
|
KOTLIN_SCRIPTING_JVM_JAR
|
||||||
).map { File(libPath, it).path }
|
).map { File(libPath, it).path }
|
||||||
}
|
}
|
||||||
PluginCliParser.loadPluginsSafe(pluginClasspath, null, configuration)
|
PluginCliParser.loadPluginsSafe(pluginClasspath, emptyList(), emptyList(), configuration)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -93,6 +93,8 @@ where advanced options include:
|
|||||||
-Xphases-to-validate-after Validate backend state after these phases
|
-Xphases-to-validate-after Validate backend state after these phases
|
||||||
-Xphases-to-validate-before Validate backend state before these phases
|
-Xphases-to-validate-before Validate backend state before these phases
|
||||||
-Xplugin=<path> Load plugins from the given classpath
|
-Xplugin=<path> Load plugins from the given classpath
|
||||||
|
-Xcompiler-plugin=<path1>,<path2>:<optionName>=<value>,<optionName>=<value>
|
||||||
|
Register compiler plugin
|
||||||
-Xprofile-phases Profile backend phases
|
-Xprofile-phases Profile backend phases
|
||||||
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
||||||
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
||||||
|
|||||||
+2
@@ -200,6 +200,8 @@ where advanced options include:
|
|||||||
-Xphases-to-validate-after Validate backend state after these phases
|
-Xphases-to-validate-after Validate backend state after these phases
|
||||||
-Xphases-to-validate-before Validate backend state before these phases
|
-Xphases-to-validate-before Validate backend state before these phases
|
||||||
-Xplugin=<path> Load plugins from the given classpath
|
-Xplugin=<path> Load plugins from the given classpath
|
||||||
|
-Xcompiler-plugin=<path1>,<path2>:<optionName>=<value>,<optionName>=<value>
|
||||||
|
Register compiler plugin
|
||||||
-Xprofile-phases Profile backend phases
|
-Xprofile-phases Profile backend phases
|
||||||
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
||||||
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/allopen-compiler-plugin.jar=annotation=foo.AllOpen
|
||||||
|
$TESTDATA_DIR$/firAllOpenPlugin.kt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/noarg-compiler-plugin.jar=annotation=foo.NoArg
|
||||||
|
|
||||||
|
-Xplugin=dist/kotlinc/lib/allopen-compiler-plugin.jar
|
||||||
|
-P
|
||||||
|
plugin\:org.jetbrains.kotlin.allopen\:annotation=foo.AllOpen
|
||||||
|
|
||||||
|
$TESTDATA_DIR$/mixingModernAndLegacyArgs.kt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun main() {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
error: mixing legacy and modern plugin arguments is prohibited. Please use only one syntax
|
||||||
|
Legacy arguments:
|
||||||
|
-Xplugin=dist/kotlinc/lib/allopen-compiler-plugin.jar
|
||||||
|
-P plugin:org.jetbrains.kotlin.allopen:annotation=foo.AllOpen
|
||||||
|
Modern arguments:
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/noarg-compiler-plugin.jar=annotation=foo.NoArg
|
||||||
|
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/allopen-compiler-plugin.jar=annotation=foo.AllOpen1,annotation=foo.AllOpen2
|
||||||
|
$TESTDATA_DIR$/multipleOptionsForOnePlugin.kt
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
annotation class AllOpen1
|
||||||
|
annotation class AllOpen2
|
||||||
|
|
||||||
|
@AllOpen1
|
||||||
|
class Base1
|
||||||
|
class Derived1 : Base1()
|
||||||
|
|
||||||
|
@AllOpen2
|
||||||
|
class Base2
|
||||||
|
class Derived2 : Base2()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/allopen-compiler-plugin.jar=annotation=foo.AllOpen
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/noarg-compiler-plugin.jar=annotation=foo.NoArg
|
||||||
|
$TESTDATA_DIR$/multiplePlugins.kt
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
annotation class NoArg
|
||||||
|
annotation class AllOpen
|
||||||
|
|
||||||
|
@AllOpen
|
||||||
|
class Base(val s: String)
|
||||||
|
|
||||||
|
class Derived(s: String) : Base(s) {
|
||||||
|
@NoArg
|
||||||
|
inner class Inner(val s: String)
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
compiler/testData/cli/jvm/plugins/multiplePlugins.kt:11:17: error: noarg constructor generation is not possible for inner classes
|
||||||
|
inner class Inner(val s: String)
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/allopen-compiler-plugin.jar,dist/kotlinc/lib/noarg-compiler-plugin.jar=annotation=foo.AllOpen
|
||||||
|
$TESTDATA_DIR$/multiplePluginsInSameArg.kt
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
annotation class NoArg
|
||||||
|
annotation class AllOpen
|
||||||
|
|
||||||
|
@AllOpen
|
||||||
|
class Base(val s: String)
|
||||||
|
|
||||||
|
@NoArg
|
||||||
|
class Derived(s: String) : Base(s)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
error: multiple plugins found in given classpath: org.jetbrains.kotlin.allopen.AllOpenComponentRegistrar, org.jetbrains.kotlin.noarg.NoArgComponentRegistrar
|
||||||
|
Plugin configuration is: dist/kotlinc/lib/allopen-compiler-plugin.jar,dist/kotlinc/lib/noarg-compiler-plugin.jar=annotation=foo.AllOpen
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/kotlin-reflect.jar
|
||||||
|
$TESTDATA_DIR$/noPluginInClasspath.kt
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun main() {}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
error: no plugins found in given classpath: dist/kotlinc/lib/kotlin-reflect.jar
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/android-extensions-compiler.jar=package=com.myapp,variant=main;$TESTDATA_DIR$/../androidPlugin/res
|
||||||
|
$TESTDATA_DIR$/pluginSimple.kt
|
||||||
|
$TESTDATA_DIR$/../androidPlugin
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xcompiler-plugin=dist/kotlinc/lib/android-extensions-compiler.jar=package=com.myapp,variant=main;$TESTDATA_DIR$/../androidPlugin/res
|
||||||
|
$TESTDATA_DIR$/pluginSimple.kt
|
||||||
|
$TESTDATA_DIR$/../androidPlugin
|
||||||
|
-Xuse-k2
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
warning: ATTENTION!
|
||||||
|
This build uses experimental K2 compiler:
|
||||||
|
-Xuse-k2
|
||||||
|
error: there are some plugins incompatible with K2 compiler:
|
||||||
|
org.jetbrains.kotlin.android.synthetic.AndroidComponentRegistrar
|
||||||
|
Please remove -Xuse-k2
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -69,19 +69,59 @@ public class CliTestGenerated extends AbstractCliTest {
|
|||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/cli/jvm/plugins"), Pattern.compile("^(.+)\\.args$"), null, false);
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/cli/jvm/plugins"), Pattern.compile("^(.+)\\.args$"), null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("firAllOpenPlugin.args")
|
@TestMetadata("firAllOpenPlugin_legacy.args")
|
||||||
public void testFirAllOpenPlugin() throws Exception {
|
public void testFirAllOpenPlugin_legacy() throws Exception {
|
||||||
runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin.args");
|
runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("pluginSimple.args")
|
@TestMetadata("firAllOpenPlugin_modern.args")
|
||||||
public void testPluginSimple() throws Exception {
|
public void testFirAllOpenPlugin_modern() throws Exception {
|
||||||
runTest("compiler/testData/cli/jvm/plugins/pluginSimple.args");
|
runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args");
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("pluginWithK2Error.args")
|
@TestMetadata("mixingModernAndLegacyArgs.args")
|
||||||
public void testPluginWithK2Error() throws Exception {
|
public void testMixingModernAndLegacyArgs() throws Exception {
|
||||||
runTest("compiler/testData/cli/jvm/plugins/pluginWithK2Error.args");
|
runTest("compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleOptionsForOnePlugin.args")
|
||||||
|
public void testMultipleOptionsForOnePlugin() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multiplePlugins.args")
|
||||||
|
public void testMultiplePlugins() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/multiplePlugins.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multiplePluginsInSameArg.args")
|
||||||
|
public void testMultiplePluginsInSameArg() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noPluginInClasspath.args")
|
||||||
|
public void testNoPluginInClasspath() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/noPluginInClasspath.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("pluginSimple_legacy.args")
|
||||||
|
public void testPluginSimple_legacy() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/pluginSimple_legacy.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("pluginSimple_modern.args")
|
||||||
|
public void testPluginSimple_modern() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/pluginSimple_modern.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("pluginWithK2Error_legacy.args")
|
||||||
|
public void testPluginWithK2Error_legacy() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/pluginWithK2Error_legacy.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("pluginWithK2Error_modern.args")
|
||||||
|
public void testPluginWithK2Error_modern() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.args");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val pluginLoadResult =
|
val pluginLoadResult =
|
||||||
PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, configuration)
|
PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, arguments.pluginConfigurations, configuration)
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
||||||
|
|
||||||
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable,
|
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable,
|
||||||
|
|||||||
Reference in New Issue
Block a user