Move scripting configuration into compiler plugin
This commit is contained in:
@@ -165,7 +165,7 @@ public class K2JSCompiler extends CLICompiler<K2JSCompilerArguments> {
|
|||||||
return COMPILATION_ERROR;
|
return COMPILATION_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitCode pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments, configuration);
|
ExitCode pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments.getPluginClasspaths(), arguments.getPluginOptions(), configuration);
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult;
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult;
|
||||||
|
|
||||||
configuration.put(JSConfigurationKeys.LIBRARIES, configureLibraries(arguments, paths, messageCollector));
|
configuration.put(JSConfigurationKeys.LIBRARIES, configureLibraries(arguments, paths, messageCollector));
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
|
|||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
|
||||||
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
import org.jetbrains.kotlin.cli.jvm.modules.CoreJrtFileSystem
|
||||||
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
|
import org.jetbrains.kotlin.cli.jvm.plugins.PluginCliParser
|
||||||
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
import org.jetbrains.kotlin.cli.jvm.repl.ReplFromTerminal
|
||||||
@@ -45,22 +44,14 @@ import org.jetbrains.kotlin.javac.JavacWrapper
|
|||||||
import org.jetbrains.kotlin.load.java.JavaClassesTracker
|
import org.jetbrains.kotlin.load.java.JavaClassesTracker
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
|
||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
|
||||||
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
||||||
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
import org.jetbrains.kotlin.script.StandardScriptDefinition
|
||||||
import org.jetbrains.kotlin.script.experimental.KotlinScriptDefinitionAdapterFromNewAPI
|
|
||||||
import org.jetbrains.kotlin.util.PerformanceCounter
|
import org.jetbrains.kotlin.util.PerformanceCounter
|
||||||
import org.jetbrains.kotlin.utils.KotlinPaths
|
import org.jetbrains.kotlin.utils.KotlinPaths
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.jar.JarFile
|
|
||||||
import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass
|
|
||||||
|
|
||||||
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||||
override fun doExecute(
|
override fun doExecute(
|
||||||
@@ -77,7 +68,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
if (it != OK) return it
|
if (it != OK) return it
|
||||||
}
|
}
|
||||||
|
|
||||||
val pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments, configuration)
|
val pluginLoadResult = loadPlugins(paths, arguments, configuration)
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
||||||
|
|
||||||
if (!arguments.script && arguments.buildFile == null) {
|
if (!arguments.script && arguments.buildFile == null) {
|
||||||
@@ -153,7 +144,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
|
|
||||||
KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleChunk.modules, buildFile)
|
KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleChunk.modules, buildFile)
|
||||||
|
|
||||||
val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector)
|
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
|
||||||
?: return COMPILATION_ERROR
|
?: return COMPILATION_ERROR
|
||||||
|
|
||||||
registerJavacIfNeeded(environment, arguments).let {
|
registerJavacIfNeeded(environment, arguments).let {
|
||||||
@@ -167,7 +158,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
|
|
||||||
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
|
||||||
|
|
||||||
val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector)
|
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
|
||||||
?: return COMPILATION_ERROR
|
?: return COMPILATION_ERROR
|
||||||
|
|
||||||
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(environment.project)
|
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(environment.project)
|
||||||
@@ -191,7 +182,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector)
|
val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector)
|
||||||
?: return COMPILATION_ERROR
|
?: return COMPILATION_ERROR
|
||||||
|
|
||||||
registerJavacIfNeeded(environment, arguments).let {
|
registerJavacIfNeeded(environment, arguments).let {
|
||||||
@@ -229,6 +220,39 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadPlugins(paths: KotlinPaths?, arguments: K2JVMCompilerArguments, configuration: CompilerConfiguration): ExitCode {
|
||||||
|
val pluginClasspaths = arguments.pluginClasspaths?.toMutableList() ?: ArrayList()
|
||||||
|
val pluginOptions = arguments.pluginOptions?.toMutableList() ?: ArrayList()
|
||||||
|
val isEmbeddable =
|
||||||
|
try {
|
||||||
|
this::class.java.classLoader.loadClass("com.intellij.mock.MockProject") == null
|
||||||
|
} catch (_: ClassNotFoundException) {
|
||||||
|
true
|
||||||
|
} catch (_: NoClassDefFoundError) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEmbeddable && pluginClasspaths.none { File(it).name == PathUtil.KOTLIN_SCRIPTING_COMPILER_PLUGIN_JAR }) {
|
||||||
|
// if scripting plugin is not enabled explicitly (probably from another path) try to enable it implicitly
|
||||||
|
val libPath = paths?.libPath ?: File(".")
|
||||||
|
val pluginJar = File(libPath, PathUtil.KOTLIN_SCRIPTING_COMPILER_PLUGIN_JAR)
|
||||||
|
val scriptingJar = File(libPath, PathUtil.KOTLIN_SCRIPTING_COMMON_JAR)
|
||||||
|
val scriptingJvmJar = File(libPath, PathUtil.KOTLIN_SCRIPTING_JVM_JAR)
|
||||||
|
if (pluginJar.exists() && scriptingJar.exists() && scriptingJvmJar.exists()) {
|
||||||
|
pluginClasspaths.addAll(listOf(pluginJar.canonicalPath, scriptingJar.canonicalPath, scriptingJvmJar.canonicalPath))
|
||||||
|
if (arguments.scriptTemplates?.isNotEmpty() == true) {
|
||||||
|
pluginOptions.add("-P")
|
||||||
|
pluginOptions.add("plugin:kotlin.scripting:script-templates=${arguments.scriptTemplates!!.joinToString(",")}")
|
||||||
|
}
|
||||||
|
if (arguments.scriptResolverEnvironment?.isNotEmpty() == true) {
|
||||||
|
pluginOptions.add("-P")
|
||||||
|
pluginOptions.add("plugin:kotlin.scripting:script-resolver-environment=${arguments.scriptResolverEnvironment!!.joinToString(",")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PluginCliParser.loadPluginsSafe(pluginClasspaths, pluginOptions, configuration)
|
||||||
|
}
|
||||||
|
|
||||||
private fun registerJavacIfNeeded(
|
private fun registerJavacIfNeeded(
|
||||||
environment: KotlinCoreEnvironment,
|
environment: KotlinCoreEnvironment,
|
||||||
arguments: K2JVMCompilerArguments
|
arguments: K2JVMCompilerArguments
|
||||||
@@ -257,17 +281,8 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
private fun createCoreEnvironment(
|
private fun createCoreEnvironment(
|
||||||
rootDisposable: Disposable,
|
rootDisposable: Disposable,
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
arguments: K2JVMCompilerArguments,
|
|
||||||
messageCollector: MessageCollector
|
messageCollector: MessageCollector
|
||||||
): KotlinCoreEnvironment? {
|
): KotlinCoreEnvironment? {
|
||||||
val scriptResolverEnv = createScriptResolverEnvironment(arguments, messageCollector) ?: return null
|
|
||||||
val templatesFromClasspath = discoverScriptTemplatesInClasspath(configuration, messageCollector)
|
|
||||||
configureScriptDefinitions(
|
|
||||||
arguments.scriptTemplates.orEmpty().asIterable() + templatesFromClasspath,
|
|
||||||
configuration,
|
|
||||||
messageCollector,
|
|
||||||
scriptResolverEnv
|
|
||||||
)
|
|
||||||
if (messageCollector.hasErrors()) return null
|
if (messageCollector.hasErrors()) return null
|
||||||
|
|
||||||
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
|
||||||
@@ -278,55 +293,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
initStartNanos = 0L
|
initStartNanos = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!messageCollector.hasErrors()) {
|
return if (messageCollector.hasErrors()) null else environment
|
||||||
scriptResolverEnv.put("projectRoot", environment.project.run { basePath ?: baseDir?.canonicalPath }?.let(::File))
|
|
||||||
return environment
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun discoverScriptTemplatesInClasspath(configuration: CompilerConfiguration, messageCollector: MessageCollector): Iterable<String> {
|
|
||||||
val templates = arrayListOf<String>()
|
|
||||||
val templatesPath = "META-INF/kotlin/script/templates/"
|
|
||||||
for (dep in configuration.jvmClasspathRoots) {
|
|
||||||
when {
|
|
||||||
dep.isFile ->
|
|
||||||
try {
|
|
||||||
with(JarFile(dep)) {
|
|
||||||
for (template in entries()) {
|
|
||||||
if (!template.isDirectory && template.name.startsWith(templatesPath)) {
|
|
||||||
val templateClassName = template.name.removePrefix(templatesPath)
|
|
||||||
templates.add(templateClassName)
|
|
||||||
messageCollector.report(
|
|
||||||
CompilerMessageSeverity.LOGGING,
|
|
||||||
"Configure scripting: Added template $templateClassName from $dep"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
messageCollector.report(
|
|
||||||
CompilerMessageSeverity.WARNING,
|
|
||||||
"Configure scripting: unable to process classpath entry $dep: $e"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
dep.isDirectory -> {
|
|
||||||
val dir = File(dep, templatesPath)
|
|
||||||
if (dir.isDirectory) {
|
|
||||||
dir.listFiles().forEach {
|
|
||||||
templates.add(it.name)
|
|
||||||
messageCollector.report(
|
|
||||||
CompilerMessageSeverity.LOGGING,
|
|
||||||
"Configure scripting: Added template ${it.name} from $dep"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> messageCollector.report(CompilerMessageSeverity.WARNING, "Configure scripting: Unknown classpath entry $dep")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return templates
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setupPlatformSpecificArgumentsAndServices(
|
override fun setupPlatformSpecificArgumentsAndServices(
|
||||||
@@ -506,73 +473,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
|||||||
|
|
||||||
return OK
|
return OK
|
||||||
}
|
}
|
||||||
|
|
||||||
fun configureScriptDefinitions(
|
|
||||||
scriptTemplates: List<String>,
|
|
||||||
configuration: CompilerConfiguration,
|
|
||||||
messageCollector: MessageCollector,
|
|
||||||
scriptResolverEnv: HashMap<String, Any?>
|
|
||||||
) {
|
|
||||||
val classpath = configuration.jvmClasspathRoots
|
|
||||||
// TODO: consider using escaping to allow kotlin escaped names in class names
|
|
||||||
if (scriptTemplates.isNotEmpty()) {
|
|
||||||
val classloader =
|
|
||||||
URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)
|
|
||||||
var hasErrors = false
|
|
||||||
for (template in scriptTemplates) {
|
|
||||||
try {
|
|
||||||
val cls = classloader.loadClass(template)
|
|
||||||
val def =
|
|
||||||
if (cls.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null) {
|
|
||||||
KotlinScriptDefinitionAdapterFromNewAPI(ScriptDefinitionFromAnnotatedBaseClass(cls.kotlin))
|
|
||||||
} else {
|
|
||||||
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
|
|
||||||
}
|
|
||||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, def)
|
|
||||||
messageCollector.report(
|
|
||||||
INFO,
|
|
||||||
"Added script definition $template to configuration: name = ${def.name}, " +
|
|
||||||
"resolver = ${def.dependencyResolver.javaClass.name}"
|
|
||||||
)
|
|
||||||
} catch (ex: ClassNotFoundException) {
|
|
||||||
messageCollector.report(ERROR, "Cannot find script definition template class $template")
|
|
||||||
hasErrors = true
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
messageCollector.report(ERROR, "Error processing script definition template $template: ${ex.message}")
|
|
||||||
hasErrors = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasErrors) {
|
|
||||||
messageCollector.report(LOGGING, "(Classpath used for templates loading: $classpath)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createScriptResolverEnvironment(arguments: K2JVMCompilerArguments, messageCollector: MessageCollector): HashMap<String, Any?>? {
|
|
||||||
val scriptResolverEnv = hashMapOf<String, Any?>()
|
|
||||||
// parses key/value pairs in the form <key>=<value>, where
|
|
||||||
// <key> - is a single word (\w+ pattern)
|
|
||||||
// <value> - optionally quoted string with allowed escaped chars (only double-quote and backslash chars are supported)
|
|
||||||
// TODO: implement generic unescaping
|
|
||||||
val envParseRe = """(\w+)=(?:"([^"\\]*(\\.[^"\\]*)*)"|([^\s]*))""".toRegex()
|
|
||||||
val unescapeRe = """\\(["\\])""".toRegex()
|
|
||||||
if (arguments.scriptResolverEnvironment != null) {
|
|
||||||
for (envParam in arguments.scriptResolverEnvironment!!) {
|
|
||||||
val match = envParseRe.matchEntire(envParam)
|
|
||||||
if (match == null || match.groupValues.size < 4 || match.groupValues[1].isBlank()) {
|
|
||||||
messageCollector.report(ERROR, "Unable to parse script-resolver-environment argument $envParam")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
scriptResolverEnv.put(
|
|
||||||
match.groupValues[1],
|
|
||||||
match.groupValues.drop(2).firstOrNull { it.isNotEmpty() }?.let { unescapeRe.replace(it, "\$1") })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scriptResolverEnv
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
|||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.*
|
||||||
|
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
|
||||||
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
|
import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension
|
||||||
import org.jetbrains.kotlin.extensions.PreprocessedVirtualFileFactoryExtension
|
import org.jetbrains.kotlin.extensions.PreprocessedVirtualFileFactoryExtension
|
||||||
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||||
@@ -178,6 +179,7 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
|
DeclarationAttributeAltererExtension.registerExtensionPoint(project)
|
||||||
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
|
PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project)
|
||||||
JsSyntheticTranslateExtension.registerExtensionPoint(project)
|
JsSyntheticTranslateExtension.registerExtensionPoint(project)
|
||||||
|
CompilerConfigurationExtension.registerExtensionPoint(project)
|
||||||
|
|
||||||
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) {
|
||||||
try {
|
try {
|
||||||
@@ -195,12 +197,19 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
registerProjectServices(projectEnvironment, messageCollector)
|
registerProjectServices(projectEnvironment, messageCollector)
|
||||||
|
|
||||||
|
CompilerConfigurationExtension.getInstances(project).forEach {
|
||||||
|
it.updateConfiguration(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
sourceFiles += CompileEnvironmentUtil.getKtFiles(project, getSourceRootsCheckingForDuplicates(), this.configuration, {
|
sourceFiles += CompileEnvironmentUtil.getKtFiles(project, getSourceRootsCheckingForDuplicates(), this.configuration, {
|
||||||
message ->
|
message ->
|
||||||
report(ERROR, message)
|
report(ERROR, message)
|
||||||
})
|
})
|
||||||
sourceFiles.sortBy { it.virtualFile.path }
|
sourceFiles.sortBy { it.virtualFile.path }
|
||||||
|
|
||||||
|
// We should always support at least the standard script definition
|
||||||
|
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition)
|
||||||
|
|
||||||
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
|
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider
|
||||||
if (scriptDefinitionProvider != null) {
|
if (scriptDefinitionProvider != null) {
|
||||||
scriptDefinitionProvider.setScriptDefinitions(
|
scriptDefinitionProvider.setScriptDefinitions(
|
||||||
|
|||||||
@@ -32,11 +32,15 @@ import java.util.*
|
|||||||
object PluginCliParser {
|
object PluginCliParser {
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadPluginsSafe(arguments: CommonCompilerArguments, configuration: CompilerConfiguration): ExitCode {
|
fun loadPluginsSafe(pluginClasspaths: Array<String>?, pluginOptions: Array<String>?, configuration: CompilerConfiguration): ExitCode =
|
||||||
|
loadPluginsSafe(pluginClasspaths?.asIterable(), pluginOptions?.asIterable(), configuration)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun loadPluginsSafe(pluginClasspaths: Iterable<String>?, pluginOptions: Iterable<String>?, configuration: CompilerConfiguration): ExitCode {
|
||||||
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PluginCliParser.loadPlugins(arguments, configuration)
|
PluginCliParser.loadPlugins(pluginClasspaths, pluginOptions, configuration)
|
||||||
}
|
}
|
||||||
catch (e: PluginCliOptionProcessingException) {
|
catch (e: PluginCliOptionProcessingException) {
|
||||||
val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options)
|
val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options)
|
||||||
@@ -55,9 +59,9 @@ object PluginCliParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun loadPlugins(arguments: CommonCompilerArguments, configuration: CompilerConfiguration) {
|
fun loadPlugins(pluginClasspaths: Iterable<String>?, pluginOptions: Iterable<String>?, configuration: CompilerConfiguration) {
|
||||||
val classLoader = PluginURLClassLoader(
|
val classLoader = PluginURLClassLoader(
|
||||||
arguments.pluginClasspaths
|
pluginClasspaths
|
||||||
?.map { File(it).toURI().toURL() }
|
?.map { File(it).toURI().toURL() }
|
||||||
?.toTypedArray()
|
?.toTypedArray()
|
||||||
?: arrayOf<URL>(),
|
?: arrayOf<URL>(),
|
||||||
@@ -68,15 +72,15 @@ object PluginCliParser {
|
|||||||
componentRegistrars.addAll(BundledCompilerPlugins.componentRegistrars)
|
componentRegistrars.addAll(BundledCompilerPlugins.componentRegistrars)
|
||||||
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars)
|
||||||
|
|
||||||
processPluginOptions(arguments, configuration, classLoader)
|
processPluginOptions(pluginOptions, configuration, classLoader)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processPluginOptions(
|
private fun processPluginOptions(
|
||||||
arguments: CommonCompilerArguments,
|
pluginOptions: Iterable<String>?,
|
||||||
configuration: CompilerConfiguration,
|
configuration: CompilerConfiguration,
|
||||||
classLoader: ClassLoader
|
classLoader: ClassLoader
|
||||||
) {
|
) {
|
||||||
val optionValuesByPlugin = arguments.pluginOptions?.map(::parsePluginOption)?.groupBy {
|
val optionValuesByPlugin = pluginOptions?.map(::parsePluginOption)?.groupBy {
|
||||||
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
if (it == null) throw CliOptionProcessingException("Wrong plugin option format: $it, should be ${CommonCompilerArguments.PLUGIN_OPTION_FORMAT}")
|
||||||
it.pluginId
|
it.pluginId
|
||||||
} ?: mapOf()
|
} ?: mapOf()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class K2MetadataCompiler : CLICompiler<K2MetadataCompilerArguments>() {
|
|||||||
): ExitCode {
|
): ExitCode {
|
||||||
val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
val collector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||||
|
|
||||||
val pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments, configuration)
|
val pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments.pluginClasspaths, arguments.pluginOptions, configuration)
|
||||||
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
|
||||||
|
|
||||||
for (arg in arguments.freeArgs) {
|
for (arg in arguments.freeArgs) {
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ jvmTarget = "1.6"
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile(project(":compiler:util"))
|
compile(project(":compiler:util"))
|
||||||
compile(project(":compiler:frontend"))
|
compile(project(":compiler:frontend"))
|
||||||
compile(project(":kotlin-scripting-common"))
|
|
||||||
compile(project(":kotlin-scripting-jvm"))
|
|
||||||
compile(projectDist(":kotlin-stdlib"))
|
compile(projectDist(":kotlin-stdlib"))
|
||||||
compileOnly(project(":kotlin-reflect-api"))
|
compileOnly(project(":kotlin-reflect-api"))
|
||||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||||
|
|||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. 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.extensions
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
|
||||||
|
interface CompilerConfigurationExtension {
|
||||||
|
|
||||||
|
companion object : ProjectExtensionDescriptor<CompilerConfigurationExtension>(
|
||||||
|
"org.jetbrains.kotlin.compilerConfigurationExtension",
|
||||||
|
CompilerConfigurationExtension::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
fun updateConfiguration(configuration: CompilerConfiguration)
|
||||||
|
}
|
||||||
@@ -100,20 +100,6 @@ class CompilerApiTest : KotlinIntegrationTestBase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testScriptResolverEnvironmentArgsParsing() {
|
|
||||||
|
|
||||||
fun args(body: K2JVMCompilerArguments.() -> Unit): K2JVMCompilerArguments =
|
|
||||||
K2JVMCompilerArguments().apply(body)
|
|
||||||
|
|
||||||
val longStr = (1..100).joinToString { """\" $it aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \\""" }
|
|
||||||
val unescapeRe = """\\(["\\])""".toRegex()
|
|
||||||
val messageCollector = TestMessageCollector()
|
|
||||||
Assert.assertEquals(hashMapOf("abc" to "def", "11" to "ab cd \\ \"", "long" to unescapeRe.replace(longStr, "\$1")),
|
|
||||||
K2JVMCompiler.createScriptResolverEnvironment(
|
|
||||||
args { scriptResolverEnvironment = arrayOf("abc=def", """11="ab cd \\ \""""", "long=\"$longStr\"") },
|
|
||||||
messageCollector))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun testHelloAppLocal() {
|
fun testHelloAppLocal() {
|
||||||
val messageCollector = TestMessageCollector()
|
val messageCollector = TestMessageCollector()
|
||||||
val jar = tmpdir.absolutePath + File.separator + "hello.jar"
|
val jar = tmpdir.absolutePath + File.separator + "hello.jar"
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ object PathUtil {
|
|||||||
const val KOTLIN_REFLECT_SRC_JAR = "$KOTLIN_JAVA_REFLECT_NAME-sources.jar"
|
const val KOTLIN_REFLECT_SRC_JAR = "$KOTLIN_JAVA_REFLECT_NAME-sources.jar"
|
||||||
|
|
||||||
const val KOTLIN_JAVA_SCRIPT_RUNTIME_JAR = "kotlin-script-runtime.jar"
|
const val KOTLIN_JAVA_SCRIPT_RUNTIME_JAR = "kotlin-script-runtime.jar"
|
||||||
|
const val KOTLIN_SCRIPTING_COMMON_JAR = "kotlin-scripting-common.jar"
|
||||||
|
const val KOTLIN_SCRIPTING_JVM_JAR = "kotlin-scripting-jvm.jar"
|
||||||
|
const val KOTLIN_SCRIPTING_COMPILER_PLUGIN_JAR = "kotlin-scripting-compiler.jar"
|
||||||
|
|
||||||
const val KOTLIN_TEST_NAME = "kotlin-test"
|
const val KOTLIN_TEST_NAME = "kotlin-test"
|
||||||
const val KOTLIN_TEST_JAR = "$KOTLIN_TEST_NAME.jar"
|
const val KOTLIN_TEST_JAR = "$KOTLIN_TEST_NAME.jar"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ dependencies {
|
|||||||
compile(project(":idea:ide-common"))
|
compile(project(":idea:ide-common"))
|
||||||
compile(project(":idea:idea-jps-common"))
|
compile(project(":idea:idea-jps-common"))
|
||||||
compile(project(":plugins:android-extensions-compiler"))
|
compile(project(":plugins:android-extensions-compiler"))
|
||||||
|
compile(project(":kotlin-scripting-idea-plugin"))
|
||||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||||
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) { isTransitive = false }
|
compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-jdk8")) { isTransitive = false }
|
||||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinition
|
|||||||
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||||
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
import org.jetbrains.kotlin.script.ScriptDefinitionProvider
|
||||||
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
|
import org.jetbrains.kotlin.script.ScriptTemplatesProvider
|
||||||
import org.jetbrains.kotlin.script.experimental.KotlinScriptDefinitionAdapterFromNewAPI
|
import org.jetbrains.kotlin.scripting.compiler.plugin.KotlinScriptDefinitionAdapterFromNewAPI
|
||||||
import org.jetbrains.kotlin.utils.PathUtil
|
import org.jetbrains.kotlin.utils.PathUtil
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
import org.jetbrains.kotlin.utils.addToStdlib.flattenTo
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ dependencies {
|
|||||||
runtime project(path: ':kotlin-compiler-runner', configuration: 'runtimeJar')
|
runtime project(path: ':kotlin-compiler-runner', configuration: 'runtimeJar')
|
||||||
runtime project(':kotlin-reflect')
|
runtime project(':kotlin-reflect')
|
||||||
runtime project(':kotlin-scripting-common')
|
runtime project(':kotlin-scripting-common')
|
||||||
|
runtime project(':kotlin-scripting-compiler-plugin')
|
||||||
runtime project(':kotlin-scripting-jvm')
|
runtime project(':kotlin-scripting-jvm')
|
||||||
|
|
||||||
// com.android.tools.build:gradle has ~50 unneeded transitive dependencies
|
// com.android.tools.build:gradle has ~50 unneeded transitive dependencies
|
||||||
|
|||||||
@@ -40,6 +40,11 @@
|
|||||||
<artifactId>kotlin-compiler</artifactId>
|
<artifactId>kotlin-compiler</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains.kotlin</groupId>
|
||||||
|
<artifactId>kotlin-scripting-compiler-plugin</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.groovy</groupId>
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
<artifactId>groovy-all</artifactId>
|
<artifactId>groovy-all</artifactId>
|
||||||
|
|||||||
+3
-3
@@ -49,6 +49,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
|
|||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.psi.KtScript;
|
import org.jetbrains.kotlin.psi.KtScript;
|
||||||
import org.jetbrains.kotlin.script.ReflectionUtilKt;
|
import org.jetbrains.kotlin.script.ReflectionUtilKt;
|
||||||
|
import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationExtensionKt;
|
||||||
import org.jetbrains.kotlin.utils.PathUtil;
|
import org.jetbrains.kotlin.utils.PathUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -183,9 +184,8 @@ public class ExecuteKotlinScriptMojo extends AbstractMojo {
|
|||||||
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(scriptFile.getAbsolutePath()));
|
configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(scriptFile.getAbsolutePath()));
|
||||||
configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME);
|
configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME);
|
||||||
|
|
||||||
K2JVMCompiler.Companion.configureScriptDefinitions(
|
ScriptingCompilerConfigurationExtensionKt.configureScriptDefinitions(
|
||||||
scriptTemplates.toArray(new String[scriptTemplates.size()]),
|
scriptTemplates, configuration, messageCollector, new HashMap<>()
|
||||||
configuration, messageCollector, new HashMap<>()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
description = "Kotlin Scripting Compiler Plugin"
|
||||||
|
|
||||||
|
apply { plugin("kotlin") }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(project(":compiler:frontend"))
|
||||||
|
compileOnly(project(":compiler:frontend.java"))
|
||||||
|
compileOnly(project(":compiler:plugin-api"))
|
||||||
|
compileOnly(project(":compiler:cli"))
|
||||||
|
compile(project(":kotlin-scripting-common"))
|
||||||
|
compile(project(":kotlin-scripting-jvm"))
|
||||||
|
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||||
|
|
||||||
|
testCompile(project(":compiler:frontend"))
|
||||||
|
testCompile(project(":compiler:frontend.script"))
|
||||||
|
testCompile(project(":compiler:plugin-api"))
|
||||||
|
testCompile(project(":compiler:util"))
|
||||||
|
testCompile(project(":compiler:cli"))
|
||||||
|
testCompile(project(":compiler:cli-common"))
|
||||||
|
testCompile(project(":compiler:frontend.java"))
|
||||||
|
testCompile(projectTests(":compiler:tests-common"))
|
||||||
|
testCompile(commonDep("junit:junit"))
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
"main" { projectDefault() }
|
||||||
|
"test" { projectDefault() }
|
||||||
|
}
|
||||||
|
|
||||||
|
val jar = runtimeJar {
|
||||||
|
from(fileTree("$projectDir/src")) { include("META-INF/**") }
|
||||||
|
}
|
||||||
|
sourcesJar()
|
||||||
|
javadocJar()
|
||||||
|
|
||||||
|
publish()
|
||||||
|
|
||||||
|
dist()
|
||||||
|
|
||||||
|
ideaPlugin()
|
||||||
|
|
||||||
|
projectTest {
|
||||||
|
workingDir = rootDir
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCommandLineProcessor
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar
|
||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
* that can be found in the license/LICENSE.txt file.
|
* that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.script.experimental
|
package org.jetbrains.kotlin.scripting.compiler.plugin
|
||||||
|
|
||||||
import com.intellij.openapi.fileTypes.LanguageFileType
|
import com.intellij.openapi.fileTypes.LanguageFileType
|
||||||
import kotlinx.coroutines.experimental.runBlocking
|
import kotlinx.coroutines.experimental.runBlocking
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. 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
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CliOption
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||||
|
|
||||||
|
object ScriptingConfigurationKeys {
|
||||||
|
val SCRIPT_DEFINITIONS: CompilerConfigurationKey<List<String>> =
|
||||||
|
CompilerConfigurationKey.create("Script definition classes")
|
||||||
|
|
||||||
|
val SCRIPT_DEFINITIONS_CLASSPATH: CompilerConfigurationKey<List<File>> =
|
||||||
|
CompilerConfigurationKey.create("Additional classpath for the script definitions")
|
||||||
|
|
||||||
|
val DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION: CompilerConfigurationKey<Boolean> =
|
||||||
|
CompilerConfigurationKey.create("Do not extract script definitions from the compilation classpath")
|
||||||
|
|
||||||
|
val LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION: CompilerConfigurationKey<MutableMap<String, Any?>> =
|
||||||
|
CompilerConfigurationKey.create("Script resolver environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptingCommandLineProcessor : CommandLineProcessor {
|
||||||
|
companion object {
|
||||||
|
val SCRIPT_DEFINITIONS_OPTION = CliOption(
|
||||||
|
"script-definitions", "<fully qualified class name[,]>", "Script definition classes",
|
||||||
|
required = false, allowMultipleOccurrences = true
|
||||||
|
)
|
||||||
|
val SCRIPT_DEFINITIONS_CLASSPATH_OPTION = CliOption(
|
||||||
|
"script-definitions-classpath", "<classpath entry[:]>", "Additional classpath for the script definitions",
|
||||||
|
required = false, allowMultipleOccurrences = true
|
||||||
|
)
|
||||||
|
val DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION = CliOption(
|
||||||
|
"disable-script-definitions-from-classpath", "true/false", "Do not extract script definitions from the compilation classpath",
|
||||||
|
required = false, allowMultipleOccurrences = false
|
||||||
|
)
|
||||||
|
val LEGACY_SCRIPT_TEMPLATES_OPTION = CliOption(
|
||||||
|
"script-templates", "<fully qualified class name[,]>", "Script definition template classes",
|
||||||
|
required = false, allowMultipleOccurrences = true
|
||||||
|
)
|
||||||
|
val LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION = CliOption(
|
||||||
|
"script-resolver-environment", "<key=value[,]>",
|
||||||
|
"Script resolver environment in key-value pairs (the value could be quoted and escaped)",
|
||||||
|
required = false, allowMultipleOccurrences = true
|
||||||
|
)
|
||||||
|
|
||||||
|
val PLUGIN_ID = "kotlin.scripting"
|
||||||
|
}
|
||||||
|
|
||||||
|
override val pluginId = PLUGIN_ID
|
||||||
|
override val pluginOptions =
|
||||||
|
listOf(
|
||||||
|
SCRIPT_DEFINITIONS_OPTION,
|
||||||
|
SCRIPT_DEFINITIONS_CLASSPATH_OPTION,
|
||||||
|
DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION,
|
||||||
|
LEGACY_SCRIPT_TEMPLATES_OPTION,
|
||||||
|
LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) = when (option) {
|
||||||
|
SCRIPT_DEFINITIONS_OPTION, LEGACY_SCRIPT_TEMPLATES_OPTION -> {
|
||||||
|
val currentDefs = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS).toMutableList()
|
||||||
|
currentDefs.addAll(value.split(','))
|
||||||
|
configuration.put(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS, currentDefs)
|
||||||
|
}
|
||||||
|
SCRIPT_DEFINITIONS_CLASSPATH_OPTION -> {
|
||||||
|
val currentCP = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSPATH).toMutableList()
|
||||||
|
currentCP.addAll(value.split(File.pathSeparatorChar).map(::File))
|
||||||
|
configuration.put(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS_CLASSPATH, currentCP)
|
||||||
|
}
|
||||||
|
DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION -> {
|
||||||
|
configuration.put(
|
||||||
|
ScriptingConfigurationKeys.DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION,
|
||||||
|
value.takeUnless { it.isBlank() }?.toBoolean() ?: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION -> {
|
||||||
|
val currentEnv = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION).toMutableMap()
|
||||||
|
// parses key/value pairs in the form <key>=<value>, where
|
||||||
|
// <key> - is a single word (\w+ pattern)
|
||||||
|
// <value> - optionally quoted string with allowed escaped chars (only double-quote and backslash chars are supported)
|
||||||
|
// TODO: implement generic unescaping
|
||||||
|
val envParseRe = """(\w+)=(?:"([^"\\]*(\\.[^"\\]*)*)"|([^\s]*))""".toRegex()
|
||||||
|
val unescapeRe = """\\(["\\])""".toRegex()
|
||||||
|
for (envParam in value.split(',')) {
|
||||||
|
val match = envParseRe.matchEntire(envParam)
|
||||||
|
if (match == null || match.groupValues.size < 4 || match.groupValues[1].isBlank()) {
|
||||||
|
throw CliOptionProcessingException("Unable to parse script-resolver-environment argument $envParam")
|
||||||
|
}
|
||||||
|
currentEnv[match.groupValues[1]] =
|
||||||
|
match.groupValues.drop(2).firstOrNull { it.isNotEmpty() }?.let { unescapeRe.replace(it, "\$1") }
|
||||||
|
}
|
||||||
|
configuration.put(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION, currentEnv)
|
||||||
|
}
|
||||||
|
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. 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
|
||||||
|
|
||||||
|
import com.intellij.mock.MockProject
|
||||||
|
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||||
|
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||||
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
|
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
|
import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension
|
||||||
|
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass
|
||||||
|
|
||||||
|
class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension {
|
||||||
|
|
||||||
|
override fun updateConfiguration(configuration: CompilerConfiguration) {
|
||||||
|
|
||||||
|
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) ?: MessageCollector.NONE
|
||||||
|
val explicitScriptDefinitions = configuration.getList(ScriptingConfigurationKeys.SCRIPT_DEFINITIONS)
|
||||||
|
|
||||||
|
val scriptDefinitions =
|
||||||
|
if (configuration.getBoolean(ScriptingConfigurationKeys.DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION))
|
||||||
|
explicitScriptDefinitions
|
||||||
|
else
|
||||||
|
explicitScriptDefinitions + discoverScriptTemplatesInClasspath(configuration, messageCollector)
|
||||||
|
|
||||||
|
if (scriptDefinitions.isNotEmpty()) {
|
||||||
|
val projectRoot = project.run { basePath ?: baseDir?.canonicalPath }?.let(::File)
|
||||||
|
if (projectRoot != null) {
|
||||||
|
configuration.put(
|
||||||
|
ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION,
|
||||||
|
"projectRoot",
|
||||||
|
projectRoot
|
||||||
|
)
|
||||||
|
}
|
||||||
|
configureScriptDefinitions(
|
||||||
|
scriptDefinitions,
|
||||||
|
configuration,
|
||||||
|
messageCollector,
|
||||||
|
configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar {
|
||||||
|
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
|
||||||
|
CompilerConfigurationExtension.registerExtension(project, ScriptingCompilerConfigurationExtension(project))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun discoverScriptTemplatesInClasspath(configuration: CompilerConfiguration, messageCollector: MessageCollector): Iterable<String> {
|
||||||
|
val templates = arrayListOf<String>()
|
||||||
|
val templatesPath = "META-INF/kotlin/script/templates/"
|
||||||
|
for (dep in configuration.jvmClasspathRoots) {
|
||||||
|
when {
|
||||||
|
dep.isFile -> {
|
||||||
|
// this is the compiler behaviour, so the same logic implemented here
|
||||||
|
if (dep.extension == "jar") {
|
||||||
|
try {
|
||||||
|
with(JarFile(dep)) {
|
||||||
|
for (template in entries()) {
|
||||||
|
if (!template.isDirectory && template.name.startsWith(templatesPath)) {
|
||||||
|
val templateClassName = template.name.removePrefix(templatesPath)
|
||||||
|
templates.add(templateClassName)
|
||||||
|
messageCollector.report(
|
||||||
|
CompilerMessageSeverity.LOGGING,
|
||||||
|
"Configure scripting: Added template $templateClassName from $dep"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
messageCollector.report(
|
||||||
|
CompilerMessageSeverity.WARNING,
|
||||||
|
"Configure scripting: unable to process classpath entry $dep: $e"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dep.isDirectory -> {
|
||||||
|
val dir = File(dep, templatesPath)
|
||||||
|
if (dir.isDirectory) {
|
||||||
|
dir.listFiles().forEach {
|
||||||
|
templates.add(it.name)
|
||||||
|
messageCollector.report(
|
||||||
|
CompilerMessageSeverity.LOGGING,
|
||||||
|
"Configure scripting: Added template ${it.name} from $dep"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> messageCollector.report(CompilerMessageSeverity.WARNING, "Configure scripting: Unknown classpath entry $dep")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return templates
|
||||||
|
}
|
||||||
|
|
||||||
|
fun configureScriptDefinitions(
|
||||||
|
scriptTemplates: List<String>,
|
||||||
|
configuration: CompilerConfiguration,
|
||||||
|
messageCollector: MessageCollector,
|
||||||
|
scriptResolverEnv: Map<String, Any?>
|
||||||
|
) {
|
||||||
|
val classpath = configuration.jvmClasspathRoots
|
||||||
|
// TODO: consider using escaping to allow kotlin escaped names in class names
|
||||||
|
if (scriptTemplates.isNotEmpty()) {
|
||||||
|
val classloader =
|
||||||
|
URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), Thread.currentThread().contextClassLoader)
|
||||||
|
var hasErrors = false
|
||||||
|
for (template in scriptTemplates) {
|
||||||
|
try {
|
||||||
|
val cls = classloader.loadClass(template)
|
||||||
|
val def =
|
||||||
|
if (cls.annotations.firstIsInstanceOrNull<kotlin.script.experimental.annotations.KotlinScript>() != null) {
|
||||||
|
KotlinScriptDefinitionAdapterFromNewAPI(ScriptDefinitionFromAnnotatedBaseClass(cls.kotlin))
|
||||||
|
} else {
|
||||||
|
KotlinScriptDefinitionFromAnnotatedTemplate(cls.kotlin, scriptResolverEnv)
|
||||||
|
}
|
||||||
|
configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, def)
|
||||||
|
messageCollector.report(
|
||||||
|
CompilerMessageSeverity.INFO,
|
||||||
|
"Added script definition $template to configuration: name = ${def.name}, " +
|
||||||
|
"resolver = ${def.dependencyResolver.javaClass.name}"
|
||||||
|
)
|
||||||
|
} catch (ex: ClassNotFoundException) {
|
||||||
|
messageCollector.report(CompilerMessageSeverity.ERROR, "Cannot find script definition template class $template")
|
||||||
|
hasErrors = true
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
messageCollector.report(
|
||||||
|
CompilerMessageSeverity.ERROR,
|
||||||
|
"Error processing script definition template $template: ${ex.message}"
|
||||||
|
)
|
||||||
|
hasErrors = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasErrors) {
|
||||||
|
messageCollector.report(CompilerMessageSeverity.LOGGING, "(Classpath used for templates loading: $classpath)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. 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
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.test.TestCaseWithTmpdir
|
||||||
|
import org.junit.Assert
|
||||||
|
|
||||||
|
class ScriptingCompilerPluginTest : TestCaseWithTmpdir() {
|
||||||
|
fun testScriptResolverEnvironmentArgsParsing() {
|
||||||
|
|
||||||
|
val longStr = (1..100).joinToString { """\" $it aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \\""" }
|
||||||
|
val unescapeRe = """\\(["\\])""".toRegex()
|
||||||
|
val cmdlineProcessor = ScriptingCommandLineProcessor()
|
||||||
|
val configuration = CompilerConfiguration()
|
||||||
|
|
||||||
|
cmdlineProcessor.processOption(
|
||||||
|
ScriptingCommandLineProcessor.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION,
|
||||||
|
"""abc=def,11="ab cd \\ \"",long="$longStr"""",
|
||||||
|
configuration
|
||||||
|
)
|
||||||
|
|
||||||
|
val res = configuration.getMap(ScriptingConfigurationKeys.LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION)
|
||||||
|
|
||||||
|
Assert.assertEquals(
|
||||||
|
hashMapOf("abc" to "def", "11" to "ab cd \\ \"", "long" to unescapeRe.replace(longStr, "\$1")),
|
||||||
|
res
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
description = "Kotlin Scripting IDEA Plugin"
|
||||||
|
|
||||||
|
apply { plugin("kotlin") }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile(project(":kotlin-scripting-compiler-plugin"))
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
"main" { projectDefault() }
|
||||||
|
"test" {}
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeJar()
|
||||||
|
|
||||||
|
ideaPlugin()
|
||||||
|
|
||||||
@@ -153,6 +153,8 @@ include ":kotlin-build-common",
|
|||||||
":kotlin-scripting-common",
|
":kotlin-scripting-common",
|
||||||
":kotlin-scripting-jvm",
|
":kotlin-scripting-jvm",
|
||||||
":kotlin-scripting-jvm-host",
|
":kotlin-scripting-jvm-host",
|
||||||
|
":kotlin-scripting-compiler-plugin",
|
||||||
|
":kotlin-scripting-idea-plugin",
|
||||||
":examples:scripting-jvm-simple-script",
|
":examples:scripting-jvm-simple-script",
|
||||||
":examples:scripting-jvm-maven-deps",
|
":examples:scripting-jvm-maven-deps",
|
||||||
":pill:generate-all-tests",
|
":pill:generate-all-tests",
|
||||||
@@ -251,6 +253,8 @@ project(':kotlin-annotations-android').projectDir = "$rootDir/libraries/tools/ko
|
|||||||
project(':kotlin-scripting-common').projectDir = "$rootDir/libraries/scripting/common" as File
|
project(':kotlin-scripting-common').projectDir = "$rootDir/libraries/scripting/common" as File
|
||||||
project(':kotlin-scripting-jvm').projectDir = "$rootDir/libraries/scripting/jvm" as File
|
project(':kotlin-scripting-jvm').projectDir = "$rootDir/libraries/scripting/jvm" as File
|
||||||
project(':kotlin-scripting-jvm-host').projectDir = "$rootDir/libraries/scripting/jvm-host" as File
|
project(':kotlin-scripting-jvm-host').projectDir = "$rootDir/libraries/scripting/jvm-host" as File
|
||||||
|
project(':kotlin-scripting-compiler-plugin').projectDir = "$rootDir/plugins/scripting/scripting-cli" as File
|
||||||
|
project(':kotlin-scripting-idea-plugin').projectDir = "$rootDir/plugins/scripting/scripting-idea" as File
|
||||||
project(':examples:scripting-jvm-simple-script').projectDir = "$rootDir/libraries/examples/scripting/jvm-simple-script" as File
|
project(':examples:scripting-jvm-simple-script').projectDir = "$rootDir/libraries/examples/scripting/jvm-simple-script" as File
|
||||||
project(':examples:scripting-jvm-maven-deps').projectDir = "$rootDir/libraries/examples/scripting/jvm-maven-deps" as File
|
project(':examples:scripting-jvm-maven-deps').projectDir = "$rootDir/libraries/examples/scripting/jvm-maven-deps" as File
|
||||||
project(':pill:generate-all-tests').projectDir = "$rootDir/plugins/pill/generate-all-tests" as File
|
project(':pill:generate-all-tests').projectDir = "$rootDir/plugins/pill/generate-all-tests" as File
|
||||||
|
|||||||
Reference in New Issue
Block a user