Migration for pre-1.1.3 JS libraries

#KT-19156 Fixed
This commit is contained in:
Dmitry Jemerov
2017-07-21 19:49:52 +02:00
parent 861219f502
commit e0dc7a27a0
16 changed files with 196 additions and 43 deletions
@@ -33,9 +33,9 @@ import java.util.jar.Attributes
object JsLibraryStdDetectionUtil {
private val IS_JS_LIBRARY_STD_LIB = Key.create<Boolean>("IS_JS_LIBRARY_STD_LIB")
fun hasJsStdlibJar(library: Library): Boolean {
fun hasJsStdlibJar(library: Library, ignoreKind: Boolean = false): Boolean {
if (library !is LibraryEx || library.isDisposed) return false
if (library.kind !is JSLibraryKind) return false
if (!ignoreKind && library.kind !is JSLibraryKind) return false
val classes = Arrays.asList(*library.getFiles(OrderRootType.CLASSES))
return getJsStdLibJar(classes) != null
@@ -90,7 +90,7 @@ public class ProjectStructureUtil {
ModuleRootManager.getInstance(module).orderEntries().librariesOnly().forEachLibrary(new Processor<Library>() {
@Override
public boolean process(Library library) {
if (JsLibraryStdDetectionUtil.INSTANCE.hasJsStdlibJar(library)) {
if (JsLibraryStdDetectionUtil.INSTANCE.hasJsStdlibJar(library, false)) {
jsLibrary.set(library);
return false;
}
@@ -22,12 +22,19 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
import com.intellij.openapi.util.Computable
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.FileTypeIndex
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
@@ -202,7 +209,7 @@ fun hasAnyKotlinRuntimeInScope(module: Module): Boolean {
val scope = module.getModuleWithDependenciesAndLibrariesScope(hasKotlinFilesOnlyInTests(module))
return runReadAction {
getKotlinJvmRuntimeMarkerClass(module.project, scope) != null ||
hasKotlinJsKjsmFile(module.project, scope) ||
hasKotlinJsKjsmFile(module.project, LibraryKindSearchScope(module, scope, JSLibraryKind) ) ||
hasKotlinCommonRuntimeInScope(scope)
}
}
@@ -217,7 +224,7 @@ fun hasKotlinJvmRuntimeInScope(module: Module): Boolean {
fun hasKotlinJsRuntimeInScope(module: Module): Boolean {
return runReadAction {
val scope = module.getModuleWithDependenciesAndLibrariesScope(hasKotlinFilesOnlyInTests(module))
hasKotlinJsKjsmFile(module.project, scope)
hasKotlinJsKjsmFile(module.project, LibraryKindSearchScope(module, scope, JSLibraryKind))
}
}
@@ -244,4 +251,18 @@ fun isEap(version: String): Boolean {
fun useEapRepository(minorKotlinVersion: Int, version: String): Boolean {
return Regex("1\\.$minorKotlinVersion(\\.\\d)?-[A-Za-z][A-Za-z0-9-]*").matches(version) &&
!version.startsWith("1.$minorKotlinVersion.0-dev")
}
}
private class LibraryKindSearchScope(val module: Module,
val baseScope: GlobalSearchScope,
val libraryKind: PersistentLibraryKind<*>
) : DelegatingGlobalSearchScope(baseScope) {
override fun contains(file: VirtualFile): Boolean {
if (!super.contains(file)) return false
val orderEntry = ModuleRootManager.getInstance(module).fileIndex.getOrderEntryForFile(file)
if (orderEntry is LibraryOrderEntry) {
return (orderEntry.library as LibraryEx).kind == libraryKind
}
return true
}
}
@@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.notification.Notification
import com.intellij.notification.NotificationsManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
import kotlin.reflect.KClass
@@ -64,3 +66,16 @@ interface KotlinSingleNotificationManager<in T: Notification> {
}
}
fun checkHideNonConfiguredNotifications(project: Project) {
if (ConfigureKotlinNotificationManager.getVisibleNotifications(project).isNotEmpty()) {
ApplicationManager.getApplication().executeOnPooledThread {
DumbService.getInstance(project).waitForSmartMode()
if (getConfigurableModulesWithKotlinFiles(project).all(::isModuleConfigured)) {
ApplicationManager.getApplication().invokeLater {
ConfigureKotlinNotificationManager.expireOldNotifications(project)
}
}
}
}
}
@@ -22,6 +22,9 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.projectRoots.JavaSdkVersion
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.Library
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.JvmTarget
@@ -30,6 +33,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgume
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
import org.jetbrains.kotlin.idea.facet.initializeIfNeeded
import org.jetbrains.kotlin.idea.framework.JavaRuntimeLibraryDescription
import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
@@ -38,6 +42,9 @@ import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
open class KotlinJavaModuleConfigurator internal constructor() : KotlinWithLibraryConfigurator() {
override fun isApplicable(module: Module): Boolean {
return super.isApplicable(module) && !hasBrokenJsRuntime(module)
}
override fun isConfigured(module: Module): Boolean {
return hasKotlinJvmRuntimeInScope(module)
@@ -113,4 +120,12 @@ open class KotlinJavaModuleConfigurator internal constructor() : KotlinWithLibra
val instance: KotlinJavaModuleConfigurator
get() = Extensions.findExtension(KotlinProjectConfigurator.EP_NAME, KotlinJavaModuleConfigurator::class.java)
}
private fun hasBrokenJsRuntime(module: Module): Boolean {
for (orderEntry in ModuleRootManager.getInstance(module).orderEntries) {
val library = (orderEntry as? LibraryOrderEntry)?.library as? LibraryEx ?: continue
if (JsLibraryStdDetectionUtil.hasJsStdlibJar(library, ignoreKind = true)) return true
}
return false
}
}
@@ -18,11 +18,17 @@ package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.libraries.DummyLibraryProperties
import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.LibraryType
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.JSLibraryStdDescription
import org.jetbrains.kotlin.idea.framework.JSLibraryType
import org.jetbrains.kotlin.idea.framework.JsLibraryStdDetectionUtil
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
import org.jetbrains.kotlin.idea.versions.isKotlinJsRuntime
import org.jetbrains.kotlin.js.JavaScript
@@ -65,4 +71,32 @@ open class KotlinJsModuleConfigurator : KotlinWithLibraryConfigurator() {
companion object {
const val NAME = JavaScript.LOWER_NAME
}
/**
* Migrate pre-1.1.3 projects which didn't have explicitly specified kind for JS libraries.
*/
override fun findAndFixBrokenKotlinLibrary(module: Module, collector: NotificationMessageCollector): Library? {
val allLibraries = mutableListOf<LibraryEx>()
var brokenStdlib: Library? = null
for (orderEntry in ModuleRootManager.getInstance(module).orderEntries) {
val library = (orderEntry as? LibraryOrderEntry)?.library as? LibraryEx ?: continue
allLibraries.add(library)
if (JsLibraryStdDetectionUtil.hasJsStdlibJar(library, ignoreKind = true) && library.kind == null) {
brokenStdlib = library
}
}
if (brokenStdlib != null) {
runWriteAction {
for (library in allLibraries.filter { it.kind == null }) {
library.modifiableModel.apply {
kind = JSLibraryKind
commit()
}
}
}
collector.addMessage("Updated JavaScript libraries in module ${module.name}")
}
return brokenStdlib
}
}
@@ -114,7 +114,7 @@ class KotlinSetupEnvironmentNotificationProvider(
createComponentActionLabel("Configure") { label ->
val singleConfigurator = configurators.singleOrNull()
if (singleConfigurator != null) {
singleConfigurator.configure(module.project, emptyList())
singleConfigurator.apply(module.project)
}
else {
val configuratorsPopup = createConfiguratorsPopup(module.project, configurators)
@@ -125,15 +125,19 @@ class KotlinSetupEnvironmentNotificationProvider(
}
}
private fun KotlinProjectConfigurator.apply(project: Project) {
configure(project, emptyList())
EditorNotifications.getInstance(project).updateAllNotifications()
checkHideNonConfiguredNotifications(project)
}
fun createConfiguratorsPopup(project: Project, configurators: List<KotlinProjectConfigurator>): ListPopup {
val step = object : BaseListPopupStep<KotlinProjectConfigurator>("Choose Configurator", configurators) {
override fun getTextFor(value: KotlinProjectConfigurator?): String {
return value?.presentableText ?: "<none>"
}
override fun getTextFor(value: KotlinProjectConfigurator?) = value?.presentableText ?: "<none>"
override fun onChosen(selectedValue: KotlinProjectConfigurator?, finalChoice: Boolean): PopupStep<*>? {
return doFinalStep {
selectedValue?.configure(project, emptyList())
selectedValue?.apply(project)
}
}
}
@@ -137,7 +137,8 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
) {
val project = module.project
val library = getKotlinLibrary(module)
val library = findAndFixBrokenKotlinLibrary(module, collector)
?: getKotlinLibrary(module)
?: getKotlinLibrary(project)
?: createNewLibrary(project, collector)
@@ -316,15 +317,17 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
protected open fun configureKotlinSettings(modules: List<Module>) {
}
protected open fun findAndFixBrokenKotlinLibrary(module: Module, collector: NotificationMessageCollector): Library? = null
protected open fun isApplicable(module: Module): Boolean {
return !KotlinPluginUtil.isAndroidGradleModule(module) &&
!KotlinPluginUtil.isMavenModule(module) &&
!KotlinPluginUtil.isGradleModule(module)
}
companion object {
val DEFAULT_LIBRARY_DIR = "lib"
protected fun isApplicable(module: Module): Boolean {
return !KotlinPluginUtil.isAndroidGradleModule(module) &&
!KotlinPluginUtil.isMavenModule(module) &&
!KotlinPluginUtil.isGradleModule(module)
}
fun getPathFromLibrary(library: Library?, type: OrderRootType): String? {
if (library == null) return null
@@ -26,7 +26,9 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ModuleRootEvent
import com.intellij.openapi.roots.ModuleRootListener
import com.intellij.openapi.startup.StartupManager
import org.jetbrains.kotlin.idea.configuration.*
import org.jetbrains.kotlin.idea.configuration.checkHideNonConfiguredNotifications
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
import org.jetbrains.kotlin.idea.configuration.showConfigureKotlinNotificationIfNeeded
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
import org.jetbrains.kotlin.idea.versions.collectModulesWithOutdatedRuntime
import org.jetbrains.kotlin.idea.versions.findOutdatedKotlinLibraries
@@ -54,16 +56,7 @@ class KotlinConfigurationCheckerComponent(project: Project) : AbstractProjectCom
}
}
if (ConfigureKotlinNotificationManager.getVisibleNotifications(project).isNotEmpty()) {
ApplicationManager.getApplication().executeOnPooledThread {
DumbService.getInstance(myProject).waitForSmartMode()
if (getConfigurableModulesWithKotlinFiles(project).all(::isModuleConfigured)) {
ApplicationManager.getApplication().invokeLater {
ConfigureKotlinNotificationManager.expireOldNotifications(project)
}
}
}
}
checkHideNonConfiguredNotifications(project)
}
})
}
@@ -7,7 +7,7 @@
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK"/>
<component name="libraryTable">
<library name="KotlinJavaScript">
<library name="KotlinJavaScript" type="kotlin.js">
<CLASSES />
<JAVADOC />
<SOURCES />
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime (2)" level="project" />
</component>
</module>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
<component name="CopyrightManager" default="" />
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/module.iml" filepath="$PROJECT_DIR$/module.iml" />
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="libraryTable">
<library name="KotlinJavaRuntime (2)">
<CLASSES>
<root url="jar://$PROJECT_DIR$/../jsRuntime11Maven/kotlin-stdlib-js-1.1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
</SOURCES>
</library>
</component>
</project>
@@ -8,7 +8,7 @@
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK"/>
<component name="libraryTable">
<library name="KotlinJavaScript">
<library name="KotlinJavaScript" type="kotlin.js">
<CLASSES>
<root url="jar://$TEMP_TEST_DIR$/kotlin-stdlib-js.jar!/" />
</CLASSES>
@@ -176,24 +176,24 @@ abstract class AbstractConfigureKotlinTest : PlatformTestCase() {
}
private val pathToNonexistentRuntimeJar: String
get() {
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.KOTLIN_JAVA_RUNTIME_JAR
PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar))
return pathToTempKotlinRuntimeJar
}
get() {
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.KOTLIN_JAVA_RUNTIME_JAR
PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar))
return pathToTempKotlinRuntimeJar
}
private val pathToNonexistentJsJar: String
get() {
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.JS_LIB_JAR_NAME
PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar))
return pathToTempKotlinRuntimeJar
}
get() {
val pathToTempKotlinRuntimeJar = FileUtil.getTempDirectory() + "/" + PathUtil.JS_LIB_JAR_NAME
PlatformTestCase.myFilesToDelete.add(File(pathToTempKotlinRuntimeJar))
return pathToTempKotlinRuntimeJar
}
private val pathToExistentRuntimeJar: String
get() = PathUtil.getKotlinPathsForDistDirectory().stdlibPath.parent
get() = PathUtil.getKotlinPathsForDistDirectory().stdlibPath.parent
private val pathToExistentJsJar: String
get() = PathUtil.getKotlinPathsForDistDirectory().jsStdLibJarPath.parent
get() = PathUtil.getKotlinPathsForDistDirectory().jsStdLibJarPath.parent
protected fun assertNotConfigured(module: Module, configurator: KotlinWithLibraryConfigurator) {
TestCase.assertFalse(
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.idea.configuration;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.RootPolicy;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.io.FileUtil;
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments;
@@ -121,6 +124,11 @@ public class ConfigureKotlinTest extends AbstractConfigureKotlinTest {
doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.DO_NOT_COPY);
}
public void testJsLibraryWrongKind() {
doTestOneJsModule(KotlinWithLibraryConfigurator.FileState.EXISTS);
assertEquals(1, ModuleRootManager.getInstance(getModule()).orderEntries().process(new LibraryCountingRootPolicy(), 0).intValue());
}
public void testProjectWithoutFacetWithRuntime106WithoutLanguageLevel() {
assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(myProject, null).getLanguageVersion());
assertEquals(LanguageVersion.KOTLIN_1_0, PlatformKt.getLanguageVersionSettings(getModule()).getLanguageVersion());
@@ -261,4 +269,11 @@ public class ConfigureKotlinTest extends AbstractConfigureKotlinTest {
public void testProjectWithoutFacetWithJvmTarget18() {
assertEquals(TargetPlatformKind.Jvm.Companion.get(JvmTarget.JVM_1_8), PlatformKt.getTargetPlatform(getModule()));
}
private static class LibraryCountingRootPolicy extends RootPolicy<Integer> {
@Override
public Integer visitLibraryOrderEntry(LibraryOrderEntry libraryOrderEntry, Integer value) {
return value + 1;
}
}
}