Fix the way how script SDK is found in IDE

This commit is contained in:
Natalia Selezneva
2018-03-29 10:48:00 +03:00
parent 8d9f539c79
commit 429e0afe2a
4 changed files with 146 additions and 12 deletions
@@ -369,7 +369,7 @@ private class LibrarySourceScope(project: Project, private val library: Library)
} }
//TODO: (module refactoring) android sdk has modified scope //TODO: (module refactoring) android sdk has modified scope
private class SdkScope(project: Project, private val sdk: Sdk) : private class SdkScope(project: Project, val sdk: Sdk) :
LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) { LibraryScopeBase(project, sdk.rootProvider.getFiles(OrderRootType.CLASSES), arrayOf<VirtualFile>()) {
override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk override fun equals(other: Any?) = other is SdkScope && sdk == other.sdk
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.idea.core.script.ScriptDependenciesManager
import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope import org.jetbrains.kotlin.idea.stubindex.KotlinSourceFilterScope
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.KotlinScriptDefinition
import java.io.File
import kotlin.script.experimental.dependencies.ScriptDependencies import kotlin.script.experimental.dependencies.ScriptDependencies
class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) { class ScriptModuleSearchScope(val scriptFile: VirtualFile, baseScope: GlobalSearchScope) : DelegatingGlobalSearchScope(baseScope) {
@@ -61,8 +62,9 @@ fun findJdk(dependencies: ScriptDependencies?, project: Project): Sdk? {
null null
} }
return allJdks.find { javaHome != null && it.homePath == javaHome } ?: ProjectRootManager.getInstance(project).projectSdk return allJdks.find { javaHome != null && File(it.homePath).canonicalPath == javaHome }
?: allJdks.firstOrNull() ?: ProjectRootManager.getInstance(project).projectSdk
?: allJdks.firstOrNull()
} }
sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, BinaryModuleInfo { sealed class ScriptDependenciesInfo(val project: Project) : IdeaModuleInfo, BinaryModuleInfo {
@@ -24,6 +24,11 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.ex.PathUtilEx import com.intellij.openapi.projectRoots.ex.PathUtilEx
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.idea.caches.project.SdkInfo
import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo
import org.jetbrains.kotlin.script.KotlinScriptDefinition 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
@@ -223,7 +228,9 @@ class BundledKotlinScriptDependenciesResolver(private val project: Project) : De
scriptContents: ScriptContents, scriptContents: ScriptContents,
environment: Environment environment: Environment
): DependenciesResolver.ResolveResult { ): DependenciesResolver.ResolveResult {
val javaHome = getScriptSDK(project) val virtualFile = scriptContents.file?.let { VfsUtil.findFileByIoFile(it, true) }
val javaHome = getScriptSDK(project, virtualFile)
val dependencies = ScriptDependencies( val dependencies = ScriptDependencies(
javaHome = javaHome?.let(::File), javaHome = javaHome?.let(::File),
classpath = with(PathUtil.kotlinPathsForIdeaPlugin) { classpath = with(PathUtil.kotlinPathsForIdeaPlugin) {
@@ -237,8 +244,18 @@ class BundledKotlinScriptDependenciesResolver(private val project: Project) : De
return dependencies.asSuccess() return dependencies.asSuccess()
} }
private fun getScriptSDK(project: Project): String? { private fun getScriptSDK(project: Project, virtualFile: VirtualFile?): String? {
val jdk = ProjectJdkTable.getInstance().allJdks.firstOrNull { sdk -> sdk.sdkType is JavaSdk } ?: PathUtilEx.getAnyJdk(project) if (virtualFile != null) {
val dependentModuleSourceInfo = getScriptRelatedModuleInfo(project, virtualFile)
val sdk = dependentModuleSourceInfo?.dependencies()?.filterIsInstance<SdkInfo>()?.singleOrNull()?.sdk
if (sdk != null) {
return sdk.homePath
}
}
val jdk = ProjectRootManager.getInstance(project).projectSdk
?: ProjectJdkTable.getInstance().allJdks.firstOrNull { sdk -> sdk.sdkType is JavaSdk }
?: PathUtilEx.getAnyJdk(project)
return jdk?.homePath return jdk?.homePath
} }
} }
@@ -18,20 +18,29 @@ package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.StdModuleTypes import com.intellij.openapi.module.StdModuleTypes
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.roots.DependencyScope import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.roots.ModuleRootModificationUtil
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.libraries.Library import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess
import com.intellij.testFramework.ModuleTestCase import com.intellij.testFramework.ModuleTestCase
import com.intellij.testFramework.PlatformTestCase
import com.intellij.testFramework.PsiTestUtil import com.intellij.testFramework.PsiTestUtil
import com.intellij.testFramework.UsefulTestCase import com.intellij.testFramework.UsefulTestCase
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.idea.caches.project.* import org.jetbrains.kotlin.idea.caches.project.*
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryKind import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.test.util.addDependency import org.jetbrains.kotlin.test.util.addDependency
import org.jetbrains.kotlin.test.util.jarRoot import org.jetbrains.kotlin.test.util.jarRoot
import org.jetbrains.kotlin.test.util.projectLibrary import org.jetbrains.kotlin.test.util.projectLibrary
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.junit.Assert import org.junit.Assert
class IdeaModuleInfoTest : ModuleTestCase() { class IdeaModuleInfoTest : ModuleTestCase() {
@@ -319,12 +328,108 @@ class IdeaModuleInfoTest : ModuleTestCase() {
stdlibJs.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes) stdlibJs.classes.assertAdditionalLibraryDependencies(stdlibCommon.classes)
} }
fun testScriptDependenciesForModule() {
val a = module("a")
val b = module("b")
with(createFileInModule(a, "script.kts").moduleInfo) {
dependencies().contains(a.production)
dependencies().contains(a.test)
!dependencies().contains(b.production)
dependencies().firstIsInstance<ScriptDependenciesInfo.ForFile>()
}
}
override fun setUp() {
super.setUp()
VfsRootAccess.allowRootAccess("C:/Work/Projects/kotlin/")
}
fun testScriptDependenciesForProject() {
val a = module("a")
val script = createFileInProject("script.kts").moduleInfo
!script.dependencies().contains(a.production)
!script.dependencies().contains(a.test)
script.dependencies().firstIsInstance<ScriptDependenciesInfo.ForFile>()
}
fun testSdkForScript() {
runWriteAction {
ProjectJdkTable.getInstance().addJdk(mockJdk6())
ProjectJdkTable.getInstance().addJdk(mockJdk9())
}
with(createFileInProject("script.kts").moduleInfo) {
dependencies().filterIsInstance<SdkInfo>().single { it.sdk == mockJdk6() }
}
}
fun testSdkForScriptProjectSdk() {
runWriteAction {
ProjectJdkTable.getInstance().addJdk(mockJdk6())
ProjectJdkTable.getInstance().addJdk(mockJdk9())
ProjectRootManager.getInstance(project).projectSdk = mockJdk9()
}
with(createFileInProject("script.kts").moduleInfo) {
dependencies().filterIsInstance<SdkInfo>().single { it.sdk == mockJdk9() }
}
}
fun testSdkForScriptModuleSdk() {
val a = module("a")
runWriteAction {
ProjectJdkTable.getInstance().addJdk(mockJdk6())
ProjectJdkTable.getInstance().addJdk(mockJdk9())
ProjectRootManager.getInstance(project).projectSdk = mockJdk6()
with(ModuleRootManager.getInstance(a).modifiableModel) {
sdk = mockJdk9()
commit()
}
}
with(createFileInModule(a, "script.kts").moduleInfo) {
dependencies().filterIsInstance<SdkInfo>().first { it.sdk == mockJdk9() }
}
}
private fun createFileInModule(module: Module, fileName: String, inTests: Boolean = false): VirtualFile {
val fileToCopyIO = createTempFile(fileName, "")
for (contentEntry in ModuleRootManager.getInstance(module).contentEntries) {
for (sourceFolder in contentEntry.sourceFolders) {
if (((!inTests && !sourceFolder.isTestSource) || (inTests && sourceFolder.isTestSource)) && sourceFolder.file != null) {
return runWriteAction {
PlatformTestCase.getVirtualFile(fileToCopyIO).copy(this, sourceFolder.file!!, fileName)
}
}
}
}
error("Couldn't find source folder in ${module.name}")
}
private fun createFileInProject(fileName: String): VirtualFile {
return runWriteAction {
PlatformTestCase.getVirtualFile(createTempFile(fileName, "")).copy(this, project.baseDir, fileName)
}
}
private fun Module.addDependency( private fun Module.addDependency(
other: Module, other: Module,
dependencyScope: DependencyScope = DependencyScope.COMPILE, dependencyScope: DependencyScope = DependencyScope.COMPILE,
exported: Boolean = false exported: Boolean = false
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported) ) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
private val VirtualFile.moduleInfo: IdeaModuleInfo
get() = getModuleInfoByVirtualFile(project, this)!!
private val Module.production: ModuleProductionSourceInfo private val Module.production: ModuleProductionSourceInfo
get() = productionSourceInfo()!! get() = productionSourceInfo()!!
@@ -361,13 +466,23 @@ class IdeaModuleInfoTest : ModuleTestCase() {
UsefulTestCase.assertSameElements(this.getDependentModules(), expected.toList()) UsefulTestCase.assertSameElements(this.getDependentModules(), expected.toList())
} }
private fun stdlibCommon(): Library = projectLibrary("kotlin-stdlib-common", private fun stdlibCommon(): Library = projectLibrary(
ForTestCompileRuntime.stdlibCommonForTests().jarRoot, "kotlin-stdlib-common",
kind = CommonLibraryKind) ForTestCompileRuntime.stdlibCommonForTests().jarRoot,
kind = CommonLibraryKind
)
private fun stdlibJvm(): Library = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests().jarRoot) private fun stdlibJvm(): Library = projectLibrary("kotlin-stdlib", ForTestCompileRuntime.runtimeJarForTests().jarRoot)
private fun stdlibJs(): Library = projectLibrary("kotlin-stdlib-js", private fun stdlibJs(): Library = projectLibrary(
ForTestCompileRuntime.runtimeJarForTests().jarRoot, "kotlin-stdlib-js",
kind = JSLibraryKind) ForTestCompileRuntime.runtimeJarForTests().jarRoot,
kind = JSLibraryKind
)
override fun tearDown() {
clearSdkTable(testRootDisposable)
super.tearDown()
}
} }