Remove support for xml-based script configs, other minor refactorings

This commit is contained in:
Ilya Chernikov
2016-08-31 16:55:58 +02:00
parent 0f31695a0b
commit 63c5133167
11 changed files with 28 additions and 350 deletions
@@ -98,9 +98,6 @@ public class K2JVMCompilerArguments extends CommonCompilerArguments {
@ValueDescription("<path>")
public String declarationsOutputPath;
@Argument(value = "Xload-script-configs", description = "Load script configuration files from project directory tree")
public boolean loadScriptConfigs;
@Argument(value = "Xsingle-module", description = "Combine modules for source files and binary dependencies into a single module")
public boolean singleModule;
@@ -93,7 +93,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
)
return COMPILATION_ERROR
}
configuration.addKotlinSourceRoot(arguments.freeArgs.get(0))
configuration.addKotlinSourceRoot(arguments.freeArgs[0])
}
else if (arguments.module == null) {
for (arg in arguments.freeArgs) {
@@ -376,7 +376,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
configuration.put(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, arguments.inheritMultifileParts)
configuration.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
configuration.put(CLIConfigurationKeys.REPORT_PERF, arguments.reportPerf)
configuration.put(JVMConfigurationKeys.LOAD_SCRIPT_CONFIGS, arguments.loadScriptConfigs)
configuration.put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
arguments.declarationsOutputPath?.let { configuration.put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) }
@@ -93,9 +93,9 @@ import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisCompletedHandlerExten
import org.jetbrains.kotlin.resolve.jvm.extensions.PackageFragmentProviderExtension
import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFactoryService
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.utils.ifEmpty
import java.io.File
import java.lang.IllegalStateException
import java.util.*
@@ -108,7 +108,7 @@ class KotlinCoreEnvironment private constructor(
private val projectEnvironment: JavaCoreProjectEnvironment = object : KotlinCoreProjectEnvironment(parentDisposable, applicationEnvironment) {
override fun preregisterServices() {
registerProjectExtensionPoints(Extensions.getArea(getProject()))
registerProjectExtensionPoints(Extensions.getArea(project))
}
}
private val sourceFiles = ArrayList<KtFile>()
@@ -132,24 +132,11 @@ class KotlinCoreEnvironment private constructor(
message ->
report(ERROR, message)
}))
sourceFiles.sortedWith(object : Comparator<KtFile> {
override fun compare(o1: KtFile, o2: KtFile): Int {
return o1.virtualFile.path.compareTo(o2.virtualFile.path, ignoreCase = true)
}
})
sourceFiles.sortedWith(Comparator<KtFile> { o1, o2 -> o1.virtualFile.path.compareTo(o2.virtualFile.path, ignoreCase = true) })
KotlinScriptDefinitionProvider.getInstance(project).let { scriptDefinitionProvider ->
scriptDefinitionProvider.setScriptDefinitions(
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS)
.ifEmpty {
if (configuration.get(JVMConfigurationKeys.LOAD_SCRIPT_CONFIGS) ?: false)
loadScriptConfigsFromProjectRoot(File(project.basePath ?: ".")).let { configs ->
val kotlinEnvVars = generateKotlinScriptClasspathEnvVars(project)
configs.map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) }
}
else null
?: emptyList()
})
configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS))
KotlinScriptExternalImportsProvider.getInstance(project)?.run {
configuration.addJvmClasspathRoots(
@@ -275,7 +262,7 @@ class KotlinCoreEnvironment private constructor(
private fun findJarRoot(root: JvmClasspathRoot): VirtualFile? {
val path = root.file
val jarFile = applicationEnvironment.jarFileSystem.findFileByPath("${path}${URLUtil.JAR_SEPARATOR}")
val jarFile = applicationEnvironment.jarFileSystem.findFileByPath("$path${URLUtil.JAR_SEPARATOR}")
if (jarFile == null) {
report(WARNING, "Classpath entry points to a file that is not a JAR archive: $path")
return null
@@ -320,12 +307,10 @@ class KotlinCoreEnvironment private constructor(
if (!(System.getProperty(KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY).toBooleanLenient() ?: false)) {
// JPS may run many instances of the compiler in parallel (there's an option for compiling independent modules in parallel in IntelliJ)
// All projects share the same ApplicationEnvironment, and when the last project is disposed, the ApplicationEnvironment is disposed as well
Disposer.register(parentDisposable, object : Disposable {
override fun dispose() {
synchronized (APPLICATION_LOCK) {
if (--ourProjectCount <= 0) {
disposeApplicationEnvironment()
}
Disposer.register(parentDisposable, Disposable {
synchronized (APPLICATION_LOCK) {
if (--ourProjectCount <= 0) {
disposeApplicationEnvironment()
}
}
})
@@ -357,11 +342,9 @@ class KotlinCoreEnvironment private constructor(
val parentDisposable = Disposer.newDisposable()
ourApplicationEnvironment = createApplicationEnvironment(parentDisposable, configuration, configFilePaths)
ourProjectCount = 0
Disposer.register(parentDisposable, object : Disposable {
override fun dispose() {
synchronized (APPLICATION_LOCK) {
ourApplicationEnvironment = null
}
Disposer.register(parentDisposable, Disposable {
synchronized (APPLICATION_LOCK) {
ourApplicationEnvironment = null
}
})
return ourApplicationEnvironment!!
@@ -80,9 +80,6 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<List<Module>> MODULES =
CompilerConfigurationKey.create("module data");
public static final CompilerConfigurationKey<Boolean> LOAD_SCRIPT_CONFIGS =
CompilerConfigurationKey.create("Load script configuration files from project directory tree");
public static final CompilerConfigurationKey<List<String>> FRIEND_PATHS =
CompilerConfigurationKey.create("friend module paths");
}
@@ -1,118 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.script
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parsing.KotlinParserDefinition
import org.jetbrains.kotlin.psi.KtScript
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.KotlinPathsFromHomeDir
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.util.concurrent.Future
import kotlin.reflect.KClass
import kotlin.script.StandardScriptTemplate
data class KotlinConfigurableScriptDefinition(val config: KotlinScriptConfig, val environmentVars: Map<String, List<String>>?) : KotlinScriptDefinition {
override val name = config.name
// This is a temporary workaround: this class will be removed later
override val template: KClass<out Any>
get() = StandardScriptTemplate::class
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter> =
config.parameters.map { ScriptParameter(Name.identifier(it.name), getKotlinTypeByFqName(scriptDescriptor, it.type)) }
override fun getScriptSupertypes(scriptDescriptor: ScriptDescriptor): List<KotlinType> =
config.supertypes.map { getKotlinTypeByFqName(scriptDescriptor, it) }
override fun getScriptParametersToPassToSuperclass(scriptDescriptor: ScriptDescriptor): List<Name> =
config.superclassParamsMapping.map { Name.identifier(it) }
override fun <TF> isScript(file: TF): Boolean =
Regex(config.fileNameMatch).matches(getFileName(file))
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, KotlinParserDefinition.STD_SCRIPT_EXT)
private val evaluatedClasspath by lazy { config.classpath.evalWithVars(environmentVars).map { File(it) }.distinctBy { it.canonicalPath } }
override fun <TF> getDependenciesFor(file: TF, project: Project, previousDependencies: KotlinScriptExternalDependencies?): KotlinScriptExternalDependencies? =
if (!isScript(file)) null
else {
val extDeps = getScriptDependenciesFromConfig(file)
when {
extDeps != null ->
object : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> = evaluatedClasspath + extDeps.classpath
override val imports = extDeps.imports
override val sources: Iterable<File> = extDeps.sources
}
!evaluatedClasspath.isEmpty() ->
object : KotlinScriptExternalDependencies {
override val classpath: Iterable<File> = evaluatedClasspath
}
else -> null
}
}
}
// return all combination of replacements of vars in the strings
// if corresponding list of replacements is empty, all strings containing the reference to the var are removed
// TODO: fix and tests
// TODO: move to some utils
internal fun Iterable<String>.evalWithVars(varsMap: Map<String, List<String>>?): Iterable<String> =
if (varsMap == null || varsMap.isEmpty()) this
else this.flatMap { cpentry ->
varsMap.entries.fold(listOf(cpentry)) { p, v ->
if (cpentry.contains("\${${v.key}}")) {
if (v.value.isEmpty()) emptyList()
else v.value.flatMap { valListElement ->
p.map { it.replace("\${${v.key}}", valListElement) }
}
}
else p
}
}
fun generateKotlinScriptClasspathEnvVars(project: Project, kotlinHomeDir: File? = null): Map<String, List<String>> =
generateKotlinScriptClasspathEnvVarsFromPaths(
project,
kotlinHomeDir?.let { KotlinPathsFromHomeDir(it) } ?: tryFindKotlinPathsForScriptClasspathEnvVars(project))
private fun tryFindKotlinPathsForScriptClasspathEnvVars(project: Project): KotlinPaths? =
sequenceOf( { PathUtil.getKotlinPathsForIdeaPlugin() },
{ PathUtil.getKotlinPathsForJpsPlugin() },
{ PathUtil.getKotlinPathsForCompiler() },
{ KotlinPathsFromHomeDir(File(project.basePath ?: ".")) },
// note: these are only usable when debugging kotlin project
// TODO: replace with more reliable mechanism
{ KotlinPathsFromHomeDir(File(PathUtil.getPathUtilJar().parentFile.parentFile.parentFile, "dist/kotlinc")) })
.map { it() }
.firstOrNull { it.runtimePath.exists() }
fun generateKotlinScriptClasspathEnvVarsFromPaths(project: Project, paths: KotlinPaths?): Map<String, List<String>> =
mapOf("kotlin-runtime" to (paths?.run { listOf(runtimePath.canonicalPath) } ?: emptyList()),
"kotlin-reflect" to (paths?.run { listOf(reflectPath.canonicalPath) } ?: emptyList()),
"project-root" to listOf(project.basePath ?: "."),
"jdk" to PathUtil.getJdkClassesRoots().map { it.canonicalPath })
.filterNot { it.value.isEmpty() }
@@ -1,103 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.script
import com.intellij.openapi.util.JDOMUtil
import com.intellij.util.xmlb.XmlSerializer
import com.intellij.util.xmlb.annotations.AbstractCollection
import com.intellij.util.xmlb.annotations.Tag
import org.jdom.Document
import org.jdom.Element
import org.jdom.output.Format
import org.jdom.output.XMLOutputter
import java.io.File
import java.io.InputStream
import java.io.StringWriter
import java.util.*
val SCRIPT_CONFIG_FILE_EXTENSION = ".ktscfg.xml"
fun isScriptDefinitionConfigFile(file: java.io.File) = file.isFile && file.name.endsWith(org.jetbrains.kotlin.script.SCRIPT_CONFIG_FILE_EXTENSION)
fun isScriptDefinitionConfigFile(file: com.intellij.openapi.vfs.VirtualFile) = !file.isDirectory && file.name.endsWith(org.jetbrains.kotlin.script.SCRIPT_CONFIG_FILE_EXTENSION)
fun loadScriptConfigsFromProjectRoot(projectRoot: java.io.File): List<org.jetbrains.kotlin.script.KotlinScriptConfig> =
projectRoot.listFiles { it -> isScriptDefinitionConfigFile(it) }.toList()
.flatMap { loadScriptConfigs(it) }
fun loadScriptConfigs(configFile: File): List<KotlinScriptConfig> =
JDOMUtil.loadDocument(configFile).rootElement.children.mapNotNull {
XmlSerializer.deserialize(it, KotlinScriptConfig::class.java)
}
@Suppress("unused") // Used externally
fun loadScriptConfigs(configStream: InputStream): List<KotlinScriptConfig> =
JDOMUtil.loadDocument(configStream).rootElement.children.mapNotNull {
XmlSerializer.deserialize(it, KotlinScriptConfig::class.java)
}
@Suppress("unused")
fun generateSampleScriptConfig(): String {
val doc = Document(Element("KotlinScriptDefinitions"))
val element = XmlSerializer.serialize(
KotlinScriptConfig(name = "abc", fileNameMatch = ".*\\.kts", classpath = arrayListOf("aaa", "bbb"),
parameters = arrayListOf(KotlinScriptParameterConfig("p1", "t1"))))
doc.rootElement.addContent(element)
val sw = StringWriter()
with (XMLOutputter()) {
format = Format.getPrettyFormat()
output(doc, sw)
}
return sw.toString()
}
@Tag("scriptParam")
data class KotlinScriptParameterConfig(
@Tag("name") var name: String = "",
@Tag("type") var type: String = ""
)
@Tag("script")
data class KotlinScriptConfig(
@Tag("name")
var name: String = "KotlinScript",
@Tag("files")
var fileNameMatch: String = ".*\\.kts",
@Tag("template")
var template: String = "kotlin.script.StandardScriptTemplate",
@Tag("classpath")
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
var classpath: MutableList<String> = ArrayList(),
@Tag("parameters")
@AbstractCollection(surroundWithTag = false, elementValueAttribute = "")
var parameters: MutableList<KotlinScriptParameterConfig> = ArrayList(),
@Tag("supertypes")
@AbstractCollection(surroundWithTag = false, elementTag = "type", elementValueAttribute = "")
var supertypes: MutableList<String> = ArrayList(),
@Tag("superclassParameters")
@AbstractCollection(surroundWithTag = false, elementTag = "name", elementValueAttribute = "")
var superclassParamsMapping: MutableList<String> = ArrayList()
)
@@ -1,69 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.script
import com.intellij.openapi.util.JDOMUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.util.xmlb.XmlSerializer
import com.intellij.util.xmlb.annotations.AbstractCollection
import com.intellij.util.xmlb.annotations.Tag
import java.io.File
import java.io.InputStream
import java.util.*
@Tag("import")
class KotlinScriptExternalDependenciesConfig : KotlinScriptExternalDependencies {
@Tag("classpath")
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
override var classpath: Iterable<File> = ArrayList()
@Tag("imports")
@AbstractCollection(surroundWithTag = false, elementTag = "name", elementValueAttribute = "")
override var imports: MutableList<String> = ArrayList()
@Tag("sources")
@AbstractCollection(surroundWithTag = false, elementTag = "path", elementValueAttribute = "")
override var sources: Iterable<File> = ArrayList()
}
fun loadScriptExternalImportConfigs(configFile: File): List<KotlinScriptExternalDependenciesConfig> =
JDOMUtil.loadDocument(configFile).rootElement.children.mapNotNull {
XmlSerializer.deserialize(it, KotlinScriptExternalDependenciesConfig::class.java)
}
fun loadScriptExternalImportConfigs(configStream: InputStream): List<KotlinScriptExternalDependenciesConfig> =
JDOMUtil.loadDocument(configStream).rootElement.children.mapNotNull {
XmlSerializer.deserialize(it, KotlinScriptExternalDependenciesConfig::class.java)
}
fun <TF> getScriptDependenciesFromConfig(file: TF): KotlinScriptExternalDependencies? {
val IMPORTS_FILE_EXTENSION = ".ktsimports.xml"
fun streamFromSibling(file: VirtualFile): InputStream? =
file.parent.findFileByRelativePath(file.name + IMPORTS_FILE_EXTENSION)?.let { it.inputStream }
fun streamFromSibling(file: File): InputStream? {
val sibling = File(file.parentFile, file.name + IMPORTS_FILE_EXTENSION)
return if (sibling.exists()) sibling.inputStream()
else null
}
return when (file) {
is VirtualFile -> streamFromSibling(file)
is PsiFile -> streamFromSibling(file.originalFile.virtualFile)
is File -> streamFromSibling(file)
else -> throw IllegalArgumentException("Unsupported file type $file")
}?.let { KotlinScriptExternalDependenciesUnion(loadScriptExternalImportConfigs(it)) }
}
-1
View File
@@ -8,7 +8,6 @@ where advanced options include:
-Xallow-kotlin-package Allow compiling code in package 'kotlin'
-Xskip-metadata-version-check Try loading binary incompatible classes, may cause crashes
-Xdump-declarations-to <path> Path to JSON file to dump Java to Kotlin declaration mappings
-Xload-script-configs Load script configuration files from project directory tree
-Xsingle-module Combine modules for source files and binary dependencies into a single module
-Xinterface-compatibility Generate DefaultImpls classes for interfaces in JVM target bytecode version 1.8 for binary compatibility with 1.6
-Xno-inline Disable method inlining
@@ -45,7 +45,7 @@ import kotlin.script.StandardScriptTemplate
// TODO: the contetnts of this file should go into ScriptTest.kt and replace appropriate xml-based functionality,
// as soon as the the latter is removed from the codebase
class ScriptTest2 {
class ScriptTemplateTest {
@Test
fun testScriptWithParam() {
val aClass = compileScript("fib.kts", ScriptWithIntParam::class, null)
@@ -35,8 +35,10 @@ import com.intellij.util.io.URLUtil
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.script.*
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.KotlinScriptExternalImportsProvider
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.script.makeScriptDefsFromTemplatesProviderExtensions
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
@@ -49,10 +51,6 @@ class KotlinScriptConfigurationManager(
private val scriptExternalImportsProvider: KotlinScriptExternalImportsProvider
) {
private val kotlinEnvVars: Map<String, List<String>> by lazy {
generateKotlinScriptClasspathEnvVarsFromPaths(project, PathUtil.getKotlinPathsForIdeaPlugin())
}
init {
reloadScriptDefinitions()
@@ -102,8 +100,7 @@ class KotlinScriptConfigurationManager(
fun getAllLibrarySourcesScope() = NonClasspathDirectoriesScope(getAllLibrarySources())
private fun reloadScriptDefinitions() {
(makeScriptDefsFromTemplatesProviderExtensions(project, { ep, ex -> log.warn("[kts] Error loading definition from ${ep.id}", ex) }) +
loadScriptConfigsFromProjectRoot(File(project.basePath ?: "")).map { KotlinConfigurableScriptDefinition(it, kotlinEnvVars) }).let {
makeScriptDefsFromTemplatesProviderExtensions(project, { ep, ex -> log.warn("[kts] Error loading definition from ${ep.id}", ex) }).let {
if (it.isNotEmpty()) {
scriptDefinitionProvider.setScriptDefinitions(it + StandardScriptDefinition)
}
@@ -21,7 +21,6 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.ResolveScopeProvider
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.idea.core.script.KotlinScriptConfigurationManager
import org.jetbrains.kotlin.script.KotlinConfigurableScriptDefinition
import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromTemplate
import org.jetbrains.kotlin.script.StandardScriptDefinition
import org.jetbrains.kotlin.script.getScriptDefinition
@@ -29,18 +28,15 @@ import kotlin.script.StandardScriptTemplate
class KotlinScriptResolveScopeProvider : ResolveScopeProvider() {
override fun getResolveScope(file: VirtualFile, project: Project): GlobalSearchScope? {
val scriptDefinition = getScriptDefinition(file, project) ?: return null
// This is a workaround for completion in scripts and REPL to provide module dependencies
if (scriptDefinition == StandardScriptDefinition || scriptDefinition.template == Any::class) {
return null
}
val scriptDefinition = getScriptDefinition(file, project)
// TODO: this should get this particular scripts dependencies
if (scriptDefinition is KotlinConfigurableScriptDefinition || scriptDefinition is KotlinScriptDefinitionFromTemplate) {
// TODO: should include the file itself
return KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspathScope()
return when {
scriptDefinition == null -> null
// This is a workaround for completion in scripts and REPL to provide module dependencies
scriptDefinition == StandardScriptDefinition || scriptDefinition.template == Any::class -> null
scriptDefinition is KotlinScriptDefinitionFromTemplate -> // TODO: should include the file itself
KotlinScriptConfigurationManager.getInstance(project).getAllScriptsClasspathScope()
else -> null
}
return null
}
}