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
This commit is contained in:
@@ -173,43 +173,8 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
|
|||||||
val pluginConfigurations = arguments.pluginConfigurations.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) {
|
if (!checkPluginsArguments(messageCollector, arguments.useK2, pluginClasspaths, pluginOptions, pluginConfigurations)) {
|
||||||
if (!File(classpath).exists()) {
|
return INTERNAL_ERROR
|
||||||
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 (!arguments.disableDefaultScriptingPlugin) {
|
if (!arguments.disableDefaultScriptingPlugin) {
|
||||||
@@ -259,3 +224,49 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ plugins {
|
|||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val kotlinxSerializationGradlePluginClasspath by configurations.creating
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testApi(project(":kotlin-main-kts"))
|
testApi(project(":kotlin-main-kts"))
|
||||||
testCompileOnly(project(":compiler:cli"))
|
testCompileOnly(project(":compiler:cli"))
|
||||||
@@ -13,6 +15,7 @@ dependencies {
|
|||||||
testApi(commonDependency("junit"))
|
testApi(commonDependency("junit"))
|
||||||
testApi(projectTests(":kotlin-scripting-compiler")) { isTransitive = false }
|
testApi(projectTests(":kotlin-scripting-compiler")) { isTransitive = false }
|
||||||
testImplementation(project(":kotlin-compiler-embeddable"))
|
testImplementation(project(":kotlin-compiler-embeddable"))
|
||||||
|
kotlinxSerializationGradlePluginClasspath(project(":kotlin-serialization")) { isTransitive = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -21,7 +24,11 @@ sourceSets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
projectTest(parallel = true) {
|
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
|
workingDir = rootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+51
-16
@@ -5,15 +5,17 @@
|
|||||||
package org.jetbrains.kotlin.mainKts.test
|
package org.jetbrains.kotlin.mainKts.test
|
||||||
|
|
||||||
import org.jetbrains.kotlin.mainKts.COMPILED_SCRIPTS_CACHE_DIR_PROPERTY
|
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.MainKtsScript
|
||||||
import org.jetbrains.kotlin.mainKts.SCRIPT_FILE_LOCATION_DEFAULT_VARIABLE_NAME
|
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.jetbrains.kotlin.scripting.compiler.plugin.assertTrue
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.*
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.PrintStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.script.experimental.api.*
|
import kotlin.script.experimental.api.*
|
||||||
import kotlin.script.experimental.host.toScriptSource
|
import kotlin.script.experimental.host.toScriptSource
|
||||||
@@ -24,21 +26,31 @@ import kotlin.script.experimental.jvmhost.createJvmScriptDefinitionFromTemplate
|
|||||||
|
|
||||||
fun evalFile(scriptFile: File, cacheDir: File? = null): ResultWithDiagnostics<EvaluationResult> =
|
fun evalFile(scriptFile: File, cacheDir: File? = null): ResultWithDiagnostics<EvaluationResult> =
|
||||||
withProperty(COMPILED_SCRIPTS_CACHE_DIR_PROPERTY, cacheDir?.absolutePath ?: "") {
|
withProperty(COMPILED_SCRIPTS_CACHE_DIR_PROPERTY, cacheDir?.absolutePath ?: "") {
|
||||||
val scriptDefinition = createJvmScriptDefinitionFromTemplate<MainKtsScript>(
|
evalFileWithConfigurations(scriptFile)
|
||||||
evaluation = {
|
|
||||||
jvm {
|
|
||||||
baseClassLoader(null)
|
|
||||||
}
|
|
||||||
constructorArgs(emptyArray<String>())
|
|
||||||
enableScriptsInstancesSharing()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
BasicJvmScriptingHost().eval(
|
|
||||||
scriptFile.toScriptSource(), scriptDefinition.compilationConfiguration, scriptDefinition.evaluationConfiguration
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"
|
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")
|
val OUT_FROM_IMPORT_TEST = listOf("Hi from common", "Hi from middle", "Hi from main", "sharedVar == 5")
|
||||||
@@ -231,9 +243,32 @@ class MainKtsTest {
|
|||||||
assertSucceeded(res)
|
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>) {
|
private fun assertSucceeded(res: ResultWithDiagnostics<EvaluationResult>) {
|
||||||
Assert.assertTrue(
|
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
|
res is ResultWithDiagnostics.Success
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-7
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
|||||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
import org.jetbrains.kotlin.cli.common.reportArgumentParseProblems
|
import org.jetbrains.kotlin.cli.common.reportArgumentParseProblems
|
||||||
import org.jetbrains.kotlin.cli.common.setupCommonArguments
|
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.*
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
|
||||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||||
@@ -23,6 +24,7 @@ import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
|||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
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.compiler.plugin.ComponentRegistrar
|
||||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
@@ -199,13 +201,6 @@ internal fun createInitialConfigurations(
|
|||||||
val initialScriptCompilationConfiguration =
|
val initialScriptCompilationConfiguration =
|
||||||
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
scriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration)
|
||||||
|
|
||||||
kotlinCompilerConfiguration.add(
|
|
||||||
ScriptingConfigurationKeys.SCRIPT_DEFINITIONS,
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -339,6 +334,22 @@ private fun createInitialCompilerConfiguration(
|
|||||||
configureJdkClasspathRoots()
|
configureJdkClasspathRoots()
|
||||||
|
|
||||||
put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, true)
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -160,7 +160,6 @@ internal fun reportArgumentsIgnoredGenerally(
|
|||||||
K2JVMCompilerArguments::disableStandardScript,
|
K2JVMCompilerArguments::disableStandardScript,
|
||||||
K2JVMCompilerArguments::defaultScriptExtension,
|
K2JVMCompilerArguments::defaultScriptExtension,
|
||||||
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
K2JVMCompilerArguments::disableDefaultScriptingPlugin,
|
||||||
K2JVMCompilerArguments::pluginClasspaths,
|
|
||||||
K2JVMCompilerArguments::useJavac,
|
K2JVMCompilerArguments::useJavac,
|
||||||
K2JVMCompilerArguments::compileJava,
|
K2JVMCompilerArguments::compileJava,
|
||||||
K2JVMCompilerArguments::reportPerf,
|
K2JVMCompilerArguments::reportPerf,
|
||||||
@@ -181,7 +180,10 @@ internal fun reportArgumentsIgnoredFromRefinement(
|
|||||||
K2JVMCompilerArguments::javaModulePath,
|
K2JVMCompilerArguments::javaModulePath,
|
||||||
K2JVMCompilerArguments::classpath,
|
K2JVMCompilerArguments::classpath,
|
||||||
K2JVMCompilerArguments::noStdlib,
|
K2JVMCompilerArguments::noStdlib,
|
||||||
K2JVMCompilerArguments::noReflect
|
K2JVMCompilerArguments::noReflect,
|
||||||
|
K2JVMCompilerArguments::pluginClasspaths,
|
||||||
|
K2JVMCompilerArguments::pluginOptions,
|
||||||
|
K2JVMCompilerArguments::pluginConfigurations,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -30,8 +30,7 @@ private val scriptCompilationDisabledCommandlineProcessors =
|
|||||||
ScriptingCommandLineProcessor::class.java.name
|
ScriptingCommandLineProcessor::class.java.name
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun CompilerConfiguration.loadPlugins() {
|
internal fun CompilerConfiguration.loadPlugins(classLoader: ClassLoader = CompilerConfiguration::class.java.classLoader) {
|
||||||
val classLoader = CompilerConfiguration::class.java.classLoader
|
|
||||||
val registrars =
|
val registrars =
|
||||||
classLoader.loadServices<ComponentRegistrar>(scriptCompilationDisabledPlugins, SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY)
|
classLoader.loadServices<ComponentRegistrar>(scriptCompilationDisabledPlugins, SCRIPT_COMPILATION_DISABLE_PLUGINS_PROPERTY)
|
||||||
addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, registrars)
|
addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, registrars)
|
||||||
|
|||||||
Reference in New Issue
Block a user