JAL-3013 resolve symlink to hmmer binaries folder
[jalview.git] / src / jalview / hmmer / HmmerCommand.java
index 8888c20..b5c1b25 100644 (file)
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.List;
@@ -97,19 +98,19 @@ public abstract class HmmerCommand implements Runnable
    * Runs a command as a separate process and waits for it to complete. Answers
    * true if the process return status is zero, else false.
    * 
-   * @param command
+   * @param commands
    *          the executable command and any arguments to it
    * @throws IOException
    */
-  public boolean runCommand(List<String> command)
+  public boolean runCommand(List<String> commands)
           throws IOException
   {
-    List<String> commands = Platform.isWindows() ? wrapWithCygwin(command)
-            : command;
+    List<String> args = Platform.isWindows() ? wrapWithCygwin(commands)
+            : commands;
 
     try
     {
-      ProcessBuilder pb = new ProcessBuilder(commands);
+      ProcessBuilder pb = new ProcessBuilder(args);
       pb.redirectErrorStream(true); // merge syserr to sysout
       final Process p = pb.start();
       new Thread(new Runnable()
@@ -139,7 +140,7 @@ public abstract class HmmerCommand implements Runnable
       if (exitValue != 0)
       {
         Cache.log.error("Command failed, return code = " + exitValue);
-        Cache.log.error("Command/args were: " + commands.toString());
+        Cache.log.error("Command/args were: " + args.toString());
       }
       return exitValue == 0; // 0 is success, by convention
     } catch (Exception e)
@@ -150,35 +151,35 @@ public abstract class HmmerCommand implements Runnable
   }
 
   /**
-   * Converts the given command to a Cygwin "run" command wrapper
+   * Converts the given command to a Cygwin "bash" command wrapper. The hmmer
+   * command and any arguments to it are converted into a single parameter to the
+   * bash command.
    * 
-   * @param command
-   * @return
+   * @param commands
    */
-  protected List<String> wrapWithCygwin(List<String> command)
+  protected List<String> wrapWithCygwin(List<String> commands)
   {
-    File runCygwin = FileUtils.getExecutable("run",
+    File bash = FileUtils.getExecutable("bash",
             Cache.getProperty(Preferences.CYGWIN_PATH));
-    if (runCygwin == null)
+    if (bash == null)
     {
       Cache.log.error("Cygwin shell not found");
-      return command;
+      return commands;
     }
 
     List<String> wrapped = new ArrayList<>();
-    wrapped.add(runCygwin.getAbsolutePath());
-    if (!command.isEmpty())
+    wrapped.add(bash.getAbsolutePath());
+    wrapped.add("-c");
+
+    /*
+     * combine hmmbuild/search/align and arguments to a single string
+     */
+    StringBuilder sb = new StringBuilder();
+    for (String cmd : commands)
     {
-      wrapped.add(command.get(0));
-      // wrapped.add("--quote");
-      StringBuilder args = new StringBuilder();
-      for (String arg : command.subList(1, command.size()))
-      {
-        args.append(" ").append(arg);
-      }
-      wrapped.add(args.toString());
+      sb.append(" ").append(cmd);
     }
-    // TODO this doesn't yet pass parameters successfully
+    wrapped.add(sb.toString());
 
     return wrapped;
   }
@@ -247,10 +248,13 @@ public abstract class HmmerCommand implements Runnable
    * @param cmd
    *          command short name e.g. hmmalign
    * @return
+   * @throws IOException
    */
-  protected String getCommandPath(String cmd)
+  protected String getCommandPath(String cmd) throws IOException
   {
     String binariesFolder = Cache.getProperty(Preferences.HMMER_PATH);
+    // ensure any symlink to the directory is resolved:
+    binariesFolder = Paths.get(binariesFolder).toRealPath().toString();
     File file = FileUtils.getExecutable(cmd, binariesFolder);
     if (file == null && af != null)
     {
@@ -258,7 +262,7 @@ public abstract class HmmerCommand implements Runnable
               .formatMessage("label.executable_not_found", cmd));
     }
 
-    return file == null ? null : file.getAbsolutePath();
+    return file == null ? null : getFilePath(file);
   }
 
   /**
@@ -304,4 +308,31 @@ public abstract class HmmerCommand implements Runnable
     }
     return null;
   }
+
+  /**
+   * Answers an absolute path to the given file, in a format suitable for
+   * processing by a hmmer command. On a Windows platform, the native Windows file
+   * path is converted to Cygwin format, by replacing '\'with '/' and drive letter
+   * X with /cygdrive/x.
+   * 
+   * @param resultFile
+   * @return
+   */
+  protected String getFilePath(File resultFile)
+  {
+    String path = resultFile.getAbsolutePath();
+    if (Platform.isWindows())
+    {
+      // the first backslash escapes '\' for the regular expression argument
+      path = path.replaceAll("\\" + File.separator, "/");
+      int colon = path.indexOf(':');
+      if (colon > 0)
+      {
+        String drive = path.substring(0, colon);
+        path = path.replaceAll(drive + ":", "/cygdrive/" + drive);
+      }
+    }
+
+    return path;
+  }
 }