[platform] better restarter for Linux (IDEA-161186)
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Sat, 24 Sep 2016 14:21:11 +0000 (17:21 +0300)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Sat, 24 Sep 2016 14:21:11 +0000 (17:21 +0300)
bin/linux/restart.py [new file with mode: 0755]
bin/scripts/unix/idea.sh
build/groovy/org/jetbrains/intellij/build/impl/CrossPlatformDistributionBuilder.groovy
build/groovy/org/jetbrains/intellij/build/impl/LinuxDistributionBuilder.groovy
build/scripts/utils.gant
build/update.sh
platform/bootstrap/src/com/intellij/idea/Main.java
platform/platform-impl/src/com/intellij/ide/SystemHealthMonitor.java
platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java
platform/platform-impl/src/com/intellij/util/Restarter.java
platform/platform-resources-en/src/messages/IdeBundle.properties

diff --git a/bin/linux/restart.py b/bin/linux/restart.py
new file mode 100755 (executable)
index 0000000..9055b03
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Waits for the parent process to terminate, then executes specified commands.
+
+import os
+import sys
+import time
+
+if len(sys.argv) < 2:
+    raise Exception('At least one argument expected')
+
+while os.getppid() != 1:
+    time.sleep(0.5)
+
+if len(sys.argv) > 2:
+    os.spawnv(os.P_WAIT, sys.argv[2], sys.argv[2:])
+
+to_launch = sys.argv[1]
+if sys.platform == 'darwin':
+    os.execv('/usr/bin/open', ['/usr/bin/open', to_launch])
+else:
+    os.execv(to_launch, [to_launch])
index 786a99486585ec0951ff14c863cb786f3dc1e39a..38487a589aa4edee7a6af1a3658957941306d55e 100755 (executable)
@@ -184,23 +184,11 @@ LD_LIBRARY_PATH="$IDE_BIN_HOME:$LD_LIBRARY_PATH" "$JAVA_BIN" \
   "-Xbootclasspath/a:$IDE_HOME/lib/boot.jar" \
   -classpath "$CLASSPATH" \
   ${VM_OPTIONS} \
-  "-Djb.vmOptionsFile=$VM_OPTIONS_FILE" \
   "-XX:ErrorFile=$HOME/java_error_in_@@product_uc@@_%p.log" \
   "-XX:HeapDumpPath=$HOME/java_error_in_@@product_uc@@.hprof" \
-  -Djb.restart.code=88 -Didea.paths.selector=@@system_selector@@ \
+  -Didea.paths.selector=@@system_selector@@ \
+  "-Djb.vmOptionsFile=$VM_OPTIONS_FILE" \
   ${IDE_PROPERTIES_PROPERTY} \
   @@ide_jvm_args@@ \
   com.intellij.idea.Main \
   "$@"
-EC=$?
-unset IFS
-
-test ${EC} -ne 88 && exit ${EC}
-
-RESTARTER="$HOME/.@@system_selector@@/system/restart/restarter.sh"
-if [ -x "$RESTARTER" ]; then
-  "$RESTARTER"
-  "$RM" -f "$RESTARTER"
-fi
-
-exec "$0"
index 1f2b603a71ee7faaaa667ec73082f1fd27dfdea0..6d2cbd5861cb13781dcace41314ddcc429b336d5 100644 (file)
@@ -78,11 +78,13 @@ class CrossPlatformDistributionBuilder {
           exclude(name: "bin/fsnotifier*")
           exclude(name: "bin/*.vmoptions")
           exclude(name: "bin/*.sh")
+          exclude(name: "bin/*.py")
           exclude(name: "bin/idea.properties")
           exclude(name: "help/**")
         }
-        zipfileset(dir: "$linuxDistPath/bin", filemode: "775", prefix: "bin") {
+        zipfileset(dir: "$linuxDistPath/bin", prefix: "bin", filemode: "775") {
           include(name: "*.sh")
+          include(name: "*.py")
         }
         zipfileset(dir: "$linuxDistPath/bin", prefix: "bin/linux", filemode: "775") {
           include(name: "fsnotifier*")
@@ -97,7 +99,7 @@ class CrossPlatformDistributionBuilder {
           exclude(name: "bin/idea.properties")
           exclude(name: "bin/*.vmoptions")
         }
-        zipfileset(dir: "$macDistPath/bin", filemode: "775", prefix: "bin") {
+        zipfileset(dir: "$macDistPath/bin", prefix: "bin", filemode: "775") {
           include(name: "restarter*")
           include(name: "*.py")
         }
index a80bd98e956b20d2ec1e47da26e0e86ecbf41246..df30239efe1355b87d039d184f0f94762186ea06 100644 (file)
@@ -157,6 +157,7 @@ class LinuxDistributionBuilder extends OsSpecificDistributionBuilder {
         paths.each {
           tarfileset(dir: it, prefix: tarRoot) {
             exclude(name: "bin/*.sh")
+            exclude(name: "bin/*.py")
             exclude(name: "bin/fsnotifier*")
             extraBins.each {
               exclude(name: it)
@@ -166,8 +167,9 @@ class LinuxDistributionBuilder extends OsSpecificDistributionBuilder {
         }
 
         paths.each {
-          tarfileset(dir: it, filemode: "755", prefix: tarRoot) {
+          tarfileset(dir: it, prefix: tarRoot, filemode: "755") {
             include(name: "bin/*.sh")
+            include(name: "bin/*.py")
             include(name: "bin/fsnotifier*")
             extraBins.each {
               include(name: it)
index e6b010a1cb9375145e846703176b8a265a6fcc24..5bc2b23fb9ce4db1a5b6a40e4fc0c5732353bc79 100644 (file)
@@ -1006,10 +1006,12 @@ binding.setVariable("buildCrossPlatformZip", { String zipPath, String sandbox, L
       exclude(name: "bin/fsnotifier*")
       exclude(name: "bin/*.vmoptions")
       exclude(name: "bin/*.sh")
+      exclude(name: "bin/*.py")
       exclude(name: "help/**")
     }
-    zipfileset(dir: "$distUnix/bin", filemode: "775", prefix: "bin") {
+    zipfileset(dir: "$distUnix/bin", prefix: "bin", filemode: "775") {
       include(name: "*.sh")
+      include(name: "*.py")
     }
     zipfileset(dir: "$distUnix/bin", prefix: "bin/linux", filemode: "775") {
       include(name: "fsnotifier*")
@@ -1024,7 +1026,7 @@ binding.setVariable("buildCrossPlatformZip", { String zipPath, String sandbox, L
       exclude(name: "bin/idea.properties")
       exclude(name: "bin/*.vmoptions")
     }
-    zipfileset(dir: "$distMac/bin", filemode: "775", prefix: "bin") {
+    zipfileset(dir: "$distMac/bin", prefix: "bin", filemode: "775") {
       include(name: "restarter*")
       include(name: "*.py")
     }
@@ -1058,7 +1060,7 @@ binding.setVariable("buildMacZip", { String zipRoot, String zipPath, List paths,
     }
 
     allPaths.each {
-      zipfileset(dir: it, filemode: "755", prefix: zipRoot) {
+      zipfileset(dir: it, prefix: zipRoot, filemode: "755") {
         include(name: "bin/*.sh")
         include(name: "bin/*.py")
         include(name: "bin/fsnotifier")
@@ -1090,6 +1092,7 @@ binding.setVariable("buildTarGz", { String tarRoot, String tarPath, List paths,
     paths.each {
       tarfileset(dir: it, prefix: tarRoot) {
         exclude(name: "bin/*.sh")
+        exclude(name: "bin/*.py")
         exclude(name: "bin/fsnotifier*")
         extraBins.each {
           exclude(name: it)
@@ -1099,8 +1102,9 @@ binding.setVariable("buildTarGz", { String tarRoot, String tarPath, List paths,
     }
 
     paths.each {
-      tarfileset(dir: it, filemode: "755", prefix: tarRoot) {
+      tarfileset(dir: it, prefix: tarRoot, filemode: "755") {
         include(name: "bin/*.sh")
+        include(name: "bin/*.py")
         include(name: "bin/fsnotifier*")
         extraBins.each {
           include(name: it)
index ba75073ff0c2fc1f2d3e24c0dd2afc16d55e54cf..099b6332f180834de6679828aecf88b290ad48ef 100755 (executable)
@@ -53,6 +53,7 @@ OS_TYPE=`uname -s`
 if [ "$OS_TYPE" = "Linux" ]; then
   cp -a "$DEV_IDEA_HOME/bin/linux/"*.so "$WORK_IDEA_HOME/bin"
   cp -a "$DEV_IDEA_HOME/bin/linux/"fsnotifier* "$WORK_IDEA_HOME/bin"
+  cp -a "$DEV_IDEA_HOME/bin/linux/"*.py "$WORK_IDEA_HOME/bin"
 elif [ "$OS_TYPE" = "Darwin" ]; then
   cp -a "$DEV_IDEA_HOME/bin/mac/"*.jnilib "$WORK_IDEA_HOME/bin"
   cp -a "$DEV_IDEA_HOME/bin/mac/fsnotifier" "$WORK_IDEA_HOME/bin"
index 2988d812edd7bd9723dff677e6e8c5f012e39e87..8adafa0c9b7f9cff86de8c758a8a09a740c3db12 100644 (file)
@@ -30,7 +30,7 @@ import java.io.StringWriter;
 
 public class Main {
   public static final int NO_GRAPHICS = 1;
-  //public static final int UPDATE_FAILED = 2;
+  public static final int RESTART_FAILED = 2;
   public static final int STARTUP_EXCEPTION = 3;
   public static final int JDK_CHECK_FAILED = 4;
   public static final int DIR_CHECK_FAILED = 5;
index 00a7e0094a96f54722cd0b2153341a9197af0ffd..d80290d4a19bad9bc93bab2d50c500cb8ca4986f 100644 (file)
@@ -64,6 +64,7 @@ public class SystemHealthMonitor extends ApplicationComponent.Adapter {
   public void initComponent() {
     checkJvm();
     checkIBus();
+    checkLauncherScript();
     startDiskSpaceMonitoring();
   }
 
@@ -91,6 +92,12 @@ public class SystemHealthMonitor extends ApplicationComponent.Adapter {
     }
   }
 
+  private void checkLauncherScript() {
+    if (SystemInfo.isXWindow && System.getProperty("jb.restart.code") != null) {
+      showNotification("ide.launcher.script.outdated");
+    }
+  }
+
   private void showNotification(@PropertyKey(resourceBundle = "messages.IdeBundle") String key) {
     final String ignoreKey = "ignore." + key;
     boolean ignored = myProperties.isValueSet(ignoreKey);
index 10cdb97b8285b9607f803c87eaeed2db83376f19..8485b9d9fe6696b9db1cdf95a1e9acbeef84c6f8 100644 (file)
@@ -780,10 +780,12 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App
       int exitCode = 0;
       if (restart && Restarter.isSupported()) {
         try {
-          exitCode = Restarter.scheduleRestart(beforeRestart);
+          Restarter.scheduleRestart(beforeRestart);
         }
-        catch (IOException e) {
-          LOG.error("Cannot restart", e);
+        catch (Throwable t) {
+          LOG.error("Restart failed", t);
+          Main.showMessage("Restart failed", t);
+          exitCode = Main.RESTART_FAILED;
         }
       }
       System.exit(exitCode);
index c9d8862e683994b98404c86525e96e6d98ed2120..4787908047dc3a02f11d05d13326b035d487d344 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.util;
 
 import com.intellij.jna.JnaLoader;
+import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.application.PathManager;
 import com.intellij.openapi.util.SystemInfo;
 import com.intellij.openapi.util.io.FileUtilRt;
@@ -28,9 +29,7 @@ import com.sun.jna.ptr.IntByReference;
 import com.sun.jna.win32.StdCallLibrary;
 import org.jetbrains.annotations.NotNull;
 
-import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -39,76 +38,40 @@ import java.util.List;
 public class Restarter {
   private Restarter() { }
 
-  private static int getRestartCode() {
-    return SystemProperties.getIntProperty("jb.restart.code", 0);
+  private static File getLauncherScript() {
+    return new File(PathManager.getBinPath(), ApplicationNamesInfo.getInstance().getLowercaseProductName() + ".sh");
   }
 
   public static boolean isSupported() {
-    if (getRestartCode() != 0) {
-      return true;
-    }
     if (SystemInfo.isWindows) {
       return JnaLoader.isLoaded() && new File(PathManager.getBinPath(), "restarter.exe").exists();
     }
     if (SystemInfo.isMac) {
-      return PathManager.getHomePath().contains(".app");
-    }
-    return false;
-  }
-
-  public static int scheduleRestart(@NotNull String... beforeRestart) throws IOException {
-    try {
-      int restartCode = getRestartCode();
-      if (restartCode != 0) {
-        runCommand(beforeRestart);
-        return restartCode;
-      }
-      else if (SystemInfo.isWindows) {
-        restartOnWindows(beforeRestart);
-        return 0;
-      }
-      else if (SystemInfo.isMac) {
-        restartOnMac(beforeRestart);
-        return 0;
-      }
+      return PathManager.getHomePath().contains(".app") && new File(PathManager.getBinPath(), "restarter").canExecute();
     }
-    catch (Throwable t) {
-      throw new IOException("Cannot restart application: " + t.getMessage(), t);
+    if (SystemInfo.isUnix) {
+      return getLauncherScript().canExecute() && new File(PathManager.getBinPath(), "restart.py").canExecute();
     }
 
-    runCommand(beforeRestart);
-    throw new IOException("Cannot restart application: not supported.");
+    return false;
   }
 
-  private static void runCommand(String... beforeRestart) throws IOException {
-    if (beforeRestart.length == 0) return;
-
-    File restartDir = new File(getRestarterDir());
-    String systemPath = new File(System.getProperty("user.home") + "/." + System.getProperty("idea.paths.selector") + "/system/restart").getPath();
-    if (! systemPath.equals(restartDir.getPath())){
-      throw new IOException("idea.system.path was changed. Restart is not supported.");
+  public static void scheduleRestart(@NotNull String... beforeRestart) throws IOException {
+    if (SystemInfo.isWindows) {
+      restartOnWindows(beforeRestart);
     }
-    if (!FileUtilRt.createDirectory(restartDir)) {
-      throw new IOException("Cannot create dir: " + restartDir);
+    else if (SystemInfo.isMac) {
+      restartOnMac(beforeRestart);
     }
-
-    File restarter = new File(restartDir, "restarter.sh");
-    try (BufferedWriter output = new BufferedWriter(new FileWriter(restarter))) {
-      output.write("#!/bin/sh\n");
-      for (int i = 0; i < beforeRestart.length; i++) {
-        output.write(beforeRestart[i]);
-        if (i <= beforeRestart.length - 2) output.write(' ');
-        if (i >= beforeRestart.length - 2) output.write('"');
-      }
-      output.write('\n');
+    else if (SystemInfo.isUnix) {
+      restartOnUnix(beforeRestart);
     }
-
-    if (!restarter.setExecutable(true, true)) {
-      throw new IOException("Cannot make file executable: " + restarter);
+    else {
+      throw new IOException("Cannot restart application: not supported.");
     }
   }
 
-  private static void restartOnWindows(@NotNull String... beforeRestart) throws IOException {
+  private static void restartOnWindows(String... beforeRestart) throws IOException {
     Kernel32 kernel32 = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
     Shell32 shell32 = (Shell32)Native.loadLibrary("shell32", Shell32.class);
 
@@ -144,14 +107,22 @@ public class Restarter {
     TimeoutUtil.sleep(500);
   }
 
-  private static void restartOnMac(@NotNull String... beforeRestart) throws IOException {
+  private static void restartOnMac(String... beforeRestart) throws IOException {
     String homePath = PathManager.getHomePath();
     int p = homePath.indexOf(".app");
     if (p < 0) throw new IOException("Application bundle not found: " + homePath);
-
     String bundlePath = homePath.substring(0, p + 4);
     doScheduleRestart(new File(PathManager.getBinPath(), "restarter"), commands -> {
-      Collections.addAll(commands, bundlePath);
+      commands.add(bundlePath);
+      Collections.addAll(commands, beforeRestart);
+    });
+  }
+
+  private static void restartOnUnix(String... beforeRestart) throws IOException {
+    File launcherScript = getLauncherScript();
+    if (!launcherScript.exists()) throw new IOException("Launcher script not found: " + launcherScript);
+    doScheduleRestart(new File(PathManager.getBinPath(), "restart.py"), commands -> {
+      commands.add(launcherScript.getPath());
       Collections.addAll(commands, beforeRestart);
     });
   }
index b91af5868706f531d016a2479f0a82b64559c25a..83c64e9d39556cf993d3ac99bf2587b282f9666c 100644 (file)
@@ -1138,11 +1138,9 @@ FileChooser.refreshActionLabelText=Refresh
 browsers.settings=Web Browsers
 updates.check.period.on.exit=On every exit
 
-unsupported.jvm.openjdk.message=OpenJDK 6 is not supported. Please use Oracle Java or newer OpenJDK.
 unsupported.jvm.ea.message=Early Access Java versions may cause compatibility issues. Please use stable release.
-
 ibus.blocking.warn.message=IBus prior to 1.5.11 may cause input problems. See <a href="https://youtrack.jetbrains.com/issue/IDEA-78860">IDEA-78860</a> for details.
-
+ide.launcher.script.outdated=IDE launcher script (idea.sh) is outdated. Please upgrade, otherwise you won't be able to restart the IDE.
 sys.health.acknowledge.link=<br/><a href="ack">Do not show again</a>.
 
 low.disk.space.message=Low disk space on a {0} system directory partition