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.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.LibraryOrderEntry
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.VirtualFile
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.getLocalPath
import com.intellij.util.containers.MultiMap
import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.ID
import com.intellij.util.indexing.ScalarIndexExtension
import org.jetbrains.kotlin.idea.KotlinPluginUtil
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.JavaRuntimePresentationProvider
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.utils.KotlinJavascriptMetadataUtils
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
import java.io.IOException
import java.util.*
fun getLibraryRootsWithAbiIncompatibleKotlinClasses(project: Project): Collection<VirtualFile> {
fun getLibraryRootsWithAbiIncompatibleKotlinClasses(module: Module): Collection<VirtualFile> {
return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinMetadataVersionIndex,
{ module -> ProjectStructureUtil.isJavaKotlinModule(module) },
module, KotlinMetadataVersionIndex,
{ version -> !version.isCompatible() })
}
fun getLibraryRootsWithAbiIncompatibleForKotlinJs(project: Project): Collection<VirtualFile> {
fun getLibraryRootsWithAbiIncompatibleForKotlinJs(module: Module): Collection<VirtualFile> {
return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinJavaScriptAbiVersionIndex,
{ module -> ProjectStructureUtil.isJsKotlinModule(module) },
module, KotlinJavaScriptAbiVersionIndex,
{ 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(
project: Project,
module: Module,
index: ScalarIndexExtension<BinaryVersion>,
checkModule: (Module) -> Boolean,
checkVersion: (BinaryVersion) -> Boolean): Collection<VirtualFile> {
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)
if (modulesToCheck.isEmpty()) return emptyList()
val versions = collectAllKeys(id, modulesToCheck)
val badVersions = versions.filter(checkVersion).toHashSet()
val allVersions = FileBasedIndex.getInstance().getAllKeys(id, module.project)
val badVersions = allVersions.filter(checkVersion).toHashSet()
val badRoots = Sets.newHashSet<VirtualFile>()
val fileIndex = ProjectFileIndex.SERVICE.getInstance(project)
val fileIndex = ProjectFileIndex.SERVICE.getInstance(module.project)
for (version in badVersions) {
val indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project))
id, version, moduleWithAllDependentLibraries)
for (indexedFile in indexedFiles) {
val libraryRoot = fileIndex.getClassRootForFile(indexedFile)
@@ -247,17 +239,6 @@ private fun getLibraryRootsWithAbiIncompatibleVersion(
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) {
Messages.showErrorDialog(project,
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.fileEditor.FileEditor
import com.intellij.openapi.fileEditor.OpenFileDescriptor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.project.DumbService
@@ -41,7 +42,6 @@ import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotifications
import com.intellij.ui.HyperlinkLabel
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.JavaRuntimePresentationProvider
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 kotlinLibraries = findAllUsedLibraries(project).keySet()
@@ -89,7 +89,7 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
answer.setText(text)
answer.createActionLabel(actionLabelText) { updateLibraries(project, badRuntimeLibraries) }
if (otherBadRootsCount > 0) {
createShowPathsActionLabel(answer, "Show all")
createShowPathsActionLabel(module, answer, "Show all")
}
}
else if (badRoots.size == 1) {
@@ -101,16 +101,16 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
}
else {
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
}
private fun createShowPathsActionLabel(answer: EditorNotificationPanel, labelText: String) {
private fun createShowPathsActionLabel(module: Module, answer: EditorNotificationPanel, labelText: String) {
val label: Ref<HyperlinkLabel> = Ref.create()
val action = Runnable {
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" }
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
if (!isModuleConfigured(module)) return null
return checkAndCreate(project)
return checkAndCreate(module)
}
catch (e: ProcessCanceledException) {
// Ignore
@@ -149,6 +147,20 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
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>)
: BaseListPopupStep<VirtualFile>(title, *roots.toTypedArray()) {
@@ -191,27 +203,13 @@ class UnsupportedAbiVersionNotificationPanelProvider(private val project: Projec
companion object {
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) {
OpenFileDescriptor(project, root).navigate(true)
}
private fun collectBadRoots(project: Project): Collection<VirtualFile> {
val badJVMRoots = getLibraryRootsWithAbiIncompatibleKotlinClasses(project)
val badJSRoots = getLibraryRootsWithAbiIncompatibleForKotlinJs(project)
private fun collectBadRoots(module: Module): Collection<VirtualFile> {
val badJVMRoots = getLibraryRootsWithAbiIncompatibleKotlinClasses(module)
val badJSRoots = getLibraryRootsWithAbiIncompatibleForKotlinJs(module)
if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return emptyList()