Process compiler plugins and options in scripting compiler
#KT-37766 fixed
This commit is contained in:
@@ -77,44 +77,52 @@ object PluginCliParser {
|
|||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
classLoader: URLClassLoader
|
classLoader: URLClassLoader
|
||||||
) {
|
) {
|
||||||
val optionValuesByPlugin = pluginOptions?.map(::parsePluginOption)?.groupBy {
|
|
||||||
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
|
||||||
it.pluginId
|
|
||||||
} ?: mapOf()
|
|
||||||
|
|
||||||
// TODO issue a warning on using deprecated command line processors when all official plugin migrate to the newer convention
|
// TODO issue a warning on using deprecated command line processors when all official plugin migrate to the newer convention
|
||||||
val commandLineProcessors = ServiceLoaderLite.loadImplementations(CommandLineProcessor::class.java, classLoader)
|
val commandLineProcessors = ServiceLoaderLite.loadImplementations(CommandLineProcessor::class.java, classLoader)
|
||||||
|
|
||||||
for (processor in commandLineProcessors) {
|
processCompilerPluginsOptions(configuration, pluginOptions, commandLineProcessors)
|
||||||
val declaredOptions = processor.pluginOptions.associateBy { it.optionName }
|
}
|
||||||
val optionsToValues = MultiMap<AbstractCliOption, CliOptionValue>()
|
}
|
||||||
|
|
||||||
for (optionValue in optionValuesByPlugin[processor.pluginId].orEmpty()) {
|
fun processCompilerPluginsOptions(
|
||||||
val option = declaredOptions[optionValue!!.optionName]
|
configuration: CompilerConfiguration,
|
||||||
?: throw CliOptionProcessingException("Unsupported plugin option: $optionValue")
|
pluginOptions: Iterable<String>?,
|
||||||
optionsToValues.putValue(option, optionValue)
|
commandLineProcessors: List<CommandLineProcessor>
|
||||||
|
) {
|
||||||
|
val optionValuesByPlugin = pluginOptions?.map(::parsePluginOption)?.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>()
|
||||||
|
|
||||||
|
for (optionValue in optionValuesByPlugin[processor.pluginId].orEmpty()) {
|
||||||
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -15,12 +15,12 @@ import java.util.jar.JarFile
|
|||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
import kotlin.script.experimental.jvm.impl.toFileOrNull
|
import kotlin.script.experimental.jvm.impl.toFileOrNull
|
||||||
|
|
||||||
fun ClassLoader.forAllMatchingFiles(namePattern: String, body: (String, InputStream) -> Unit) {
|
fun ClassLoader.forAllMatchingFiles(namePattern: String, vararg keyResourcePaths: String, body: (String, InputStream) -> Unit) {
|
||||||
val processedDirs = HashSet<File>()
|
val processedDirs = HashSet<File>()
|
||||||
val processedJars = HashSet<URL>()
|
val processedJars = HashSet<URL>()
|
||||||
val nameRegex = namePatternToRegex(namePattern)
|
val nameRegex = namePatternToRegex(namePattern)
|
||||||
|
|
||||||
fun iterateResources(vararg keyResourcePaths: String) {
|
fun iterateResources(keyResourcePaths: Array<out String>) {
|
||||||
for (keyResourcePath in keyResourcePaths) {
|
for (keyResourcePath in keyResourcePaths) {
|
||||||
val resourceRootCalc = ClassLoaderResourceRootFIlePathCalculator(keyResourcePath)
|
val resourceRootCalc = ClassLoaderResourceRootFIlePathCalculator(keyResourcePath)
|
||||||
for (url in getResources(keyResourcePath)) {
|
for (url in getResources(keyResourcePath)) {
|
||||||
@@ -49,7 +49,7 @@ fun ClassLoader.forAllMatchingFiles(namePattern: String, body: (String, InputStr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iterateResources("", JAR_MANIFEST_RESOURCE_NAME)
|
iterateResources(if (keyResourcePaths.isEmpty()) arrayOf("", JAR_MANIFEST_RESOURCE_NAME) else keyResourcePaths)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val wildcardChars = "*?".toCharArray()
|
internal val wildcardChars = "*?".toCharArray()
|
||||||
|
|||||||
+4
@@ -147,6 +147,8 @@ internal fun createInitialConfigurations(
|
|||||||
ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null)
|
ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
kotlinCompilerConfiguration.loadPlugins()
|
||||||
|
|
||||||
initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions ->
|
initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions ->
|
||||||
kotlinCompilerConfiguration.updateWithCompilerOptions(compilerOptions, messageCollector, ignoredOptionsReportingState, false)
|
kotlinCompilerConfiguration.updateWithCompilerOptions(compilerOptions, messageCollector, ignoredOptionsReportingState, false)
|
||||||
}
|
}
|
||||||
@@ -176,6 +178,8 @@ private fun CompilerConfiguration.updateWithCompilerOptions(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processPluginsCommandLine(compilerArguments)
|
||||||
|
|
||||||
setupCommonArguments(compilerArguments)
|
setupCommonArguments(compilerArguments)
|
||||||
|
|
||||||
setupJvmSpecificArguments(compilerArguments)
|
setupJvmSpecificArguments(compilerArguments)
|
||||||
|
|||||||
-1
@@ -115,7 +115,6 @@ internal fun reportArgumentsIgnoredGenerally(
|
|||||||
K2JVMCompilerArguments::disableStandardScript,
|
K2JVMCompilerArguments::disableStandardScript,
|
||||||
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
||||||
K2JVMCompilerArguments::pluginClasspaths,
|
K2JVMCompilerArguments::pluginClasspaths,
|
||||||
K2JVMCompilerArguments::pluginOptions,
|
|
||||||
K2JVMCompilerArguments::useJavac,
|
K2JVMCompilerArguments::useJavac,
|
||||||
K2JVMCompilerArguments::compileJava,
|
K2JVMCompilerArguments::compileJava,
|
||||||
K2JVMCompilerArguments::reportPerf,
|
K2JVMCompilerArguments::reportPerf,
|
||||||
|
|||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.scripting.compiler.plugin.impl
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.plugins.processCompilerPluginsOptions
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCommandLineProcessor
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
|
||||||
|
import kotlin.script.experimental.jvm.util.forAllMatchingFiles
|
||||||
|
|
||||||
|
private const val SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY = "script.compilation.disable.plugins"
|
||||||
|
private const val SCRIPT_COMPILATION_DISABLE_COMMANDLINE_PROCESSORS_PROPERTY = "script.compilation.disable.commandline.processors"
|
||||||
|
|
||||||
|
private val scriptCompilationDisabledPlugins =
|
||||||
|
listOf(
|
||||||
|
ScriptingCompilerConfigurationComponentRegistrar::class.java.name
|
||||||
|
)
|
||||||
|
|
||||||
|
private val scriptCompilationDisabledCommandlineProcessors =
|
||||||
|
listOf(
|
||||||
|
ScriptingCommandLineProcessor::class.java.name
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun CompilerConfiguration.loadPlugins() {
|
||||||
|
val classLoader = CompilerConfiguration::class.java.classLoader
|
||||||
|
val registrars =
|
||||||
|
classLoader.loadServices<ComponentRegistrar>(scriptCompilationDisabledPlugins, SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY)
|
||||||
|
addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, registrars)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun CompilerConfiguration.processPluginsCommandLine(arguments: K2JVMCompilerArguments) {
|
||||||
|
val classLoader = CompilerConfiguration::class.java.classLoader
|
||||||
|
val pluginOptions = arguments.pluginOptions?.asIterable() ?: emptyList()
|
||||||
|
|
||||||
|
val commandLineProcessors =
|
||||||
|
classLoader.loadServices<CommandLineProcessor>(
|
||||||
|
scriptCompilationDisabledCommandlineProcessors, SCRIPT_COMPILATION_DISABLE_COMMANDLINE_PROCESSORS_PROPERTY
|
||||||
|
)
|
||||||
|
processCompilerPluginsOptions(this, pluginOptions, commandLineProcessors)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified Service : Any> ClassLoader.loadServices(disabled: List<String>, disablingProperty: String): List<Service> {
|
||||||
|
val disabledServiceNames = disabled.toHashSet()
|
||||||
|
System.getProperty(disablingProperty)?.let {
|
||||||
|
it.split(',', ';', ' ').forEach { name ->
|
||||||
|
disabledServiceNames.add(name.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loadServices {
|
||||||
|
!disabledServiceNames.contains(it) && !disabledServiceNames.contains(it.substringAfterLast('.'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val SERVICE_DIRECTORY_LOCATION = "META-INF/services/"
|
||||||
|
|
||||||
|
private inline fun <reified Service : Any> ClassLoader.loadServices(isEnabled: (String) -> Boolean): List<Service> {
|
||||||
|
val registrarsNames = HashSet<String>()
|
||||||
|
val serviceFileName = SERVICE_DIRECTORY_LOCATION + Service::class.java.name
|
||||||
|
|
||||||
|
forAllMatchingFiles(serviceFileName, serviceFileName) { name, stream ->
|
||||||
|
stream.reader().useLines {
|
||||||
|
it.mapNotNullTo(registrarsNames) { parseServiceFileLine(name, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return registrarsNames.mapNotNull { if (isEnabled(it)) (loadClass(it).newInstance() as Service) else null }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseServiceFileLine(location: String, line: String): String? {
|
||||||
|
val actualLine = line.substringBefore('#').trim().takeIf { it.isNotEmpty() } ?: return null
|
||||||
|
actualLine.forEachIndexed { index: Int, c: Char ->
|
||||||
|
val isValid = if (index == 0) Character.isJavaIdentifierStart(c) else Character.isJavaIdentifierPart(c) || c == '.'
|
||||||
|
if (!isValid) {
|
||||||
|
val errorText = "Invalid Java identifier: $line"
|
||||||
|
throw RuntimeException("Error loading services from $location : $errorText")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actualLine
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user