From 928416c9c5fe0287ed38c11ce3e93051c76eb598 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Wed, 29 Jun 2022 15:02:58 +0300 Subject: [PATCH] [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]` --- .../arguments/CommonCompilerArguments.kt | 10 +- .../cli/plugins/PluginsOptionsParser.kt | 87 ++++++++----- .../jetbrains/kotlin/cli/js/K2JsIrCompiler.kt | 2 +- .../kotlin/cli/common/CLICompiler.kt | 24 +++- .../kotlin/cli/jvm/plugins/PluginCliParser.kt | 114 +++++++++++++++--- .../IncrementalFirJvmCompilerRunner.kt | 5 +- .../kotlin/compiler/plugin/CliOptions.kt | 14 ++- .../jetbrains/kotlin/script/scriptTestUtil.kt | 4 +- compiler/testData/cli/js/jsExtraHelp.out | 2 + compiler/testData/cli/jvm/extraHelp.out | 2 + ...ugin.args => firAllOpenPlugin_legacy.args} | 0 ...Plugin.out => firAllOpenPlugin_legacy.out} | 0 .../jvm/plugins/firAllOpenPlugin_modern.args | 4 + .../jvm/plugins/firAllOpenPlugin_modern.out | 1 + .../plugins/mixingModernAndLegacyArgs.args | 9 ++ .../jvm/plugins/mixingModernAndLegacyArgs.kt | 1 + .../jvm/plugins/mixingModernAndLegacyArgs.out | 8 ++ .../plugins/multipleOptionsForOnePlugin.args | 4 + .../plugins/multipleOptionsForOnePlugin.kt | 12 ++ .../plugins/multipleOptionsForOnePlugin.out | 1 + .../cli/jvm/plugins/multiplePlugins.args | 5 + .../cli/jvm/plugins/multiplePlugins.kt | 12 ++ .../cli/jvm/plugins/multiplePlugins.out | 4 + .../jvm/plugins/multiplePluginsInSameArg.args | 4 + .../jvm/plugins/multiplePluginsInSameArg.kt | 10 ++ .../jvm/plugins/multiplePluginsInSameArg.out | 3 + .../cli/jvm/plugins/noPluginInClasspath.args | 4 + .../cli/jvm/plugins/noPluginInClasspath.kt | 1 + .../cli/jvm/plugins/noPluginInClasspath.out | 2 + ...inSimple.args => pluginSimple_legacy.args} | 0 ...uginSimple.out => pluginSimple_legacy.out} | 0 .../cli/jvm/plugins/pluginSimple_modern.args | 5 + .../cli/jvm/plugins/pluginSimple_modern.out | 1 + ...ror.args => pluginWithK2Error_legacy.args} | 0 ...Error.out => pluginWithK2Error_legacy.out} | 0 .../jvm/plugins/pluginWithK2Error_modern.args | 6 + .../jvm/plugins/pluginWithK2Error_modern.out | 7 ++ .../kotlin/cli/CliTestGenerated.java | 58 +++++++-- .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 2 +- 39 files changed, 361 insertions(+), 67 deletions(-) rename compiler/testData/cli/jvm/plugins/{firAllOpenPlugin.args => firAllOpenPlugin_legacy.args} (100%) rename compiler/testData/cli/jvm/plugins/{firAllOpenPlugin.out => firAllOpenPlugin_legacy.out} (100%) create mode 100644 compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args create mode 100644 compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.out create mode 100644 compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.args create mode 100644 compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.kt create mode 100644 compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.out create mode 100644 compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.args create mode 100644 compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.kt create mode 100644 compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.out create mode 100644 compiler/testData/cli/jvm/plugins/multiplePlugins.args create mode 100644 compiler/testData/cli/jvm/plugins/multiplePlugins.kt create mode 100644 compiler/testData/cli/jvm/plugins/multiplePlugins.out create mode 100644 compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.args create mode 100644 compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.kt create mode 100644 compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.out create mode 100644 compiler/testData/cli/jvm/plugins/noPluginInClasspath.args create mode 100644 compiler/testData/cli/jvm/plugins/noPluginInClasspath.kt create mode 100644 compiler/testData/cli/jvm/plugins/noPluginInClasspath.out rename compiler/testData/cli/jvm/plugins/{pluginSimple.args => pluginSimple_legacy.args} (100%) rename compiler/testData/cli/jvm/plugins/{pluginSimple.out => pluginSimple_legacy.out} (100%) create mode 100644 compiler/testData/cli/jvm/plugins/pluginSimple_modern.args create mode 100644 compiler/testData/cli/jvm/plugins/pluginSimple_modern.out rename compiler/testData/cli/jvm/plugins/{pluginWithK2Error.args => pluginWithK2Error_legacy.args} (100%) rename compiler/testData/cli/jvm/plugins/{pluginWithK2Error.out => pluginWithK2Error_legacy.out} (100%) create mode 100644 compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.args create mode 100644 compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.out diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt index f6205c92de5..0c3c0f84e64 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/CommonCompilerArguments.kt @@ -20,6 +20,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() { private val serialVersionUID = 0L const val PLUGIN_OPTION_FORMAT = "plugin::=" + const val PLUGIN_DECLARATION_FORMAT = "[==]" const val WARN = "warn" const val ERROR = "error" @@ -70,9 +71,6 @@ abstract class CommonCompilerArguments : CommonToolArguments() { @Argument(value = "-script", description = "Evaluate the given Kotlin script (*.kts) file") var script: Boolean by FreezableVar(false) - @Argument(value = "-P", valueDescription = PLUGIN_OPTION_FORMAT, description = "Pass an option to a plugin") - var pluginOptions: Array? by FreezableVar(null) - @Argument( value = "-opt-in", deprecatedName = "-Xopt-in", @@ -107,6 +105,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() { @Argument(value = "-Xplugin", valueDescription = "", description = "Load plugins from the given classpath") var pluginClasspaths: Array? by FreezableVar(null) + @Argument(value = "-P", valueDescription = PLUGIN_OPTION_FORMAT, description = "Pass an option to a plugin") + var pluginOptions: Array? by FreezableVar(null) + + @Argument(value = "-Xcompiler-plugin", valueDescription = ",:=,=", description = "Register compiler plugin", delimiter = "") + var pluginConfigurations: Array? by FreezableVar(null) + @Argument(value = "-Xmulti-platform", description = "Enable experimental language support for multi-platform projects") var multiPlatform: Boolean by FreezableVar(false) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/plugins/PluginsOptionsParser.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/plugins/PluginsOptionsParser.kt index 3582b221723..bafbf1f77e4 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/plugins/PluginsOptionsParser.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/plugins/PluginsOptionsParser.kt @@ -10,46 +10,79 @@ import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments import org.jetbrains.kotlin.compiler.plugin.* import org.jetbrains.kotlin.config.CompilerConfiguration +data class PluginClasspathAndOptions( + val rawArgument: String, + val classpath: List, + val options: List +) + +private const val regularDelimiter = "," +private const val classpathOptionsDelimiter = "=" + +fun extractPluginClasspathAndOptions(pluginConfigurations: Iterable): List { + 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( configuration: CompilerConfiguration, pluginOptions: Iterable?, commandLineProcessors: List ) { - 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}") it.pluginId } ?: mapOf() for (processor in commandLineProcessors) { - val declaredOptions = processor.pluginOptions.associateBy { it.optionName } - val optionsToValues = MultiMap() + @Suppress("UNCHECKED_CAST") + processCompilerPluginOptions(processor, optionValuesByPlugin[processor.pluginId].orEmpty() as List, configuration) + } +} - for (optionValue in optionValuesByPlugin[processor.pluginId].orEmpty()) { - val option = declaredOptions[optionValue!!.optionName] - ?: throw CliOptionProcessingException("Unsupported plugin option: $optionValue") - optionsToValues.putValue(option, optionValue) +fun processCompilerPluginOptions( + processor: CommandLineProcessor, + pluginOptions: List, + configuration: CompilerConfiguration +) { + val declaredOptions = processor.pluginOptions.associateBy { it.optionName } + val optionsToValues = MultiMap() + + 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) { - 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 (value in values) { - processor.processOption(option, value.value, configuration) - } + for (value in values) { + processor.processOption(option, value.value, configuration) } } } diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index 3f6214553c4..07f0821d547 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -634,5 +634,5 @@ fun loadPluginsForTests(configuration: CompilerConfiguration): ExitCode { PathUtil.KOTLIN_SCRIPTING_PLUGIN_CLASSPATH_JARS.mapNotNull { File(libPath, it) }.partition { it.exists() } pluginClasspaths = jars.map { it.canonicalPath } + pluginClasspaths - return PluginCliParser.loadPluginsSafe(pluginClasspaths, mutableListOf(), configuration) + return PluginCliParser.loadPluginsSafe(pluginClasspaths, listOf(), listOf(), configuration) } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt index 3c19cb9f2c2..06ff899b34f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt @@ -170,6 +170,7 @@ abstract class CLICompiler : CLITool() { 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 : CLITool() { } } + 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 : CLITool() { 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 : CLITool() { } 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): Boolean = diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt index cc3cb65b567..337724d2afb 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt @@ -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?, pluginOptions: Array?, configuration: CompilerConfiguration): ExitCode = - loadPluginsSafe(pluginClasspaths?.asIterable(), pluginOptions?.asIterable(), configuration) + fun loadPluginsSafe( + pluginClasspaths: Array?, + pluginOptions: Array?, + pluginConfigurations: Array?, + configuration: CompilerConfiguration + ): ExitCode { + return loadPluginsSafe( + pluginClasspaths?.toList() ?: emptyList(), + pluginOptions?.toList() ?: emptyList(), + pluginConfigurations?.toList() ?: emptyList(), + configuration + ) + } @JvmStatic fun loadPluginsSafe( - pluginClasspaths: Iterable?, - pluginOptions: Iterable?, + pluginClasspaths: Collection, + pluginOptions: Collection, + pluginConfigurations: Collection, 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 + ) + + @Suppress("DEPRECATION") + private fun loadRegisteredPluginsInfo(rawPluginConfigurations: Iterable): List { + 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): 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?, 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?, pluginOptions: Iterable?, configuration: CompilerConfiguration) { - val classLoader = URLClassLoader( - pluginClasspaths - ?.map { File(it).toURI().toURL() } - ?.toTypedArray() - ?: emptyArray(), - this::class.java.classLoader - ) - + private fun loadPluginsLegacyStyle( + pluginClasspaths: Iterable?, + pluginOptions: Iterable?, + 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): URLClassLoader { + return URLClassLoader(classpath.map { File(it).toURI().toURL() }.toTypedArray(), this::class.java.classLoader) + } } diff --git a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalFirJvmCompilerRunner.kt b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalFirJvmCompilerRunner.kt index b1ad4f98596..3ba413afe6c 100644 --- a/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalFirJvmCompilerRunner.kt +++ b/compiler/incremental-compilation-impl/src/org/jetbrains/kotlin/incremental/IncrementalFirJvmCompilerRunner.kt @@ -144,10 +144,11 @@ class IncrementalFirJvmCompilerRunner( if (collector.hasErrors()) return ExitCode.COMPILATION_ERROR to emptyList() // -- plugins - val pluginClasspaths: Iterable = args.pluginClasspaths?.asIterable() ?: emptyList() + val pluginClasspaths = args.pluginClasspaths?.toList() ?: emptyList() val pluginOptions = args.pluginOptions?.toMutableList() ?: ArrayList() + val pluginConfigurations = args.pluginConfigurations?.toList() ?: emptyList() // 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() // -- /plugins diff --git a/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt b/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt index 948883d59c3..7d5e0e7c713 100644 --- a/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt +++ b/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt @@ -44,6 +44,8 @@ class PluginCliOptionProcessingException( cause: Throwable? = null ) : CliOptionProcessingException(message, cause) +class PluginProcessingException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) + fun cliPluginUsageString(pluginId: String, options: Collection): String { val LEFT_INDENT = 2 val MAX_OPTION_WIDTH = 26 @@ -73,7 +75,7 @@ data class CliOptionValue( override fun toString() = "$pluginId:$optionName=$value" } -fun parsePluginOption(argumentValue: String): CliOptionValue? { +fun parseLegacyPluginOption(argumentValue: String): CliOptionValue? { val pattern = Pattern.compile("""^plugin:([^:]*):([^=]*)=(.*)$""") val matcher = pattern.matcher(argumentValue) if (matcher.matches()) { @@ -83,6 +85,16 @@ fun parsePluginOption(argumentValue: String): CliOptionValue? { return null } +fun parseModernPluginOption(argumentValue: String): CliOptionValue? { + val pattern = Pattern.compile("""^([^=]*)=(.*)$""") + val matcher = pattern.matcher(argumentValue) + if (matcher.matches()) { + return CliOptionValue("", matcher.group(1), matcher.group(2)) + } + + return null +} + fun getPluginOptionString(pluginId: String, key: String, value: String): String { return "plugin:$pluginId:$key=$value" } diff --git a/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/script/scriptTestUtil.kt b/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/script/scriptTestUtil.kt index ff5c35463f4..2d4a7d3ac04 100644 --- a/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/script/scriptTestUtil.kt +++ b/compiler/test-infrastructure-utils/tests/org/jetbrains/kotlin/script/scriptTestUtil.kt @@ -20,5 +20,5 @@ fun loadScriptingPlugin(configuration: CompilerConfiguration) { KOTLIN_SCRIPTING_JVM_JAR ).map { File(libPath, it).path } } - PluginCliParser.loadPluginsSafe(pluginClasspath, null, configuration) -} \ No newline at end of file + PluginCliParser.loadPluginsSafe(pluginClasspath, emptyList(), emptyList(), configuration) +} diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index 7815c84b82f..2c605de58e6 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -93,6 +93,8 @@ where advanced options include: -Xphases-to-validate-after Validate backend state after these phases -Xphases-to-validate-before Validate backend state before these phases -Xplugin= Load plugins from the given classpath + -Xcompiler-plugin=,:=,= + Register compiler plugin -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 -Xread-deserialized-contracts Enable reading of contracts from metadata diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index fe4f852fb8f..0a9ecdfb355 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -200,6 +200,8 @@ where advanced options include: -Xphases-to-validate-after Validate backend state after these phases -Xphases-to-validate-before Validate backend state before these phases -Xplugin= Load plugins from the given classpath + -Xcompiler-plugin=,:=,= + Register compiler plugin -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 -Xread-deserialized-contracts Enable reading of contracts from metadata diff --git a/compiler/testData/cli/jvm/plugins/firAllOpenPlugin.args b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.args similarity index 100% rename from compiler/testData/cli/jvm/plugins/firAllOpenPlugin.args rename to compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.args diff --git a/compiler/testData/cli/jvm/plugins/firAllOpenPlugin.out b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.out similarity index 100% rename from compiler/testData/cli/jvm/plugins/firAllOpenPlugin.out rename to compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.out diff --git a/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args new file mode 100644 index 00000000000..764570e5f68 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args @@ -0,0 +1,4 @@ +-d +$TEMP_DIR$ +-Xcompiler-plugin=dist/kotlinc/lib/allopen-compiler-plugin.jar=annotation=foo.AllOpen +$TESTDATA_DIR$/firAllOpenPlugin.kt diff --git a/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.out b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.args b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.args new file mode 100644 index 00000000000..dbe83513c0c --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.kt b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.kt new file mode 100644 index 00000000000..d44a1616c01 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.kt @@ -0,0 +1 @@ +fun main() {} diff --git a/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.out b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.out new file mode 100644 index 00000000000..efbebc110cb --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/mixingModernAndLegacyArgs.out @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.args b/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.args new file mode 100644 index 00000000000..5f76fb11b84 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.kt b/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.kt new file mode 100644 index 00000000000..647e54e2483 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/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() diff --git a/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.out b/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multipleOptionsForOnePlugin.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/jvm/plugins/multiplePlugins.args b/compiler/testData/cli/jvm/plugins/multiplePlugins.args new file mode 100644 index 00000000000..032033a4983 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multiplePlugins.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/multiplePlugins.kt b/compiler/testData/cli/jvm/plugins/multiplePlugins.kt new file mode 100644 index 00000000000..75624567325 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/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) +} diff --git a/compiler/testData/cli/jvm/plugins/multiplePlugins.out b/compiler/testData/cli/jvm/plugins/multiplePlugins.out new file mode 100644 index 00000000000..294392adc37 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multiplePlugins.out @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.args b/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.args new file mode 100644 index 00000000000..3858669e48b --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.kt b/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.kt new file mode 100644 index 00000000000..d6c7475f7ae --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/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) diff --git a/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.out b/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.out new file mode 100644 index 00000000000..5bcca76f71e --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/multiplePluginsInSameArg.out @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/noPluginInClasspath.args b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.args new file mode 100644 index 00000000000..dd7a39fe7a7 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.args @@ -0,0 +1,4 @@ +-d +$TEMP_DIR$ +-Xcompiler-plugin=dist/kotlinc/lib/kotlin-reflect.jar +$TESTDATA_DIR$/noPluginInClasspath.kt diff --git a/compiler/testData/cli/jvm/plugins/noPluginInClasspath.kt b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.kt new file mode 100644 index 00000000000..d44a1616c01 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.kt @@ -0,0 +1 @@ +fun main() {} diff --git a/compiler/testData/cli/jvm/plugins/noPluginInClasspath.out b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.out new file mode 100644 index 00000000000..20625348796 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/noPluginInClasspath.out @@ -0,0 +1,2 @@ +error: no plugins found in given classpath: dist/kotlinc/lib/kotlin-reflect.jar +COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/plugins/pluginSimple.args b/compiler/testData/cli/jvm/plugins/pluginSimple_legacy.args similarity index 100% rename from compiler/testData/cli/jvm/plugins/pluginSimple.args rename to compiler/testData/cli/jvm/plugins/pluginSimple_legacy.args diff --git a/compiler/testData/cli/jvm/plugins/pluginSimple.out b/compiler/testData/cli/jvm/plugins/pluginSimple_legacy.out similarity index 100% rename from compiler/testData/cli/jvm/plugins/pluginSimple.out rename to compiler/testData/cli/jvm/plugins/pluginSimple_legacy.out diff --git a/compiler/testData/cli/jvm/plugins/pluginSimple_modern.args b/compiler/testData/cli/jvm/plugins/pluginSimple_modern.args new file mode 100644 index 00000000000..1738c20d466 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/pluginSimple_modern.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/pluginSimple_modern.out b/compiler/testData/cli/jvm/plugins/pluginSimple_modern.out new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/pluginSimple_modern.out @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/cli/jvm/plugins/pluginWithK2Error.args b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_legacy.args similarity index 100% rename from compiler/testData/cli/jvm/plugins/pluginWithK2Error.args rename to compiler/testData/cli/jvm/plugins/pluginWithK2Error_legacy.args diff --git a/compiler/testData/cli/jvm/plugins/pluginWithK2Error.out b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_legacy.out similarity index 100% rename from compiler/testData/cli/jvm/plugins/pluginWithK2Error.out rename to compiler/testData/cli/jvm/plugins/pluginWithK2Error_legacy.out diff --git a/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.args b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.args new file mode 100644 index 00000000000..403fc159fc2 --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.args @@ -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 diff --git a/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.out b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.out new file mode 100644 index 00000000000..092c02de5fd --- /dev/null +++ b/compiler/testData/cli/jvm/plugins/pluginWithK2Error_modern.out @@ -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 diff --git a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java index 1727ded96db..20a2f8c1f26 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -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); } - @TestMetadata("firAllOpenPlugin.args") - public void testFirAllOpenPlugin() throws Exception { - runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin.args"); + @TestMetadata("firAllOpenPlugin_legacy.args") + public void testFirAllOpenPlugin_legacy() throws Exception { + runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin_legacy.args"); } - @TestMetadata("pluginSimple.args") - public void testPluginSimple() throws Exception { - runTest("compiler/testData/cli/jvm/plugins/pluginSimple.args"); + @TestMetadata("firAllOpenPlugin_modern.args") + public void testFirAllOpenPlugin_modern() throws Exception { + runTest("compiler/testData/cli/jvm/plugins/firAllOpenPlugin_modern.args"); } - @TestMetadata("pluginWithK2Error.args") - public void testPluginWithK2Error() throws Exception { - runTest("compiler/testData/cli/jvm/plugins/pluginWithK2Error.args"); + @TestMetadata("mixingModernAndLegacyArgs.args") + public void testMixingModernAndLegacyArgs() throws Exception { + 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"); } } diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index c06992929a5..84de4596a0a 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -57,7 +57,7 @@ class K2Native : CLICompiler() { } val pluginLoadResult = - PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, configuration) + PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, arguments.pluginConfigurations, configuration) if (pluginLoadResult != ExitCode.OK) return pluginLoadResult val environment = KotlinCoreEnvironment.createForProduction(rootDisposable,