Scripting: support compiler plugins in hosted script compiler Now the plugin-related compiler arguments are respected then used in the compiler configuration (only in the static part, not supported if set in a refinement callback) Note that the "embeddable" version of the plugin should be used if embeddable compiler is used. #KT-54095 fixed
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 8abbeef..c424503 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/CLICompiler.kt
@@ -173,43 +173,8 @@ val pluginConfigurations = arguments.pluginConfigurations.orEmpty().toMutableList() val messageCollector = configuration.getNotNull(MESSAGE_COLLECTOR_KEY) - for (classpath in pluginClasspaths) { - if (!File(classpath).exists()) { - messageCollector.report(ERROR, "Plugin classpath entry points to a non-existent location: $classpath") - } - } - - if (pluginConfigurations.isNotEmpty()) { - var hasErrors = false - messageCollector.report(WARNING, "Argument -Xcompiler-plugin is experimental") - if (!arguments.useK2) { - hasErrors = true - messageCollector.report( - ERROR, - "-Xcompiler-plugin argument is allowed only for for K2 compiler. Please use -Xplugin argument or enable -Xuse-k2" - ) - } - if (pluginClasspaths.isNotEmpty() || pluginOptions.isNotEmpty()) { - hasErrors = true - 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) - } - if (hasErrors) { - return INTERNAL_ERROR - } + if (!checkPluginsArguments(messageCollector, arguments.useK2, pluginClasspaths, pluginOptions, pluginConfigurations)) { + return INTERNAL_ERROR } if (!arguments.disableDefaultScriptingPlugin) { @@ -259,3 +224,49 @@ } } +fun checkPluginsArguments( + messageCollector: MessageCollector, + useK2: Boolean, + pluginClasspaths: List<String>, + pluginOptions: List<String>, + pluginConfigurations: MutableList<String> +): Boolean { + var hasErrors = false + + for (classpath in pluginClasspaths) { + if (!File(classpath).exists()) { + messageCollector.report(ERROR, "Plugin classpath entry points to a non-existent location: $classpath") + } + } + + if (pluginConfigurations.isNotEmpty()) { + messageCollector.report(WARNING, "Argument -Xcompiler-plugin is experimental") + if (!useK2) { + hasErrors = true + messageCollector.report( + ERROR, + "-Xcompiler-plugin argument is allowed only for for K2 compiler. Please use -Xplugin argument or enable -Xuse-k2" + ) + } + if (pluginClasspaths.isNotEmpty() || pluginOptions.isNotEmpty()) { + hasErrors = true + 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 !hasErrors +} +
diff --git a/libraries/tools/kotlin-main-kts-test/build.gradle.kts b/libraries/tools/kotlin-main-kts-test/build.gradle.kts index 62459f4..23834f6 100644 --- a/libraries/tools/kotlin-main-kts-test/build.gradle.kts +++ b/libraries/tools/kotlin-main-kts-test/build.gradle.kts
@@ -5,6 +5,8 @@ kotlin("jvm") } +val kotlinxSerializationGradlePluginClasspath by configurations.creating + dependencies { testApi(project(":kotlin-main-kts")) testCompileOnly(project(":compiler:cli")) @@ -13,6 +15,7 @@ testApi(commonDependency("junit")) testApi(projectTests(":kotlin-scripting-compiler")) { isTransitive = false } testImplementation(project(":kotlin-compiler-embeddable")) + kotlinxSerializationGradlePluginClasspath(project(":kotlin-serialization")) { isTransitive = false } } sourceSets { @@ -21,7 +24,11 @@ } projectTest(parallel = true) { - dependsOn(":dist") + dependsOn(":dist", ":kotlin-serialization:jar") + val localKotlinxSerializationPluginClasspath: FileCollection = kotlinxSerializationGradlePluginClasspath + doFirst { + systemProperty("kotlin.script.test.kotlinx.serialization.plugin.classpath", localKotlinxSerializationPluginClasspath.asPath) + } workingDir = rootDir }
diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt index c39e9a7..6235439 100644 --- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt
@@ -5,15 +5,17 @@ package org.jetbrains.kotlin.mainKts.test import org.jetbrains.kotlin.mainKts.COMPILED_SCRIPTS_CACHE_DIR_PROPERTY -import org.jetbrains.kotlin.mainKts.impl.Directories import org.jetbrains.kotlin.mainKts.MainKtsScript import org.jetbrains.kotlin.mainKts.SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME +import org.jetbrains.kotlin.mainKts.impl.Directories import org.jetbrains.kotlin.scripting.compiler.plugin.assertTrue import org.junit.Assert import org.junit.Assert.assertEquals import org.junit.Ignore import org.junit.Test -import java.io.* +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream import java.util.* import kotlin.script.experimental.api.* import kotlin.script.experimental.host.toScriptSource @@ -24,21 +26,31 @@ fun evalFile(scriptFile: File, cacheDir: File? = null): ResultWithDiagnostics<EvaluationResult> = withProperty(COMPILED_SCRIPTS_CACHE_DIR_PROPERTY, cacheDir?.absolutePath ?: "") { - val scriptDefinition = createJvmScriptDefinitionFromTemplate<MainKtsScript>( - evaluation = { - jvm { - baseClassLoader(null) - } - constructorArgs(emptyArray<String>()) - enableScriptsInstancesSharing() - } - ) - - BasicJvmScriptingHost().eval( - scriptFile.toScriptSource(), scriptDefinition.compilationConfiguration, scriptDefinition.evaluationConfiguration - ) + evalFileWithConfigurations(scriptFile) } +fun evalFileWithConfigurations( + scriptFile: File, + compilation: ScriptCompilationConfiguration.Builder.() -> Unit = {}, + evaluation: ScriptEvaluationConfiguration.Builder.() -> Unit = {} +): ResultWithDiagnostics<EvaluationResult> { + val scriptDefinition = createJvmScriptDefinitionFromTemplate<MainKtsScript>( + compilation = compilation, + evaluation = { + evaluation() + jvm { + baseClassLoader(null) + } + constructorArgs(emptyArray<String>()) + enableScriptsInstancesSharing() + } + ) + + return BasicJvmScriptingHost().eval( + scriptFile.toScriptSource(), scriptDefinition.compilationConfiguration, scriptDefinition.evaluationConfiguration + ) +} + const val TEST_DATA_ROOT = "libraries/tools/kotlin-main-kts-test/testData" val OUT_FROM_IMPORT_TEST = listOf("Hi from common", "Hi from middle", "Hi from main", "sharedVar == 5") @@ -231,9 +243,32 @@ assertSucceeded(res) } + @Test + fun testHelloSerialization() { + // the embeddable plugin is needed for this test, because embeddable compiler is used. + // furtunately appropriate gradle plugin can serve as an embeddable compiler plugin now + // so, the path to it is prepared in the build file + val serializationPluginClasspath = System.getProperty("kotlin.script.test.kotlinx.serialization.plugin.classpath")!! + val out = captureOut { + val res = evalFileWithConfigurations( + File("$TEST_DATA_ROOT/hello-kotlinx-serialization.main.kts"), + compilation = { + compilerOptions( + "-Xplugin", serializationPluginClasspath + ) + } + ) + assertSucceeded(res) + }.lines() + assertEquals( + listOf("""{"firstName":"James","lastName":"Bond"}""", "User(firstName=James, lastName=Bond)"), + out + ) + } + private fun assertSucceeded(res: ResultWithDiagnostics<EvaluationResult>) { Assert.assertTrue( - "test failed:\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", + "test failed:\n ${res.reports.joinToString("\n ") { it.severity.name + ": " + it.message + if (it.exception == null) "" else ": ${it.exception}" }}", res is ResultWithDiagnostics.Success ) }
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt index eb18d9f..0623632 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.reportArgumentParseProblems import org.jetbrains.kotlin.cli.common.setupCommonArguments +import org.jetbrains.kotlin.cli.common.checkPluginsArguments import org.jetbrains.kotlin.cli.jvm.* import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment @@ -23,6 +24,7 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CompilerConfiguration @@ -199,13 +201,6 @@ val initialScriptCompilationConfiguration = scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration) - kotlinCompilerConfiguration.add( - ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, - ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null) - ) - - kotlinCompilerConfiguration.loadPlugins() - initialScriptCompilationConfiguration[ScriptCompilationConfiguration.compilerOptions]?.let { compilerOptions -> kotlinCompilerConfiguration.updateWithCompilerOptions(compilerOptions, messageCollector, ignoredOptionsReportingState, false) } @@ -339,6 +334,22 @@ configureJdkClasspathRoots() put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, true) + + add( + ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, + ScriptDefinition.FromConfigurations(hostConfiguration, scriptCompilationConfiguration, null) + ) + + val pluginClasspaths = baseArguments.pluginClasspaths?.asList().orEmpty() + val pluginOptions = baseArguments.pluginOptions?.asList().orEmpty() + val pluginConfigurations = baseArguments.pluginConfigurations.orEmpty().toMutableList() + + checkPluginsArguments(messageCollector, false, pluginClasspaths, pluginOptions, pluginConfigurations) + if (pluginClasspaths.isNotEmpty() || pluginConfigurations.isNotEmpty()) { + PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, pluginConfigurations, this) + } else { + loadPlugins() + } } }
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt index 414d642..9d24bc4 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/errorReporting.kt
@@ -160,7 +160,6 @@ K2JVMCompilerArguments::disableStandardScript, K2JVMCompilerArguments::defaultScriptExtension, K2JVMCompilerArguments::disableDefaultScriptingPlugin, - K2JVMCompilerArguments::pluginClasspaths, K2JVMCompilerArguments::useJavac, K2JVMCompilerArguments::compileJava, K2JVMCompilerArguments::reportPerf, @@ -181,7 +180,10 @@ K2JVMCompilerArguments::javaModulePath, K2JVMCompilerArguments::classpath, K2JVMCompilerArguments::noStdlib, - K2JVMCompilerArguments::noReflect + K2JVMCompilerArguments::noReflect, + K2JVMCompilerArguments::pluginClasspaths, + K2JVMCompilerArguments::pluginOptions, + K2JVMCompilerArguments::pluginConfigurations, )
diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt index 21b70cf..12faf74 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/loadCompilerPlugins.kt
@@ -30,8 +30,7 @@ ScriptingCommandLineProcessor::class.java.name ) -internal fun CompilerConfiguration.loadPlugins() { - val classLoader = CompilerConfiguration::class.java.classLoader +internal fun CompilerConfiguration.loadPlugins(classLoader: ClassLoader = CompilerConfiguration::class.java.classLoader) { val registrars = classLoader.loadServices<ComponentRegistrar>(scriptCompilationDisabledPlugins, SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY) addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, registrars)