[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
@@ -20,6 +20,7 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
private val serialVersionUID = 0L
const val PLUGIN_OPTION_FORMAT = "plugin:<pluginId>:<optionName>=<value>"
const val PLUGIN_DECLARATION_FORMAT = "<path>[=<optionName>=<value>]"
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<String>? by FreezableVar(null)
@Argument(
value = "-opt-in",
deprecatedName = "-Xopt-in",
@@ -107,6 +105,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
@Argument(value = "-Xplugin", valueDescription = "<path>", description = "Load plugins from the given classpath")
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")
var multiPlatform: Boolean by FreezableVar(false)
@@ -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<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(
configuration: CompilerConfiguration,
pluginOptions: Iterable<String>?,
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}")
it.pluginId
} ?: mapOf()
for (processor in commandLineProcessors) {
val declaredOptions = processor.pluginOptions.associateBy { it.optionName }
val optionsToValues = MultiMap<AbstractCliOption, CliOptionValue>()
@Suppress("UNCHECKED_CAST")
processCompilerPluginOptions(processor, optionValuesByPlugin[processor.pluginId].orEmpty() as List<CliOptionValue>, 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<CliOptionValue>,
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) {
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)
}
}
}