Update sources when updating kotlin runtime

This commit is contained in:
Natalia.Ukhorskaya
2013-10-03 09:48:09 +04:00
parent 3bb1d3854c
commit b6f3baa485
5 changed files with 155 additions and 140 deletions
@@ -1,13 +1,16 @@
package org.jetbrains.jet.plugin.configuration;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.impl.scopes.LibraryScope;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.plugin.framework.JavaRuntimeLibraryDescription;
import org.jetbrains.jet.plugin.framework.JavaRuntimePresentationProvider;
import org.jetbrains.jet.plugin.framework.KotlinFrameworkDetector;
import org.jetbrains.jet.plugin.framework.ui.CreateJavaLibraryDialogWithModules;
import org.jetbrains.jet.plugin.versions.KotlinRuntimeLibraryUtil;
@@ -16,6 +19,8 @@ import org.jetbrains.jet.utils.PathUtil;
import java.io.File;
import java.util.List;
import static org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils.showInfoNotification;
public class KotlinJavaModuleConfigurator extends KotlinWithLibraryConfigurator {
public static final String NAME = "java";
@@ -94,6 +99,51 @@ public class KotlinJavaModuleConfigurator extends KotlinWithLibraryConfigurator
return assertFileExists(getKotlinPaths().getRuntimeSourcesPath());
}
public void copySourcesToPathFromLibrary(@NotNull Library library) {
String dirToJarFromLibrary = getPathFromLibrary(library, OrderRootType.SOURCES);
assert dirToJarFromLibrary != null : "Directory to file from library should be non null";
copyFileToDir(getExistedSourcesJarFile(), dirToJarFromLibrary);
}
public boolean changeOldSourcesPathIfNeeded(@NotNull Library library) {
if (!removeOldSourcesRootIfNeeded(library)) {
return false;
}
String parentDir = getPathFromLibrary(library, OrderRootType.CLASSES);
assert parentDir != null : "Parent dir for classes jar should exists for Kotlin library";
return addSourcesToLibraryIfNeeded(library, getFileInDir(getSourcesJarName(), parentDir));
}
private static boolean removeOldSourcesRootIfNeeded(@NotNull Library library) {
VirtualFile runtimeJarPath = JavaRuntimePresentationProvider.getRuntimeJar(library);
if (runtimeJarPath == null) {
return false;
}
String oldLibrarySourceRoot = runtimeJarPath.getUrl() + "src";
String[] librarySourceRoots = library.getUrls(OrderRootType.SOURCES);
for (String sourceRoot : librarySourceRoots) {
if (sourceRoot.equals(oldLibrarySourceRoot)) {
final Library.ModifiableModel model = library.getModifiableModel();
model.removeRoot(oldLibrarySourceRoot, OrderRootType.SOURCES);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
model.commit();
}
});
showInfoNotification("Source root '" + oldLibrarySourceRoot + "' was removed for " + library.getName() + " library");
return true;
}
}
return false;
}
@Override
protected boolean isKotlinLibrary(@NotNull Project project, @NotNull Library library) {
if (super.isKotlinLibrary(project, library)) {
@@ -4,7 +4,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.OrderEntryUtil;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar;
@@ -192,7 +191,11 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf
@Nullable
protected String getPathFromLibrary(@NotNull Project project, @NotNull OrderRootType type) {
Library library = getKotlinLibrary(project);
return getPathFromLibrary(getKotlinLibrary(project), type);
}
@Nullable
protected static String getPathFromLibrary(@Nullable Library library, @NotNull OrderRootType type) {
if (library == null) return null;
String[] libraryFiles = library.getUrls(type);
@@ -209,11 +212,11 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf
return parentDir;
}
private static void addSourcesToLibraryIfNeeded(@NotNull Library library, @NotNull File file) {
protected static boolean addSourcesToLibraryIfNeeded(@NotNull Library library, @NotNull File file) {
String[] librarySourceRoots = library.getUrls(OrderRootType.SOURCES);
String librarySourceRoot = VfsUtil.getUrlForLibraryRoot(file);
for (String sourceRoot : librarySourceRoots) {
if (sourceRoot.equals(librarySourceRoot)) return;
if (sourceRoot.equals(librarySourceRoot)) return false;
}
final Library.ModifiableModel model = library.getModifiableModel();
@@ -226,7 +229,8 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf
}
});
showInfoNotification(library.getName() + " library was configured");
showInfoNotification("Source root '" + librarySourceRoot + "' was added to " + library.getName() + " library");
return true;
}
private void addLibraryToModuleIfNeeded(Module module) {
@@ -19,16 +19,15 @@ package org.jetbrains.jet.plugin.versions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
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.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.roots.AnnotationOrderRootType;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
@@ -50,12 +49,14 @@ import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils;
import org.jetbrains.jet.plugin.configuration.KotlinJavaModuleConfigurator;
import org.jetbrains.jet.plugin.framework.JavaRuntimePresentationProvider;
import org.jetbrains.jet.utils.KotlinPaths;
import org.jetbrains.jet.utils.PathUtil;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.intellij.util.PathUtil.getLocalFile;
@@ -160,17 +161,36 @@ public class KotlinRuntimeLibraryUtil {
return null;
}
public static void updateRuntime(@NotNull final Project project) {
public static void updateLibraries(
@NotNull final Project project,
@NotNull final Collection<Library> libraries
) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
updateJar(project, LibraryJarDescriptor.RUNTIME_JAR);
updateJar(project, LibraryJarDescriptor.RUNTIME_SRC_JAR);
KotlinJavaModuleConfigurator configurator = (KotlinJavaModuleConfigurator)
ConfigureKotlinInProjectUtils.getConfiguratorByName(KotlinJavaModuleConfigurator.NAME);
assert configurator != null : "Configurator with given name doesn't exists: " + KotlinJavaModuleConfigurator.NAME;
for (Library library : libraries) {
updateJar(project, JavaRuntimePresentationProvider.getRuntimeJar(library), LibraryJarDescriptor.RUNTIME_JAR);
if (configurator.changeOldSourcesPathIfNeeded(library)) {
configurator.copySourcesToPathFromLibrary(library);
}
else {
updateJar(project, JavaRuntimePresentationProvider.getRuntimeSrcJar(library), LibraryJarDescriptor.RUNTIME_SRC_JAR);
}
}
}
});
}
private static void updateJar(@NotNull Project project, @NotNull LibraryJarDescriptor libraryJarDescriptor) {
private static void updateJar(@NotNull Project project, @Nullable VirtualFile fileToReplace, @NotNull LibraryJarDescriptor libraryJarDescriptor) {
if (fileToReplace == null && !libraryJarDescriptor.shouldExists) {
return;
}
KotlinPaths paths = PathUtil.getKotlinPathsForIdeaPlugin();
File runtimePath = null;
switch (libraryJarDescriptor) {
@@ -183,22 +203,38 @@ public class KotlinRuntimeLibraryUtil {
return;
}
VirtualFile jar = null;
switch (libraryJarDescriptor) {
case RUNTIME_JAR: jar = getKotlinRuntimeJar(project); break;
case RUNTIME_SRC_JAR: jar = getKotlinRuntimeSourcesJar(project); break;
}
if (jar == null && !libraryJarDescriptor.shouldExists) {
return;
}
VirtualFile localJar = getLocalJar(jar);
VirtualFile localJar = getLocalJar(fileToReplace);
assert localJar != null;
replaceFile(runtimePath, 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),
RUNTIME_SRC_JAR(PathUtil.KOTLIN_JAVA_RUNTIME_SRC_JAR, false);
@@ -213,43 +249,7 @@ public class KotlinRuntimeLibraryUtil {
}
@Nullable
private static VirtualFile getKotlinRuntimeJar(@NotNull Project project) {
PsiClass markerClass = getKotlinRuntimeMarkerClass(ProjectScope.getAllScope(project));
if (markerClass == null) return null;
VirtualFile virtualFile = markerClass.getContainingFile().getVirtualFile();
if (virtualFile == null) return null;
ProjectFileIndex projectFileIndex = ProjectFileIndex.SERVICE.getInstance(project);
return projectFileIndex.getClassRootForFile(virtualFile);
}
@Nullable
private static VirtualFile getKotlinRuntimeSourcesJar(@NotNull Project project) {
KotlinJavaModuleConfigurator configurator = (KotlinJavaModuleConfigurator)
ConfigureKotlinInProjectUtils.getConfiguratorByName(KotlinJavaModuleConfigurator.NAME);
assert configurator != null : "Configurator with given name doesn't exists: " + KotlinJavaModuleConfigurator.NAME;
Library kotlinLibrary = configurator.getKotlinLibrary(project);
if (kotlinLibrary == null) return null;
VirtualFile[] kotlinSourceFiles = kotlinLibrary.getFiles(OrderRootType.SOURCES);
for (VirtualFile sourceFile : kotlinSourceFiles) {
if (sourceFile.getName().equals(PathUtil.KOTLIN_JAVA_RUNTIME_SRC_JAR)) {
return sourceFile;
}
}
return null;
}
@Nullable
public static VirtualFile getLocalKotlinRuntimeJar(@NotNull Project project) {
return getLocalJar(getKotlinRuntimeJar(project));
}
@Nullable
private static VirtualFile getLocalJar(@Nullable VirtualFile kotlinRuntimeJar) {
public static VirtualFile getLocalJar(@Nullable VirtualFile kotlinRuntimeJar) {
if (kotlinRuntimeJar == null) return null;
VirtualFile localJarFile = JarFileSystem.getInstance().getVirtualFileForJar(kotlinRuntimeJar);
@@ -26,18 +26,12 @@ 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 org.jetbrains.annotations.NotNull;
@@ -45,10 +39,8 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.plugin.JetPluginUtil;
import org.jetbrains.jet.plugin.framework.JavaRuntimePresentationProvider;
import org.jetbrains.jet.plugin.framework.LibraryPresentationProviderUtil;
import org.jetbrains.jet.utils.PathUtil;
import javax.swing.event.HyperlinkEvent;
import java.io.File;
import java.util.Collection;
import java.util.List;
@@ -134,7 +126,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
if ("update".equals(event.getDescription())) {
updateKotlinLibraries(outdatedLibraries);
KotlinRuntimeLibraryUtil.updateLibraries(myProject, outdatedLibraries);
}
else if ("ignore".equals(event.getDescription())) {
PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion);
@@ -160,79 +152,31 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent
});
}
private void updateKotlinLibraries(Collection<Library> libraries) {
File runtimePath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimePath();
if (!runtimePath.exists()) {
showRuntimeJarNotFoundDialog(myProject, PathUtil.KOTLIN_JAVA_RUNTIME_JAR).run();
return;
}
for (Library library : libraries) {
VirtualFile libraryJar = JavaRuntimePresentationProvider.getRuntimeJar(library);
assert LibraryPresentationProviderUtil.isDetected(JavaRuntimePresentationProvider.getInstance(), library) &&
libraryJar != null : "Only java runtime libraries are expected";
KotlinRuntimeLibraryUtil.replaceFile(runtimePath, libraryJar);
VirtualFile libraryJarSrc = JavaRuntimePresentationProvider.getRuntimeSrcJar(library);
if (libraryJarSrc != null) {
File runtimeSrcPath = PathUtil.getKotlinPathsForIdeaPlugin().getRuntimeSourcesPath();
if (!runtimeSrcPath.exists()) {
showRuntimeJarNotFoundDialog(myProject, PathUtil.KOTLIN_JAVA_RUNTIME_SRC_JAR).run();
return;
}
KotlinRuntimeLibraryUtil.replaceFile(runtimeSrcPath, libraryJarSrc);
}
}
}
private static Collection<VersionedLibrary> findOutdatedKotlinLibraries(Project project, String pluginVersion) {
@NotNull
private static Collection<VersionedLibrary> findOutdatedKotlinLibraries(@NotNull Project project, @NotNull String pluginVersion) {
List<VersionedLibrary> outdatedLibraries = Lists.newArrayList();
for (VersionedLibrary versionedLibrary : findKotlinLibraries(project)) {
String libraryVersion = versionedLibrary.getVersion();
for (Library library : KotlinRuntimeLibraryUtil.findKotlinLibraries(project)) {
LibraryVersionProperties javaRuntimeProperties =
LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library);
if (javaRuntimeProperties == null) {
continue;
}
String libraryVersion = javaRuntimeProperties.getVersionString();
boolean isOutdated = "snapshot".equals(libraryVersion)
|| libraryVersion == null
|| libraryVersion.startsWith("internal-") != pluginVersion.startsWith("internal-")
|| VersionComparatorUtil.compare(pluginVersion, libraryVersion) > 0;
|| libraryVersion == null
|| libraryVersion.startsWith("internal-") != pluginVersion.startsWith("internal-")
|| VersionComparatorUtil.compare(pluginVersion, libraryVersion) > 0;
if (isOutdated) {
outdatedLibraries.add(versionedLibrary);
outdatedLibraries.add(new VersionedLibrary(library, libraryVersion));
}
}
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 = LibraryPresentationProviderUtil.getLibraryProperties(
JavaRuntimePresentationProvider.getInstance(), 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, final @NotNull String jarName) {
@@ -16,6 +16,8 @@
package org.jetbrains.jet.plugin.versions;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.ProjectTopics;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
@@ -30,6 +32,7 @@ import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootAdapter;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.ui.popup.PopupStep;
@@ -47,6 +50,7 @@ import org.jetbrains.jet.plugin.JetFileType;
import org.jetbrains.jet.plugin.configuration.ConfigureKotlinInProjectUtils;
import org.jetbrains.jet.plugin.configuration.KotlinJavaModuleConfigurator;
import org.jetbrains.jet.plugin.configuration.KotlinProjectConfigurator;
import org.jetbrains.jet.plugin.framework.JavaRuntimePresentationProvider;
import javax.swing.*;
import java.awt.*;
@@ -88,23 +92,36 @@ public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifi
return null;
}
private EditorNotificationPanel doCreate(Collection<VirtualFile> badRoots) {
private EditorNotificationPanel doCreate(final Collection<VirtualFile> badRoots) {
EditorNotificationPanel answer = new ErrorNotificationPanel();
VirtualFile kotlinRuntimeJar = KotlinRuntimeLibraryUtil.getLocalKotlinRuntimeJar(project);
if (kotlinRuntimeJar != null && badRoots.contains(kotlinRuntimeJar)) {
int otherBadRootsCount = badRoots.size() - 1;
String kotlinRuntimeJarName = kotlinRuntimeJar.getPresentableName();
String text = MessageFormat.format("<html>Kotlin <b>runtime library</b> jar <b>''{0}''</b> " +
Collection<Library> kotlinLibraries = KotlinRuntimeLibraryUtil.findKotlinLibraries(project);
final Collection<Library> badRuntimeLibraries = Collections2.filter(kotlinLibraries, new Predicate<Library>() {
@Override
public boolean apply(@Nullable Library library) {
assert library != null : "library should be non null";
VirtualFile runtimeJar = JavaRuntimePresentationProvider.getRuntimeJar(library);
return badRoots.contains(KotlinRuntimeLibraryUtil.getLocalJar(runtimeJar));
}
});
if (!badRuntimeLibraries.isEmpty()) {
int otherBadRootsCount = badRoots.size() - badRuntimeLibraries.size();
String text = MessageFormat.format("<html><b>{0,choice,0#|1#|1<Some }Kotlin runtime librar{0,choice,0#|1#y|1<ies}</b>" +
"{1,choice,0#|1# and one other jar|1< and {1} other jars} " +
"{1,choice,0#has|0<have} an unsupported format</html>",
kotlinRuntimeJarName,
badRuntimeLibraries.size(),
otherBadRootsCount);
String actionLabelText = MessageFormat.format("Update {0,choice,0#|1#|1<all }Kotlin runtime librar{0,choice,0#|1#y|1<ies} ",
badRuntimeLibraries.size());
answer.setText(text);
answer.createActionLabel("Update " + kotlinRuntimeJarName, new Runnable() {
answer.createActionLabel(actionLabelText, new Runnable() {
@Override
public void run() {
KotlinRuntimeLibraryUtil.updateRuntime(project);
KotlinRuntimeLibraryUtil.updateLibraries(project, badRuntimeLibraries);
}
});
if (otherBadRootsCount > 0) {