diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java index 867495343b2..cea1d747218 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java @@ -165,7 +165,7 @@ public class K2JSCompiler extends CLICompiler { return COMPILATION_ERROR; } - ExitCode pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments, configuration); + ExitCode pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments.getPluginClasspaths(), arguments.getPluginOptions(), configuration); if (pluginLoadResult != ExitCode.OK) return pluginLoadResult; configuration.put(JSConfigurationKeys.LIBRARIES, configureLibraries(arguments, paths, messageCollector)); diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index 7271db7377d..af61f6d93ff 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt @@ -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.JvmModulePathRoot 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.plugins.PluginCliParser 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.JvmAbi 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.StandardScriptDefinition -import org.jetbrains.kotlin.script.experimental.KotlinScriptDefinitionAdapterFromNewAPI import org.jetbrains.kotlin.util.PerformanceCounter import org.jetbrains.kotlin.utils.KotlinPaths import org.jetbrains.kotlin.utils.PathUtil -import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import java.io.File -import java.io.IOException import java.lang.management.ManagementFactory -import java.net.URLClassLoader -import java.util.* import java.util.concurrent.TimeUnit -import java.util.jar.JarFile -import kotlin.script.experimental.definitions.ScriptDefinitionFromAnnotatedBaseClass class K2JVMCompiler : CLICompiler() { override fun doExecute( @@ -77,7 +68,7 @@ class K2JVMCompiler : CLICompiler() { if (it != OK) return it } - val pluginLoadResult = PluginCliParser.loadPluginsSafe(arguments, configuration) + val pluginLoadResult = loadPlugins(paths, arguments, configuration) if (pluginLoadResult != ExitCode.OK) return pluginLoadResult if (!arguments.script && arguments.buildFile == null) { @@ -153,7 +144,7 @@ class K2JVMCompiler : CLICompiler() { KotlinToJVMBytecodeCompiler.configureSourceRoots(configuration, moduleChunk.modules, buildFile) - val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector) + val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector) ?: return COMPILATION_ERROR registerJavacIfNeeded(environment, arguments).let { @@ -167,7 +158,7 @@ class K2JVMCompiler : CLICompiler() { configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true) - val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector) + val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector) ?: return COMPILATION_ERROR val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(environment.project) @@ -191,7 +182,7 @@ class K2JVMCompiler : CLICompiler() { } } - val environment = createCoreEnvironment(rootDisposable, configuration, arguments, messageCollector) + val environment = createCoreEnvironment(rootDisposable, configuration, messageCollector) ?: return COMPILATION_ERROR registerJavacIfNeeded(environment, arguments).let { @@ -229,6 +220,39 @@ class K2JVMCompiler : CLICompiler() { } } + 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( environment: KotlinCoreEnvironment, arguments: K2JVMCompilerArguments @@ -257,17 +281,8 @@ class K2JVMCompiler : CLICompiler() { private fun createCoreEnvironment( rootDisposable: Disposable, configuration: CompilerConfiguration, - arguments: K2JVMCompilerArguments, messageCollector: MessageCollector ): 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 val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) @@ -278,55 +293,7 @@ class K2JVMCompiler : CLICompiler() { initStartNanos = 0L } - if (!messageCollector.hasErrors()) { - scriptResolverEnv.put("projectRoot", environment.project.run { basePath ?: baseDir?.canonicalPath }?.let(::File)) - return environment - } - - return null - } - - private fun discoverScriptTemplatesInClasspath(configuration: CompilerConfiguration, messageCollector: MessageCollector): Iterable { - val templates = arrayListOf() - 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 + return if (messageCollector.hasErrors()) null else environment } override fun setupPlatformSpecificArgumentsAndServices( @@ -506,73 +473,6 @@ class K2JVMCompiler : CLICompiler() { return OK } - - fun configureScriptDefinitions( - scriptTemplates: List, - configuration: CompilerConfiguration, - messageCollector: MessageCollector, - scriptResolverEnv: HashMap - ) { - 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() != 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? { - val scriptResolverEnv = hashMapOf() - // parses key/value pairs in the form =, where - // - is a single word (\w+ pattern) - // - 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 - } } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 3682847263a..ae40d3dffc5 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -88,6 +88,7 @@ import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.extensions.CompilerConfigurationExtension import org.jetbrains.kotlin.extensions.DeclarationAttributeAltererExtension import org.jetbrains.kotlin.extensions.PreprocessedVirtualFileFactoryExtension import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor @@ -178,6 +179,7 @@ class KotlinCoreEnvironment private constructor( DeclarationAttributeAltererExtension.registerExtensionPoint(project) PreprocessedVirtualFileFactoryExtension.registerExtensionPoint(project) JsSyntheticTranslateExtension.registerExtensionPoint(project) + CompilerConfigurationExtension.registerExtensionPoint(project) for (registrar in configuration.getList(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS)) { try { @@ -195,12 +197,19 @@ class KotlinCoreEnvironment private constructor( val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) registerProjectServices(projectEnvironment, messageCollector) + CompilerConfigurationExtension.getInstances(project).forEach { + it.updateConfiguration(configuration) + } + sourceFiles += CompileEnvironmentUtil.getKtFiles(project, getSourceRootsCheckingForDuplicates(), this.configuration, { message -> report(ERROR, message) }) 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 if (scriptDefinitionProvider != null) { scriptDefinitionProvider.setScriptDefinitions( diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt index a3c3a8450ac..c5d0e380ce8 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/plugins/PluginCliParser.kt @@ -32,11 +32,15 @@ import java.util.* object PluginCliParser { @JvmStatic - fun loadPluginsSafe(arguments: CommonCompilerArguments, configuration: CompilerConfiguration): ExitCode { + fun loadPluginsSafe(pluginClasspaths: Array?, pluginOptions: Array?, configuration: CompilerConfiguration): ExitCode = + loadPluginsSafe(pluginClasspaths?.asIterable(), pluginOptions?.asIterable(), configuration) + + @JvmStatic + fun loadPluginsSafe(pluginClasspaths: Iterable?, pluginOptions: Iterable?, configuration: CompilerConfiguration): ExitCode { val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) try { - PluginCliParser.loadPlugins(arguments, configuration) + PluginCliParser.loadPlugins(pluginClasspaths, pluginOptions, configuration) } catch (e: PluginCliOptionProcessingException) { val message = e.message + "\n\n" + cliPluginUsageString(e.pluginId, e.options) @@ -55,9 +59,9 @@ object PluginCliParser { } @JvmStatic - fun loadPlugins(arguments: CommonCompilerArguments, configuration: CompilerConfiguration) { + fun loadPlugins(pluginClasspaths: Iterable?, pluginOptions: Iterable?, configuration: CompilerConfiguration) { val classLoader = PluginURLClassLoader( - arguments.pluginClasspaths + pluginClasspaths ?.map { File(it).toURI().toURL() } ?.toTypedArray() ?: arrayOf(), @@ -68,15 +72,15 @@ object PluginCliParser { componentRegistrars.addAll(BundledCompilerPlugins.componentRegistrars) configuration.addAll(ComponentRegistrar.PLUGIN_COMPONENT_REGISTRARS, componentRegistrars) - processPluginOptions(arguments, configuration, classLoader) + processPluginOptions(pluginOptions, configuration, classLoader) } private fun processPluginOptions( - arguments: CommonCompilerArguments, + pluginOptions: Iterable?, configuration: CompilerConfiguration, 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}") it.pluginId } ?: mapOf() diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/K2MetadataCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/K2MetadataCompiler.kt index a5c927b1f8c..cc2a9c6a69a 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/K2MetadataCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/metadata/K2MetadataCompiler.kt @@ -54,7 +54,7 @@ class K2MetadataCompiler : CLICompiler() { ): ExitCode { 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 for (arg in arguments.freeArgs) { diff --git a/compiler/frontend.script/build.gradle.kts b/compiler/frontend.script/build.gradle.kts index f3a8ecc328c..e184cc5438a 100644 --- a/compiler/frontend.script/build.gradle.kts +++ b/compiler/frontend.script/build.gradle.kts @@ -10,8 +10,6 @@ jvmTarget = "1.6" dependencies { compile(project(":compiler:util")) compile(project(":compiler:frontend")) - compile(project(":kotlin-scripting-common")) - compile(project(":kotlin-scripting-jvm")) compile(projectDist(":kotlin-stdlib")) compileOnly(project(":kotlin-reflect-api")) compile(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/extensions/CompilerConfigurationExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/extensions/CompilerConfigurationExtension.kt new file mode 100644 index 00000000000..13cbd1a00cc --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/extensions/CompilerConfigurationExtension.kt @@ -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( + "org.jetbrains.kotlin.compilerConfigurationExtension", + CompilerConfigurationExtension::class.java + ) + + fun updateConfiguration(configuration: CompilerConfiguration) +} diff --git a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerApiTest.kt b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerApiTest.kt index ca7395417af..7e8e2f65c68 100644 --- a/compiler/tests/org/jetbrains/kotlin/daemon/CompilerApiTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/daemon/CompilerApiTest.kt @@ -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() { val messageCollector = TestMessageCollector() val jar = tmpdir.absolutePath + File.separator + "hello.jar" diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.kt b/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.kt index e1cf055a176..c3df0c64740 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/PathUtil.kt @@ -57,6 +57,9 @@ object PathUtil { 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_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_JAR = "$KOTLIN_TEST_NAME.jar" diff --git a/idea/idea-core/build.gradle.kts b/idea/idea-core/build.gradle.kts index 929aa71528a..fef24b790e5 100644 --- a/idea/idea-core/build.gradle.kts +++ b/idea/idea-core/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { compile(project(":idea:ide-common")) compile(project(":idea:idea-jps-common")) 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-jdk8")) { isTransitive = false } compileOnly(intellijCoreDep()) { includeJars("intellij-core") } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt index 8b8015a8bed..21609d82575 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate import org.jetbrains.kotlin.script.ScriptDefinitionProvider 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.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.flattenTo diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle b/libraries/tools/kotlin-gradle-plugin/build.gradle index f9b755bc801..4d9bbb28ea5 100644 --- a/libraries/tools/kotlin-gradle-plugin/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin/build.gradle @@ -58,6 +58,7 @@ dependencies { runtime project(path: ':kotlin-compiler-runner', configuration: 'runtimeJar') runtime project(':kotlin-reflect') runtime project(':kotlin-scripting-common') + runtime project(':kotlin-scripting-compiler-plugin') runtime project(':kotlin-scripting-jvm') // com.android.tools.build:gradle has ~50 unneeded transitive dependencies diff --git a/libraries/tools/kotlin-maven-plugin/pom.xml b/libraries/tools/kotlin-maven-plugin/pom.xml index 4a357fdf531..216f3e39d1c 100644 --- a/libraries/tools/kotlin-maven-plugin/pom.xml +++ b/libraries/tools/kotlin-maven-plugin/pom.xml @@ -40,6 +40,11 @@ kotlin-compiler ${project.version} + + org.jetbrains.kotlin + kotlin-scripting-compiler-plugin + ${project.version} + org.codehaus.groovy groovy-all diff --git a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/ExecuteKotlinScriptMojo.java b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/ExecuteKotlinScriptMojo.java index f8d9cde5862..37dcb0daac8 100644 --- a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/ExecuteKotlinScriptMojo.java +++ b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/ExecuteKotlinScriptMojo.java @@ -49,6 +49,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtScript; import org.jetbrains.kotlin.script.ReflectionUtilKt; +import org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationExtensionKt; import org.jetbrains.kotlin.utils.PathUtil; import java.io.File; @@ -183,9 +184,8 @@ public class ExecuteKotlinScriptMojo extends AbstractMojo { configuration.add(JVMConfigurationKeys.CONTENT_ROOTS, new KotlinSourceRoot(scriptFile.getAbsolutePath())); configuration.put(CommonConfigurationKeys.MODULE_NAME, JvmAbi.DEFAULT_MODULE_NAME); - K2JVMCompiler.Companion.configureScriptDefinitions( - scriptTemplates.toArray(new String[scriptTemplates.size()]), - configuration, messageCollector, new HashMap<>() + ScriptingCompilerConfigurationExtensionKt.configureScriptDefinitions( + scriptTemplates, configuration, messageCollector, new HashMap<>() ); KotlinCoreEnvironment environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES); diff --git a/plugins/scripting/scripting-cli/build.gradle.kts b/plugins/scripting/scripting-cli/build.gradle.kts new file mode 100644 index 00000000000..df94187cdd1 --- /dev/null +++ b/plugins/scripting/scripting-cli/build.gradle.kts @@ -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 +} diff --git a/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor b/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor new file mode 100644 index 00000000000..327b0ae437d --- /dev/null +++ b/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor @@ -0,0 +1 @@ +org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCommandLineProcessor \ No newline at end of file diff --git a/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar b/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar new file mode 100644 index 00000000000..5daff819baa --- /dev/null +++ b/plugins/scripting/scripting-cli/src/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar @@ -0,0 +1 @@ +org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar diff --git a/compiler/frontend.script/src/org/jetbrains/kotlin/script/experimental/scriptDefinitions.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt similarity index 97% rename from compiler/frontend.script/src/org/jetbrains/kotlin/script/experimental/scriptDefinitions.kt rename to plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt index a373224c76d..446466ad6d0 100644 --- a/compiler/frontend.script/src/org/jetbrains/kotlin/script/experimental/scriptDefinitions.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/KotlinScriptDefinitionAdapterFromNewAPI.kt @@ -3,7 +3,7 @@ * 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 kotlinx.coroutines.experimental.runBlocking diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCommandLineProcessor.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCommandLineProcessor.kt new file mode 100644 index 00000000000..eb6941c6351 --- /dev/null +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCommandLineProcessor.kt @@ -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> = + CompilerConfigurationKey.create("Script definition classes") + + val SCRIPT_DEFINITIONS_CLASSPATH: CompilerConfigurationKey> = + CompilerConfigurationKey.create("Additional classpath for the script definitions") + + val DISABLE_SCRIPT_DEFINITIONS_FROM_CLSSPATH_OPTION: CompilerConfigurationKey = + CompilerConfigurationKey.create("Do not extract script definitions from the compilation classpath") + + val LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION: CompilerConfigurationKey> = + CompilerConfigurationKey.create("Script resolver environment") +} + +class ScriptingCommandLineProcessor : CommandLineProcessor { + companion object { + val SCRIPT_DEFINITIONS_OPTION = CliOption( + "script-definitions", "", "Script definition classes", + required = false, allowMultipleOccurrences = true + ) + val SCRIPT_DEFINITIONS_CLASSPATH_OPTION = CliOption( + "script-definitions-classpath", "", "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", "", "Script definition template classes", + required = false, allowMultipleOccurrences = true + ) + val LEGACY_SCRIPT_RESOLVER_ENVIRONMENT_OPTION = CliOption( + "script-resolver-environment", "", + "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 =, where + // - is a single word (\w+ pattern) + // - 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}") + } +} diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt new file mode 100644 index 00000000000..5dfcb4b7786 --- /dev/null +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt @@ -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 { + val templates = arrayListOf() + 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, + configuration: CompilerConfiguration, + messageCollector: MessageCollector, + scriptResolverEnv: Map +) { + 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() != 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 + } + } +} diff --git a/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt b/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt new file mode 100644 index 00000000000..0a53d3e7621 --- /dev/null +++ b/plugins/scripting/scripting-cli/tests/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerPluginTest.kt @@ -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 + ) + } +} diff --git a/plugins/scripting/scripting-idea/build.gradle.kts b/plugins/scripting/scripting-idea/build.gradle.kts new file mode 100644 index 00000000000..c965b9c539f --- /dev/null +++ b/plugins/scripting/scripting-idea/build.gradle.kts @@ -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() + diff --git a/settings.gradle b/settings.gradle index 0daf118ca56..307c63b3dcd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -153,6 +153,8 @@ include ":kotlin-build-common", ":kotlin-scripting-common", ":kotlin-scripting-jvm", ":kotlin-scripting-jvm-host", + ":kotlin-scripting-compiler-plugin", + ":kotlin-scripting-idea-plugin", ":examples:scripting-jvm-simple-script", ":examples:scripting-jvm-maven-deps", ":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-jvm').projectDir = "$rootDir/libraries/scripting/jvm" 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-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