show unsupported ABI notification only when current module or its dependencies have libraries with bad ABI

This commit is contained in:
Dmitry Jemerov
2016-01-19 16:50:55 +01:00
parent 6f80f36f2e
commit 170ba98b20
2 changed files with 37 additions and 58 deletions
@@ -20,6 +20,7 @@ import com.google.common.collect.Sets
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.LibraryOrderEntry
import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootManager
@@ -31,13 +32,10 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.JarFileSystem import com.intellij.openapi.vfs.JarFileSystem
import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import com.intellij.util.CommonProcessors
import com.intellij.util.PathUtil.getLocalFile import com.intellij.util.PathUtil.getLocalFile
import com.intellij.util.PathUtil.getLocalPath import com.intellij.util.PathUtil.getLocalPath
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import com.intellij.util.indexing.FileBasedIndex import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.ID
import com.intellij.util.indexing.ScalarIndexExtension import com.intellij.util.indexing.ScalarIndexExtension
import org.jetbrains.kotlin.idea.KotlinPluginUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator
@@ -46,25 +44,21 @@ import org.jetbrains.kotlin.idea.configuration.getConfiguratorByName
import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider
import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider
import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.PathUtil
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.*
fun getLibraryRootsWithAbiIncompatibleKotlinClasses(project: Project): Collection<VirtualFile> { fun getLibraryRootsWithAbiIncompatibleKotlinClasses(module: Module): Collection<VirtualFile> {
return getLibraryRootsWithAbiIncompatibleVersion( return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinMetadataVersionIndex, module, KotlinMetadataVersionIndex,
{ module -> ProjectStructureUtil.isJavaKotlinModule(module) },
{ version -> !version.isCompatible() }) { version -> !version.isCompatible() })
} }
fun getLibraryRootsWithAbiIncompatibleForKotlinJs(project: Project): Collection<VirtualFile> { fun getLibraryRootsWithAbiIncompatibleForKotlinJs(module: Module): Collection<VirtualFile> {
return getLibraryRootsWithAbiIncompatibleVersion( return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinJavaScriptAbiVersionIndex, module, KotlinJavaScriptAbiVersionIndex,
{ module -> ProjectStructureUtil.isJsKotlinModule(module) },
{ version -> !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.minor) }) // TODO: support major.minor.patch version in JS metadata { version -> !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.minor) }) // TODO: support major.minor.patch version in JS metadata
} }
@@ -217,25 +211,23 @@ internal fun replaceFile(updatedFile: File, replacedJarFile: VirtualFile) {
} }
private fun getLibraryRootsWithAbiIncompatibleVersion( private fun getLibraryRootsWithAbiIncompatibleVersion(
project: Project, module: Module,
index: ScalarIndexExtension<BinaryVersion>, index: ScalarIndexExtension<BinaryVersion>,
checkModule: (Module) -> Boolean,
checkVersion: (BinaryVersion) -> Boolean): Collection<VirtualFile> { checkVersion: (BinaryVersion) -> Boolean): Collection<VirtualFile> {
val id = index.name val id = index.name
val modules = ModuleManager.getInstance(project).modules val moduleWithAllDependencies = setOf(module) + ModuleUtil.getAllDependentModules(module)
val moduleWithAllDependentLibraries = GlobalSearchScope.union(
moduleWithAllDependencies.map { it.moduleWithLibrariesScope }.toTypedArray())
val modulesToCheck = modules.filter(checkModule) val allVersions = FileBasedIndex.getInstance().getAllKeys(id, module.project)
if (modulesToCheck.isEmpty()) return emptyList() val badVersions = allVersions.filter(checkVersion).toHashSet()
val versions = collectAllKeys(id, modulesToCheck)
val badVersions = versions.filter(checkVersion).toHashSet()
val badRoots = Sets.newHashSet<VirtualFile>() val badRoots = Sets.newHashSet<VirtualFile>()
val fileIndex = ProjectFileIndex.SERVICE.getInstance(project) val fileIndex = ProjectFileIndex.SERVICE.getInstance(module.project)
for (version in badVersions) { for (version in badVersions) {
val indexedFiles = FileBasedIndex.getInstance().getContainingFiles( val indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project)) id, version, moduleWithAllDependentLibraries)
for (indexedFile in indexedFiles) { for (indexedFile in indexedFiles) {
val libraryRoot = fileIndex.getClassRootForFile(indexedFile) val libraryRoot = fileIndex.getClassRootForFile(indexedFile)
@@ -247,17 +239,6 @@ private fun getLibraryRootsWithAbiIncompatibleVersion(
return badRoots return badRoots
} }
private fun <T> collectAllKeys(id: ID<T, Void>, modules: List<Module>): Collection<T> {
val allKeys = HashSet<T>()
for (module in modules) {
val scope = GlobalSearchScope.moduleWithLibrariesScope(module)
FileBasedIndex.getInstance().processAllKeys(id, CommonProcessors.CollectProcessor(allKeys), scope, null)
}
return allKeys
}
fun showRuntimeJarNotFoundDialog(project: Project, jarName: String) { fun showRuntimeJarNotFoundDialog(project: Project, jarName: String) {
Messages.showErrorDialog(project, Messages.showErrorDialog(project,
jarName + " is not found. Make sure plugin is properly installed.", jarName + " is not found. Make sure plugin is properly installed.",
@@ -23,6 +23,7 @@ import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.DumbService
@@ -41,7 +42,6 @@ import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotifications import com.intellij.ui.EditorNotifications
import com.intellij.ui.HyperlinkLabel import com.intellij.ui.HyperlinkLabel
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.configuration.isModuleConfigured
import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider
import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider
import java.text.MessageFormat import java.text.MessageFormat
@@ -66,7 +66,7 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
}) })
} }
private fun doCreate(badRoots: Collection<VirtualFile>): EditorNotificationPanel { private fun doCreate(module: Module, badRoots: Collection<VirtualFile>): EditorNotificationPanel {
val answer = ErrorNotificationPanel() val answer = ErrorNotificationPanel()
val kotlinLibraries = findAllUsedLibraries(project).keySet() val kotlinLibraries = findAllUsedLibraries(project).keySet()
@@ -89,7 +89,7 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
answer.setText(text) answer.setText(text)
answer.createActionLabel(actionLabelText) { updateLibraries(project, badRuntimeLibraries) } answer.createActionLabel(actionLabelText) { updateLibraries(project, badRuntimeLibraries) }
if (otherBadRootsCount > 0) { if (otherBadRootsCount > 0) {
createShowPathsActionLabel(answer, "Show all") createShowPathsActionLabel(module, answer, "Show all")
} }
} }
else if (badRoots.size == 1) { else if (badRoots.size == 1) {
@@ -101,16 +101,16 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
} }
else { else {
answer.setText("Some Kotlin libraries attached to this project have unsupported format. Please update the libraries or the plugin") answer.setText("Some Kotlin libraries attached to this project have unsupported format. Please update the libraries or the plugin")
createShowPathsActionLabel(answer, "Show paths") createShowPathsActionLabel(module, answer, "Show paths")
} }
return answer return answer
} }
private fun createShowPathsActionLabel(answer: EditorNotificationPanel, labelText: String) { private fun createShowPathsActionLabel(module: Module, answer: EditorNotificationPanel, labelText: String) {
val label: Ref<HyperlinkLabel> = Ref.create() val label: Ref<HyperlinkLabel> = Ref.create()
val action = Runnable { val action = Runnable {
DumbService.getInstance(project).tryRunReadActionInSmartMode({ DumbService.getInstance(project).tryRunReadActionInSmartMode({
val badRoots = collectBadRoots(project) val badRoots = collectBadRoots(module)
assert(!badRoots.isEmpty()) { "This action should only be called when bad roots are present" } assert(!badRoots.isEmpty()) { "This action should only be called when bad roots are present" }
val listPopupModel = LibraryRootsPopupModel("Unsupported format", project, badRoots) val listPopupModel = LibraryRootsPopupModel("Unsupported format", project, badRoots)
@@ -135,9 +135,7 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
val module = ModuleUtilCore.findModuleForFile(file, project) ?: return null val module = ModuleUtilCore.findModuleForFile(file, project) ?: return null
if (!isModuleConfigured(module)) return null return checkAndCreate(module)
return checkAndCreate(project)
} }
catch (e: ProcessCanceledException) { catch (e: ProcessCanceledException) {
// Ignore // Ignore
@@ -149,6 +147,20 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
return null return null
} }
fun checkAndCreate(module: Module): EditorNotificationPanel? {
val state = ServiceManager.getService(project, SuppressNotificationState::class.java).state
if (state != null && state.isSuppressed) {
return null
}
val badRoots = collectBadRoots(module)
if (!badRoots.isEmpty()) {
return doCreate(module, badRoots)
}
return null
}
private class LibraryRootsPopupModel(title: String, private val project: Project, roots: Collection<VirtualFile>) private class LibraryRootsPopupModel(title: String, private val project: Project, roots: Collection<VirtualFile>)
: BaseListPopupStep<VirtualFile>(title, *roots.toTypedArray()) { : BaseListPopupStep<VirtualFile>(title, *roots.toTypedArray()) {
@@ -191,27 +203,13 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
companion object { companion object {
private val KEY = Key.create<EditorNotificationPanel>("unsupported.abi.version") private val KEY = Key.create<EditorNotificationPanel>("unsupported.abi.version")
fun checkAndCreate(project: Project): EditorNotificationPanel? {
val state = ServiceManager.getService(project, SuppressNotificationState::class.java).state
if (state != null && state.isSuppressed) {
return null
}
val badRoots = collectBadRoots(project)
if (!badRoots.isEmpty()) {
return UnsupportedAbiVersionNotificationPanelProvider(project).doCreate(badRoots)
}
return null
}
private fun navigateToLibraryRoot(project: Project, root: VirtualFile) { private fun navigateToLibraryRoot(project: Project, root: VirtualFile) {
OpenFileDescriptor(project, root).navigate(true) OpenFileDescriptor(project, root).navigate(true)
} }
private fun collectBadRoots(project: Project): Collection<VirtualFile> { private fun collectBadRoots(module: Module): Collection<VirtualFile> {
val badJVMRoots = getLibraryRootsWithAbiIncompatibleKotlinClasses(project) val badJVMRoots = getLibraryRootsWithAbiIncompatibleKotlinClasses(module)
val badJSRoots = getLibraryRootsWithAbiIncompatibleForKotlinJs(project) val badJSRoots = getLibraryRootsWithAbiIncompatibleForKotlinJs(module)
if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return emptyList() if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return emptyList()