Update several libraries in project all at once
This commit is contained in:
+22
-4
@@ -18,8 +18,10 @@ package org.jetbrains.jet.plugin.framework;
|
||||
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.roots.libraries.*;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -34,17 +36,33 @@ public abstract class CachingLibraryPresentationProvider<T extends LibraryProper
|
||||
}
|
||||
|
||||
public boolean isDetected(@NotNull List<VirtualFile> classesRoots) {
|
||||
return cachedDetect(classesRoots, getKind());
|
||||
return cachedDetect(classesRoots, getKind()) != null;
|
||||
}
|
||||
|
||||
private static boolean cachedDetect(@NotNull List<VirtualFile> classesRoots, @NotNull final LibraryKind libraryKind) {
|
||||
return !LibraryDetectionManager.getInstance().processProperties(
|
||||
@Nullable
|
||||
public T getLibraryProperties(@NotNull Library library) {
|
||||
return cachedDetect(Arrays.asList(library.getFiles(OrderRootType.CLASSES)), getKind());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private T cachedDetect(@NotNull List<VirtualFile> classesRoots, @NotNull final LibraryKind libraryKind) {
|
||||
final Ref<T> propertiesRef = new Ref<T>();
|
||||
|
||||
LibraryDetectionManager.getInstance().processProperties(
|
||||
classesRoots,
|
||||
new LibraryDetectionManager.LibraryPropertiesProcessor() {
|
||||
@Override
|
||||
public <P extends LibraryProperties> boolean processProperties(@NotNull LibraryKind processedKind, @NotNull P properties) {
|
||||
return !libraryKind.equals(processedKind);
|
||||
if (libraryKind.equals(processedKind)) {
|
||||
//noinspection unchecked
|
||||
propertiesRef.set((T)properties);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return propertiesRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.jet.plugin.framework;
|
||||
|
||||
import com.intellij.framework.library.LibraryVersionProperties;
|
||||
import com.intellij.openapi.roots.OrderRootType;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.roots.libraries.LibraryPresentationProvider;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -26,6 +28,7 @@ import org.jetbrains.jet.plugin.versions.KotlinRuntimeLibraryUtil;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaRuntimePresentationProvider extends CachingLibraryPresentationProvider<LibraryVersionProperties> {
|
||||
@@ -46,12 +49,27 @@ public class JavaRuntimePresentationProvider extends CachingLibraryPresentationP
|
||||
@Nullable
|
||||
@Override
|
||||
public LibraryVersionProperties detect(@NotNull List<VirtualFile> classesRoots) {
|
||||
VirtualFile stdJar = getRuntimeJar(classesRoots);
|
||||
if (stdJar != null) {
|
||||
return new LibraryVersionProperties(KotlinRuntimeLibraryUtil.getLibraryVersion(stdJar));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile getRuntimeJar(@NotNull List<VirtualFile> classesRoots) {
|
||||
for (VirtualFile root : classesRoots) {
|
||||
if (root.getName().equals(PathUtil.KOTLIN_JAVA_RUNTIME_JAR)) {
|
||||
return new LibraryVersionProperties(KotlinRuntimeLibraryUtil.getLibraryVersion(root));
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile getRuntimeJar(@NotNull Library library) {
|
||||
return getRuntimeJar(Arrays.asList(library.getFiles(OrderRootType.CLASSES)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,10 +150,7 @@ public class KotlinRuntimeLibraryUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void updateRuntime(
|
||||
@NotNull final Project project,
|
||||
@NotNull final Runnable jarNotFoundHandler
|
||||
) {
|
||||
public static void updateRuntime(@NotNull final Project project, @NotNull final Runnable jarNotFoundHandler) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -163,28 +160,14 @@ public class KotlinRuntimeLibraryUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualFile runtimeJar = getLocalKotlinRuntimeJar(project);
|
||||
assert runtimeJar != null;
|
||||
VirtualFile localJar = getLocalKotlinRuntimeJar(project);
|
||||
assert localJar != null;
|
||||
|
||||
try {
|
||||
File libraryJarPath = new File(runtimeJar.getPath());
|
||||
assert !FileUtil.filesEqual(runtimePath, libraryJarPath) : "Shouldn't be called for updating same file";
|
||||
|
||||
FileUtil.copy(runtimePath, libraryJarPath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
runtimeJar.refresh(true, true);
|
||||
replaceFile(runtimePath, localJar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getRuntimeVersion(@NotNull Project project) {
|
||||
return getLibraryVersion(getKotlinRuntimeJar(project));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getLibraryVersion(@Nullable VirtualFile kotlinStdJar) {
|
||||
if (kotlinStdJar == null) return null;
|
||||
@@ -207,7 +190,6 @@ public class KotlinRuntimeLibraryUtil {
|
||||
VirtualFile virtualFile = markerClass.getContainingFile().getVirtualFile();
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
|
||||
ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
|
||||
return projectFileIndex.getClassRootForFile(virtualFile);
|
||||
}
|
||||
@@ -223,4 +205,24 @@ public class KotlinRuntimeLibraryUtil {
|
||||
}
|
||||
return kotlinRuntimeJar;
|
||||
}
|
||||
|
||||
static void replaceFile(File updatedFile, VirtualFile replacedFile) {
|
||||
try {
|
||||
String localPath = com.intellij.util.PathUtil.getLocalPath(replacedFile);
|
||||
assert localPath != null;
|
||||
|
||||
File libraryJarPath = new File(localPath);
|
||||
|
||||
if (FileUtil.filesEqual(updatedFile, libraryJarPath)) {
|
||||
throw new IllegalArgumentException("Shouldn't be called for updating same file: " + updatedFile);
|
||||
}
|
||||
|
||||
FileUtil.copy(updatedFile, libraryJarPath);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
replacedFile.refresh(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
+148
-16
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.jetbrains.jet.plugin.versions;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.framework.library.LibraryVersionProperties;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
@@ -23,14 +26,30 @@ import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.AbstractProjectComponent;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.OrderEntry;
|
||||
import com.intellij.openapi.roots.libraries.Library;
|
||||
import com.intellij.openapi.startup.StartupManager;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.text.VersionComparatorUtil;
|
||||
import com.sun.istack.internal.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.plugin.JetPluginUtil;
|
||||
import org.jetbrains.jet.plugin.framework.JavaRuntimePresentationProvider;
|
||||
import org.jetbrains.jet.utils.PathUtil;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent {
|
||||
private static final String SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version";
|
||||
@@ -39,41 +58,82 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
super(project);
|
||||
}
|
||||
|
||||
private static class VersionedLibrary extends Pair<Library, String> {
|
||||
public VersionedLibrary(@NotNull Library library, @Nullable String version) {
|
||||
super(library, version);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Library getLibrary() {
|
||||
return first;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getVersion() {
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectOpened() {
|
||||
if (ApplicationManager.getApplication().isInternal()) return;
|
||||
|
||||
StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String runtimeVersion = KotlinRuntimeLibraryUtil.getRuntimeVersion(myProject);
|
||||
final String pluginVersion = JetPluginUtil.getPluginVersion();
|
||||
if (runtimeVersion == null) return; // runtime is not present in project
|
||||
if ("@snapshot@".equals(pluginVersion)) return; // plugin is run from sources, can't compare versions
|
||||
|
||||
// user already clicked suppress
|
||||
if (pluginVersion.equals(PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME))) return;
|
||||
|
||||
boolean isRuntimeOutdated = "snapshot".equals(runtimeVersion)
|
||||
|| KotlinRuntimeLibraryUtil.UNKNOWN_VERSION.equals(runtimeVersion)
|
||||
|| runtimeVersion.startsWith("internal-") != pluginVersion.startsWith("internal-")
|
||||
|| VersionComparatorUtil.compare(pluginVersion, runtimeVersion) > 0;
|
||||
Collection<VersionedLibrary> versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject, pluginVersion);
|
||||
if (versionedOutdatedLibraries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRuntimeOutdated) return;
|
||||
final Collection<Library> outdatedLibraries = extractLibraries(versionedOutdatedLibraries);
|
||||
|
||||
String message = String.format("<p>Your version of Kotlin runtime library is %s, while plugin version is %s." +
|
||||
" Runtime library should be updated to avoid compatibility problems.</p>" +
|
||||
"<p><a href=\"update\">Update Runtime</a> <a href=\"ignore\">Ignore</a></p>",
|
||||
KotlinRuntimeLibraryUtil.UNKNOWN_VERSION.equals(runtimeVersion)
|
||||
? "older than 0.1.2296"
|
||||
: runtimeVersion, pluginVersion);
|
||||
Notifications.Bus.notify(new Notification("Outdated Kotlin Runtime", "Outdated Kotlin Runtime",
|
||||
message,
|
||||
String message;
|
||||
if (versionedOutdatedLibraries.size() == 1) {
|
||||
VersionedLibrary versionedLibrary = versionedOutdatedLibraries.iterator().next();
|
||||
|
||||
String version = versionedLibrary.getVersion();
|
||||
String readableVersion = version == null ? "unknown" : version;
|
||||
String libraryName = versionedLibrary.getLibrary().getName();
|
||||
|
||||
message = String.format(
|
||||
"<p>Your version of Kotlin runtime in '%s' library is %s, while plugin version is %s.</p>" +
|
||||
"<p>Runtime library should be updated to avoid compatibility problems.</p>" +
|
||||
"<p><a href=\"update\">Update Runtime</a> <a href=\"ignore\">Ignore</a></p>",
|
||||
libraryName,
|
||||
readableVersion,
|
||||
pluginVersion);
|
||||
}
|
||||
else {
|
||||
String libraryNames = StringUtil.join(outdatedLibraries, new Function<Library, String>() {
|
||||
@Override
|
||||
public String fun(Library library) {
|
||||
return library.getName();
|
||||
}
|
||||
}, ", ");
|
||||
|
||||
message = String.format(
|
||||
"<p>Version of Kotlin runtime is outdated in several libraries (%s). Plugin version is %s.</p>" +
|
||||
"<p>Runtime libraries should be updated to avoid compatibility problems.</p>" +
|
||||
"<p><a href=\"update\">Update All</a> <a href=\"ignore\">Ignore</a></p>",
|
||||
libraryNames,
|
||||
pluginVersion);
|
||||
}
|
||||
|
||||
|
||||
Notifications.Bus.notify(new Notification("Outdated Kotlin Runtime", "Outdated Kotlin Runtime", message,
|
||||
NotificationType.WARNING, new NotificationListener() {
|
||||
@Override
|
||||
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
|
||||
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
if ("update".equals(event.getDescription())) {
|
||||
KotlinRuntimeLibraryUtil.updateRuntime(myProject, showRuntimeJarNotFoundDialog(myProject));
|
||||
updateKotlinLibraries(outdatedLibraries, showRuntimeJarNotFoundDialog(myProject));
|
||||
}
|
||||
else if ("ignore".equals(event.getDescription())) {
|
||||
PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion);
|
||||
@@ -89,6 +149,78 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
|
||||
});
|
||||
}
|
||||
|
||||
private static Collection<Library> extractLibraries(Collection<VersionedLibrary> libraries) {
|
||||
return Collections2.transform(libraries, new com.google.common.base.Function<VersionedLibrary, Library>() {
|
||||
@Override
|
||||
public Library apply(@Nullable VersionedLibrary versionedLibrary) {
|
||||
assert versionedLibrary != null;
|
||||
return versionedLibrary.getLibrary();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void updateKotlinLibraries(Collection<Library> libraries, @NotNull Runnable jarNotFoundHandler) {
|
||||
File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath();
|
||||
if (!runtimePath.exists()) {
|
||||
jarNotFoundHandler.run();
|
||||
return;
|
||||
}
|
||||
|
||||
for (Library library : libraries) {
|
||||
VirtualFile libraryJar = JavaRuntimePresentationProvider.getRuntimeJar(library);
|
||||
assert JavaRuntimePresentationProvider.getInstance().isDetected(library) && libraryJar != null : "Only java runtime libraries are expected";
|
||||
|
||||
KotlinRuntimeLibraryUtil.replaceFile(runtimePath, libraryJar);
|
||||
}
|
||||
}
|
||||
|
||||
private static Collection<VersionedLibrary> findOutdatedKotlinLibraries(Project project, String pluginVersion) {
|
||||
List<VersionedLibrary> outdatedLibraries = Lists.newArrayList();
|
||||
|
||||
for (VersionedLibrary versionedLibrary : findKotlinLibraries(project)) {
|
||||
String libraryVersion = versionedLibrary.getVersion();
|
||||
|
||||
boolean isOutdated = "snapshot".equals(libraryVersion)
|
||||
|| libraryVersion == null
|
||||
|| libraryVersion.startsWith("internal-") != pluginVersion.startsWith("internal-")
|
||||
|| VersionComparatorUtil.compare(pluginVersion, libraryVersion) > 0;
|
||||
|
||||
if (isOutdated) {
|
||||
outdatedLibraries.add(versionedLibrary);
|
||||
}
|
||||
}
|
||||
|
||||
return outdatedLibraries;
|
||||
}
|
||||
|
||||
private static Collection<VersionedLibrary> findKotlinLibraries(Project project) {
|
||||
List<VersionedLibrary> libraries = Lists.newArrayList();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
LibraryVersionProperties javaRuntimeProperties = JavaRuntimePresentationProvider.getInstance().getLibraryProperties(library);
|
||||
if (javaRuntimeProperties != null) {
|
||||
libraries.add(new VersionedLibrary(library, javaRuntimeProperties.getVersionString()));
|
||||
}
|
||||
|
||||
// TODO: search js libraries as well
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return libraries;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Runnable showRuntimeJarNotFoundDialog(@NotNull final Project project) {
|
||||
return new Runnable() {
|
||||
|
||||
Reference in New Issue
Block a user