KotlinRuntimeLibraryUtil: J2K

This commit is contained in:
Dmitry Jemerov
2015-11-21 10:41:34 +01:00
parent f588ef2912
commit 2272516d09
7 changed files with 249 additions and 320 deletions
@@ -22,7 +22,7 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.roots.ModifiableModelsProvider import com.intellij.openapi.roots.ModifiableModelsProvider
import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.roots.ModifiableRootModel
import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder import org.jetbrains.plugins.gradle.frameworkSupport.BuildScriptDataBuilder
import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider import org.jetbrains.plugins.gradle.frameworkSupport.GradleFrameworkSupportProvider
import javax.swing.Icon import javax.swing.Icon
@@ -40,7 +40,7 @@ class GradleKotlinJavaFrameworkSupportProvider() : GradleFrameworkSupportProvide
rootModel: ModifiableRootModel, rootModel: ModifiableRootModel,
modifiableModelsProvider: ModifiableModelsProvider, modifiableModelsProvider: ModifiableModelsProvider,
buildScriptData: BuildScriptDataBuilder) { buildScriptData: BuildScriptDataBuilder) {
var kotlinVersion = KotlinRuntimeLibraryUtil.bundledRuntimeVersion() var kotlinVersion = bundledRuntimeVersion()
if (kotlinVersion == "@snapshot@") { if (kotlinVersion == "@snapshot@") {
kotlinVersion = "0.1-SNAPSHOT" kotlinVersion = "0.1-SNAPSHOT"
@@ -21,7 +21,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.SeparatorWithText; import com.intellij.ui.SeparatorWithText;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil; import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtilKt;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -49,7 +49,7 @@ public abstract class CreateLibraryDialogBase extends DialogWrapper {
init(); init();
compilerTextLabel.setText(compilerTextLabel.getText() + " - " + KotlinRuntimeLibraryUtil.bundledRuntimeVersion()); compilerTextLabel.setText(compilerTextLabel.getText() + " - " + KotlinRuntimeLibraryUtilKt.bundledRuntimeVersion());
pathPanel = new ChooseLibraryPathPanel(defaultPath); pathPanel = new ChooseLibraryPathPanel(defaultPath);
pathPanel.addValidityListener(new ValidityListener() { pathPanel.addValidityListener(new ValidityListener() {
@@ -43,7 +43,8 @@ import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType
import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
import org.jetbrains.kotlin.idea.versions.findKotlinLibraries
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -155,7 +156,7 @@ abstract class AddKotlinLibQuickFix(element: KtElement) : KotlinQuickFixAction<K
val configurator = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME) val configurator = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
.firstIsInstanceOrNull<KotlinJavaModuleConfigurator>() ?: return .firstIsInstanceOrNull<KotlinJavaModuleConfigurator>() ?: return
for (library in KotlinRuntimeLibraryUtil.findKotlinLibraries(project)) { for (library in findKotlinLibraries(project)) {
val runtimeJar = JavaRuntimePresentationProvider.getRuntimeJar(library) ?: continue val runtimeJar = JavaRuntimePresentationProvider.getRuntimeJar(library) ?: continue
if (hasLibJarInLibrary(library)) continue if (hasLibJarInLibrary(library)) continue
@@ -194,7 +195,7 @@ abstract class AddKotlinLibQuickFix(element: KtElement) : KotlinQuickFixAction<K
} }
} }
val pluginVersion = KotlinRuntimeLibraryUtil.bundledRuntimeVersion() val pluginVersion = bundledRuntimeVersion()
if ("@snapshot@" == pluginVersion) { if ("@snapshot@" == pluginVersion) {
return "0.1-SNAPSHOT" return "0.1-SNAPSHOT"
} }
@@ -14,319 +14,247 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.versions; package org.jetbrains.kotlin.idea.versions
import com.google.common.collect.Sets; 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.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
import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.libraries.Library
import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil
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.psi.search.ProjectScope; import com.intellij.util.CommonProcessors
import com.intellij.util.CommonProcessors; import com.intellij.util.PathUtil.getLocalFile
import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.PathUtil.getLocalPath
import com.intellij.util.indexing.ID; import com.intellij.util.indexing.FileBasedIndex
import com.intellij.util.indexing.ScalarIndexExtension; import com.intellij.util.indexing.ID
import kotlin.collections.ArraysKt; import com.intellij.util.indexing.ScalarIndexExtension
import kotlin.collections.CollectionsKt; import org.jetbrains.kotlin.idea.KotlinPluginUtil
import kotlin.jvm.functions.Function1; import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.configuration.KotlinJsModuleConfigurator
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.configuration.getConfiguratorByName
import org.jetbrains.kotlin.idea.KotlinPluginUtil; import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider
import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt; import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider
import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator; import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil
import org.jetbrains.kotlin.idea.configuration.KotlinJsModuleConfigurator; import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider; import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification.showRuntimeJarNotFoundDialog
import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider; import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil; import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil; import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion; import java.io.File
import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils; import java.io.IOException
import org.jetbrains.kotlin.utils.KotlinPaths; import java.util.*
import org.jetbrains.kotlin.utils.PathUtil;
import java.io.File; fun getLibraryRootsWithAbiIncompatibleKotlinClasses(project: Project): Collection<VirtualFile> {
import java.io.IOException; return getLibraryRootsWithAbiIncompatibleVersion(
import java.util.*; project, KotlinMetadataVersionIndex,
{ module -> ProjectStructureUtil.isJavaKotlinModule(module) },
{ version -> !version.isCompatible() })
}
import static com.intellij.util.PathUtil.getLocalFile; fun getLibraryRootsWithAbiIncompatibleForKotlinJs(project: Project): Collection<VirtualFile> {
import static com.intellij.util.PathUtil.getLocalPath; return getLibraryRootsWithAbiIncompatibleVersion(
import static org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification.showRuntimeJarNotFoundDialog; project, KotlinJavaScriptAbiVersionIndex,
{ module -> ProjectStructureUtil.isJsKotlinModule(module) },
public class KotlinRuntimeLibraryUtil { { version -> !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.minor) }) // TODO: support major.minor.patch version in JS metadata
private KotlinRuntimeLibraryUtil() { }
}
@NotNull
public static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleKotlinClasses(@NotNull Project project) {
return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinMetadataVersionIndex.INSTANCE,
new Function1<Module, Boolean>() {
@Override
public Boolean invoke(@Nullable Module module) {
return module != null && ProjectStructureUtil.isJavaKotlinModule(module);
}
},
new Function1<BinaryVersion, Boolean>() {
@Override
public Boolean invoke(@NotNull BinaryVersion version) {
return !version.isCompatible();
}
});
}
@NotNull
public static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleForKotlinJs(@NotNull Project project) {
return getLibraryRootsWithAbiIncompatibleVersion(
project, KotlinJavaScriptAbiVersionIndex.INSTANCE,
new Function1<Module, Boolean>() {
@Override
public Boolean invoke(@Nullable Module module) {
return module != null && ProjectStructureUtil.isJsKotlinModule(module);
}
},
new Function1<BinaryVersion, Boolean>() {
@Override
public Boolean invoke(@NotNull BinaryVersion version) {
// TODO: support major.minor.patch version in JS metadata
return !KotlinJavascriptMetadataUtils.isAbiVersionCompatible(version.getMinor());
}
});
}
public static void updateLibraries( fun updateLibraries(
@NotNull final Project project, project: Project,
@NotNull final Collection<Library> libraries libraries: Collection<Library>) {
) { ApplicationManager.getApplication().invokeLater {
ApplicationManager.getApplication().invokeLater(new Runnable() { val kJvmConfigurator = getConfiguratorByName(KotlinJavaModuleConfigurator.NAME) as KotlinJavaModuleConfigurator?
@Override assert(kJvmConfigurator != null) { "Configurator with given name doesn't exists: " + KotlinJavaModuleConfigurator.NAME }
public void run() {
KotlinJavaModuleConfigurator kJvmConfigurator = (KotlinJavaModuleConfigurator)
ConfigureKotlinInProjectUtilsKt.getConfiguratorByName(KotlinJavaModuleConfigurator.NAME);
assert kJvmConfigurator != null : "Configurator with given name doesn't exists: " + KotlinJavaModuleConfigurator.NAME;
KotlinJsModuleConfigurator kJsConfigurator = (KotlinJsModuleConfigurator) val kJsConfigurator = getConfiguratorByName(KotlinJsModuleConfigurator.NAME) as KotlinJsModuleConfigurator?
ConfigureKotlinInProjectUtilsKt.getConfiguratorByName(KotlinJsModuleConfigurator.NAME); assert(kJsConfigurator != null) { "Configurator with given name doesn't exists: " + KotlinJsModuleConfigurator.NAME }
assert kJsConfigurator != null : "Configurator with given name doesn't exists: " + KotlinJsModuleConfigurator.NAME;
for (Library library : libraries) { for (library in libraries) {
if (LibraryPresentationProviderUtil.isDetected(JavaRuntimePresentationProvider.getInstance(), library)) { if (LibraryPresentationProviderUtil.isDetected(JavaRuntimePresentationProvider.getInstance(), library)) {
updateJar(project, JavaRuntimePresentationProvider.getRuntimeJar(library), LibraryJarDescriptor.RUNTIME_JAR); updateJar(project, JavaRuntimePresentationProvider.getRuntimeJar(library), LibraryJarDescriptor.RUNTIME_JAR)
updateJar(project, JavaRuntimePresentationProvider.getReflectJar(library), LibraryJarDescriptor.REFLECT_JAR); updateJar(project, JavaRuntimePresentationProvider.getReflectJar(library), LibraryJarDescriptor.REFLECT_JAR)
if (kJvmConfigurator.changeOldSourcesPathIfNeeded(project, library)) { if (kJvmConfigurator!!.changeOldSourcesPathIfNeeded(project, library)) {
kJvmConfigurator.copySourcesToPathFromLibrary(project, library); kJvmConfigurator.copySourcesToPathFromLibrary(project, library)
} }
else { else {
updateJar(project, JavaRuntimePresentationProvider.getRuntimeSrcJar(library), LibraryJarDescriptor.RUNTIME_SRC_JAR); updateJar(project, JavaRuntimePresentationProvider.getRuntimeSrcJar(library), LibraryJarDescriptor.RUNTIME_SRC_JAR)
}
}
else if (LibraryPresentationProviderUtil.isDetected(JSLibraryStdPresentationProvider.getInstance(), library)) {
updateJar(project, JSLibraryStdPresentationProvider.getJsStdLibJar(library), LibraryJarDescriptor.JS_STDLIB_JAR);
if (kJsConfigurator.changeOldSourcesPathIfNeeded(project, library)) {
kJsConfigurator.copySourcesToPathFromLibrary(project, library);
}
else {
updateJar(project, JSLibraryStdPresentationProvider.getJsStdLibSrcJar(library), LibraryJarDescriptor.JS_STDLIB_SRC_JAR);
}
}
} }
} }
}); else if (LibraryPresentationProviderUtil.isDetected(JSLibraryStdPresentationProvider.getInstance(), library)) {
} updateJar(project, JSLibraryStdPresentationProvider.getJsStdLibJar(library), LibraryJarDescriptor.JS_STDLIB_JAR)
private static void updateJar( if (kJsConfigurator!!.changeOldSourcesPathIfNeeded(project, library)) {
@NotNull Project project, kJsConfigurator.copySourcesToPathFromLibrary(project, library)
@Nullable VirtualFile fileToReplace, }
@NotNull LibraryJarDescriptor libraryJarDescriptor else {
) { updateJar(project, JSLibraryStdPresentationProvider.getJsStdLibSrcJar(library), LibraryJarDescriptor.JS_STDLIB_SRC_JAR)
if (fileToReplace == null && !libraryJarDescriptor.shouldExist) {
return;
}
KotlinPaths paths = PathUtil.getKotlinPathsForIdeaPlugin();
File jarPath;
switch (libraryJarDescriptor) {
case RUNTIME_JAR: jarPath = paths.getRuntimePath(); break;
case REFLECT_JAR: jarPath = paths.getReflectPath(); break;
case RUNTIME_SRC_JAR: jarPath = paths.getRuntimeSourcesPath(); break;
case JS_STDLIB_JAR: jarPath = paths.getJsStdLibJarPath(); break;
case JS_STDLIB_SRC_JAR: jarPath = paths.getJsStdLibSrcJarPath(); break;
default: jarPath = null; break;
}
if (!jarPath.exists()) {
showRuntimeJarNotFoundDialog(project, libraryJarDescriptor.jarName).run();
return;
}
VirtualFile localJar = getLocalJar(fileToReplace);
assert localJar != null;
replaceFile(jarPath, localJar);
}
@NotNull
public static Collection<Library> findKotlinLibraries(@NotNull Project project) {
Set<Library> libraries = Sets.newHashSet();
for (Module module : ModuleManager.getInstance(project).getModules()) {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
for (OrderEntry entry : moduleRootManager.getOrderEntries()) {
if (entry instanceof LibraryOrderEntry) {
LibraryOrderEntry libraryOrderEntry = (LibraryOrderEntry) entry;
Library library = libraryOrderEntry.getLibrary();
if (library == null) {
continue;
}
libraries.add(library);
// TODO: search js libraries as well
} }
} }
} }
return libraries;
}
private enum LibraryJarDescriptor {
RUNTIME_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JAR, true),
REFLECT_JAR(PathUtil.KOTLIN_JAVA_REFLECT_JAR, false),
RUNTIME_SRC_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_SRC_JAR, false),
JS_STDLIB_JAR(PathUtil.JS_LIB_JAR_NAME, true),
JS_STDLIB_SRC_JAR(PathUtil.JS_LIB_SRC_JAR_NAME, false);
public final String jarName;
private final boolean shouldExist;
LibraryJarDescriptor(@NotNull String jarName, boolean shouldExist) {
this.jarName = jarName;
this.shouldExist = shouldExist;
}
}
@NotNull
public static String bundledRuntimeVersion() {
return bundledRuntimeVersion(KotlinPluginUtil.getPluginVersion());
}
@NotNull
public static String bundledRuntimeVersion(@NotNull String pluginVersion) {
int placeToSplit = -1;
int ideaPatternIndex = StringUtil.indexOf(pluginVersion, "Idea");
if (ideaPatternIndex >= 2 && Character.isDigit(pluginVersion.charAt(ideaPatternIndex - 2))) {
placeToSplit = ideaPatternIndex - 1;
}
int ijPatternIndex = StringUtil.indexOf(pluginVersion, "IJ");
if (ijPatternIndex >= 2 && Character.isDigit(pluginVersion.charAt(ijPatternIndex - 2))) {
placeToSplit = ijPatternIndex - 1;
}
if (placeToSplit == -1) {
for (int i = 1; i < pluginVersion.length(); i++) {
char ch = pluginVersion.charAt(i);
if (Character.isLetter(ch) && pluginVersion.charAt(i - 1) == '.') {
placeToSplit = i - 1;
break;
}
}
}
return placeToSplit != - 1 ? pluginVersion.substring(0, placeToSplit) : pluginVersion;
}
@Nullable
public static VirtualFile getLocalJar(@Nullable VirtualFile kotlinRuntimeJar) {
if (kotlinRuntimeJar == null) return null;
VirtualFile localJarFile = JarFileSystem.getInstance().getVirtualFileForJar(kotlinRuntimeJar);
if (localJarFile != null) {
return localJarFile;
}
return kotlinRuntimeJar;
}
static void replaceFile(File updatedFile, VirtualFile replacedJarFile) {
try {
VirtualFile replacedFile = getLocalFile(replacedJarFile);
String localPath = getLocalPath(replacedFile);
assert localPath != null : "Should be called for replacing valid root file: " + replacedJarFile;
File libraryJarPath = new File(localPath);
if (FileUtil.filesEqual(updatedFile, libraryJarPath)) {
return;
}
FileUtil.copy(updatedFile, libraryJarPath);
replacedFile.refresh(false, true);
}
catch (IOException e) {
throw new AssertionError(e);
}
}
@NotNull
private static Collection<VirtualFile> getLibraryRootsWithAbiIncompatibleVersion(
@NotNull Project project,
@NotNull ScalarIndexExtension<BinaryVersion> index,
@NotNull Function1<Module, Boolean> checkModule,
@NotNull Function1<BinaryVersion, Boolean> checkVersion
) {
ID<BinaryVersion, Void> id = index.getName();
Module[] modules = ModuleManager.getInstance(project).getModules();
List<Module> modulesToCheck = ArraysKt.filter(modules, checkModule);
if (modulesToCheck.isEmpty()) return Collections.emptyList();
Collection<BinaryVersion> versions = collectAllKeys(id, modulesToCheck);
Set<BinaryVersion> badVersions = Sets.newHashSet(CollectionsKt.filter(versions, checkVersion));
Set<VirtualFile> badRoots = Sets.newHashSet();
ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(project);
for (BinaryVersion version : badVersions) {
Collection<VirtualFile> indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project));
for (VirtualFile indexedFile : indexedFiles) {
VirtualFile libraryRoot = fileIndex.getClassRootForFile(indexedFile);
assert libraryRoot != null : "Only library roots were requested, " +
"and only class files should be indexed with KotlinAbiVersionIndex key. " +
"File: " + indexedFile.getPath();
badRoots.add(getLocalFile(libraryRoot));
}
}
return badRoots;
}
@NotNull
private static <T> Collection<T> collectAllKeys(@NotNull ID<T, Void> id, @NotNull List<Module> modules) {
Set<T> allKeys = new HashSet<T>();
for (Module module : modules) {
GlobalSearchScope scope = GlobalSearchScope.moduleWithLibrariesScope(module);
FileBasedIndex.getInstance().processAllKeys(id, new CommonProcessors.CollectProcessor<T>(allKeys), scope, null);
}
return allKeys;
} }
} }
private fun updateJar(
project: Project,
fileToReplace: VirtualFile?,
libraryJarDescriptor: LibraryJarDescriptor) {
if (fileToReplace == null && !libraryJarDescriptor.shouldExist) {
return
}
val paths = PathUtil.getKotlinPathsForIdeaPlugin()
val jarPath: File = when (libraryJarDescriptor) {
LibraryJarDescriptor.RUNTIME_JAR -> paths.runtimePath
LibraryJarDescriptor.REFLECT_JAR -> paths.reflectPath
LibraryJarDescriptor.RUNTIME_SRC_JAR -> paths.runtimeSourcesPath
LibraryJarDescriptor.JS_STDLIB_JAR -> paths.jsStdLibJarPath
LibraryJarDescriptor.JS_STDLIB_SRC_JAR -> paths.jsStdLibSrcJarPath
}
if (!jarPath.exists()) {
showRuntimeJarNotFoundDialog(project, libraryJarDescriptor.jarName).run()
return
}
val localJar = getLocalJar(fileToReplace)
assert(localJar != null)
replaceFile(jarPath, localJar!!)
}
fun findKotlinLibraries(project: Project): Collection<Library> {
val libraries = Sets.newHashSet<Library>()
for (module in ModuleManager.getInstance(project).modules) {
val moduleRootManager = ModuleRootManager.getInstance(module)
for (entry in moduleRootManager.orderEntries) {
if (entry is LibraryOrderEntry) {
val library = entry.library ?: continue
libraries.add(library)
// TODO: search js libraries as well
}
}
}
return libraries
}
private enum class LibraryJarDescriptor private constructor(val jarName: String, val shouldExist: Boolean) {
RUNTIME_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_JAR, true),
REFLECT_JAR(PathUtil.KOTLIN_JAVA_REFLECT_JAR, false),
RUNTIME_SRC_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_SRC_JAR, false),
JS_STDLIB_JAR(PathUtil.JS_LIB_JAR_NAME, true),
JS_STDLIB_SRC_JAR(PathUtil.JS_LIB_SRC_JAR_NAME, false)
}
@JvmOverloads fun bundledRuntimeVersion(pluginVersion: String = KotlinPluginUtil.getPluginVersion()): String {
var placeToSplit = -1
val ideaPatternIndex = StringUtil.indexOf(pluginVersion, "Idea")
if (ideaPatternIndex >= 2 && Character.isDigit(pluginVersion[ideaPatternIndex - 2])) {
placeToSplit = ideaPatternIndex - 1
}
val ijPatternIndex = StringUtil.indexOf(pluginVersion, "IJ")
if (ijPatternIndex >= 2 && Character.isDigit(pluginVersion[ijPatternIndex - 2])) {
placeToSplit = ijPatternIndex - 1
}
if (placeToSplit == -1) {
for (i in 1..pluginVersion.length - 1) {
val ch = pluginVersion[i]
if (Character.isLetter(ch) && pluginVersion[i - 1] == '.') {
placeToSplit = i - 1
break
}
}
}
return if (placeToSplit != -1) pluginVersion.substring(0, placeToSplit) else pluginVersion
}
fun getLocalJar(kotlinRuntimeJar: VirtualFile?): VirtualFile? {
if (kotlinRuntimeJar == null) return null
val localJarFile = JarFileSystem.getInstance().getVirtualFileForJar(kotlinRuntimeJar)
if (localJarFile != null) {
return localJarFile
}
return kotlinRuntimeJar
}
internal fun replaceFile(updatedFile: File, replacedJarFile: VirtualFile) {
try {
val replacedFile = getLocalFile(replacedJarFile)
val localPath = getLocalPath(replacedFile)
assert(localPath != null) { "Should be called for replacing valid root file: " + replacedJarFile }
val libraryJarPath = File(localPath)
if (FileUtil.filesEqual(updatedFile, libraryJarPath)) {
return
}
FileUtil.copy(updatedFile, libraryJarPath)
replacedFile.refresh(false, true)
}
catch (e: IOException) {
throw AssertionError(e)
}
}
private fun getLibraryRootsWithAbiIncompatibleVersion(
project: Project,
index: ScalarIndexExtension<BinaryVersion>,
checkModule: (Module) -> Boolean,
checkVersion: (BinaryVersion) -> Boolean): Collection<VirtualFile> {
val id = index.name
val modules = ModuleManager.getInstance(project).modules
val modulesToCheck = modules.filter(checkModule)
if (modulesToCheck.isEmpty()) return emptyList()
val versions = collectAllKeys(id, modulesToCheck)
val badVersions = versions.filter(checkVersion).toHashSet()
val badRoots = Sets.newHashSet<VirtualFile>()
val fileIndex = ProjectFileIndex.SERVICE.getInstance(project)
for (version in badVersions) {
val indexedFiles = FileBasedIndex.getInstance().getContainingFiles(
id, version, ProjectScope.getLibrariesScope(project))
for (indexedFile in indexedFiles) {
val libraryRoot = fileIndex.getClassRootForFile(indexedFile)
assert(libraryRoot != null) { "Only library roots were requested, " + "and only class files should be indexed with KotlinAbiVersionIndex key. " + "File: " + indexedFile.path }
badRoots.add(getLocalFile(libraryRoot!!))
}
}
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
}
@@ -132,7 +132,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
if ("update".equals(event.getDescription())) { if ("update".equals(event.getDescription())) {
Collection<VersionedLibrary> versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject); Collection<VersionedLibrary> versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject);
Collection<Library> outdatedLibraries = extractLibraries(versionedOutdatedLibraries); Collection<Library> outdatedLibraries = extractLibraries(versionedOutdatedLibraries);
KotlinRuntimeLibraryUtil.updateLibraries(myProject, outdatedLibraries); KotlinRuntimeLibraryUtilKt.updateLibraries(myProject, outdatedLibraries);
suggestDeleteKotlinJsIfNeeded(outdatedLibraries); suggestDeleteKotlinJsIfNeeded(outdatedLibraries);
} }
else if ("ignore".equals(event.getDescription())) { else if ("ignore".equals(event.getDescription())) {
@@ -232,7 +232,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
private static Collection<VersionedLibrary> findOutdatedKotlinLibraries(@NotNull Project project) { private static Collection<VersionedLibrary> findOutdatedKotlinLibraries(@NotNull Project project) {
List<VersionedLibrary> outdatedLibraries = Lists.newArrayList(); List<VersionedLibrary> outdatedLibraries = Lists.newArrayList();
for (Library library : KotlinRuntimeLibraryUtil.findKotlinLibraries(project)) { for (Library library : KotlinRuntimeLibraryUtilKt.findKotlinLibraries(project)) {
LibraryVersionProperties libraryVersionProperties = LibraryVersionProperties libraryVersionProperties =
LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library); LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library);
if (libraryVersionProperties == null) { if (libraryVersionProperties == null) {
@@ -244,7 +244,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
} }
String libraryVersion = libraryVersionProperties.getVersionString(); String libraryVersion = libraryVersionProperties.getVersionString();
String runtimeVersion = KotlinRuntimeLibraryUtil.bundledRuntimeVersion(); String runtimeVersion = KotlinRuntimeLibraryUtilKt.bundledRuntimeVersion();
boolean isOutdated = isRuntimeOutdated(libraryVersion, runtimeVersion); boolean isOutdated = isRuntimeOutdated(libraryVersion, runtimeVersion);
@@ -103,13 +103,13 @@ public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifi
private EditorNotificationPanel doCreate(final Collection<VirtualFile> badRoots) { private EditorNotificationPanel doCreate(final Collection<VirtualFile> badRoots) {
EditorNotificationPanel answer = new ErrorNotificationPanel(); EditorNotificationPanel answer = new ErrorNotificationPanel();
Collection<Library> kotlinLibraries = KotlinRuntimeLibraryUtil.findKotlinLibraries(project); Collection<Library> kotlinLibraries = KotlinRuntimeLibraryUtilKt.findKotlinLibraries(project);
final Collection<Library> badRuntimeLibraries = Collections2.filter(kotlinLibraries, new Predicate<Library>() { final Collection<Library> badRuntimeLibraries = Collections2.filter(kotlinLibraries, new Predicate<Library>() {
@Override @Override
public boolean apply(@Nullable Library library) { public boolean apply(@Nullable Library library) {
assert library != null : "library should be non null"; assert library != null : "library should be non null";
VirtualFile runtimeJar = KotlinRuntimeLibraryUtil.getLocalJar(JavaRuntimePresentationProvider.getRuntimeJar(library)); VirtualFile runtimeJar = KotlinRuntimeLibraryUtilKt.getLocalJar(JavaRuntimePresentationProvider.getRuntimeJar(library));
VirtualFile jsLibJar = KotlinRuntimeLibraryUtil.getLocalJar(JSLibraryStdPresentationProvider.getJsStdLibJar(library)); VirtualFile jsLibJar = KotlinRuntimeLibraryUtilKt.getLocalJar(JSLibraryStdPresentationProvider.getJsStdLibJar(library));
return badRoots.contains(runtimeJar) || badRoots.contains(jsLibJar); return badRoots.contains(runtimeJar) || badRoots.contains(jsLibJar);
} }
}); });
@@ -130,7 +130,7 @@ public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifi
answer.createActionLabel(actionLabelText, new Runnable() { answer.createActionLabel(actionLabelText, new Runnable() {
@Override @Override
public void run() { public void run() {
KotlinRuntimeLibraryUtil.updateLibraries(project, badRuntimeLibraries); KotlinRuntimeLibraryUtilKt.updateLibraries(project, badRuntimeLibraries);
} }
}); });
if (otherBadRootsCount > 0) { if (otherBadRootsCount > 0) {
@@ -278,8 +278,8 @@ public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifi
@NotNull @NotNull
private static Collection<VirtualFile> collectBadRoots(@NotNull Project project) { private static Collection<VirtualFile> collectBadRoots(@NotNull Project project) {
Collection<VirtualFile> badJVMRoots = KotlinRuntimeLibraryUtil.getLibraryRootsWithAbiIncompatibleKotlinClasses(project); Collection<VirtualFile> badJVMRoots = KotlinRuntimeLibraryUtilKt.getLibraryRootsWithAbiIncompatibleKotlinClasses(project);
Collection<VirtualFile> badJSRoots = KotlinRuntimeLibraryUtil.getLibraryRootsWithAbiIncompatibleForKotlinJs(project); Collection<VirtualFile> badJSRoots = KotlinRuntimeLibraryUtilKt.getLibraryRootsWithAbiIncompatibleForKotlinJs(project);
if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return Collections.emptyList(); if (badJVMRoots.isEmpty() && badJSRoots.isEmpty()) return Collections.emptyList();
@@ -17,8 +17,8 @@
package org.jetbrains.kotlin.idea package org.jetbrains.kotlin.idea
import junit.framework.TestCase import junit.framework.TestCase
import org.jetbrains.kotlin.idea.versions.KotlinRuntimeLibraryUtil
import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification
import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion
import org.junit.Assert import org.junit.Assert
@@ -87,15 +87,15 @@ class KotlinRuntimeLibraryUtilTest : TestCase() {
private fun outdated(plugin: String, library: String) { private fun outdated(plugin: String, library: String) {
Assert.assertTrue("Should be outdated: plugin=$plugin, library=$library", Assert.assertTrue("Should be outdated: plugin=$plugin, library=$library",
OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, KotlinRuntimeLibraryUtil.bundledRuntimeVersion(plugin))) OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, bundledRuntimeVersion(plugin)))
} }
private fun notOutdated(plugin: String, library: String) { private fun notOutdated(plugin: String, library: String) {
Assert.assertFalse("Should NOT be outdated: plugin=$plugin, library=$library", Assert.assertFalse("Should NOT be outdated: plugin=$plugin, library=$library",
OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, KotlinRuntimeLibraryUtil.bundledRuntimeVersion(plugin))) OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, bundledRuntimeVersion(plugin)))
} }
private fun test(version: String, expected: String) { private fun test(version: String, expected: String) {
Assert.assertEquals(expected, KotlinRuntimeLibraryUtil.bundledRuntimeVersion(version)) Assert.assertEquals(expected, bundledRuntimeVersion(version))
} }
} }