JAL-2937 working Cygwin bash execution of hmmbuild on Windows
authorMungo Carstairs <gmungoc@gmail.com>
Tue, 17 Apr 2018 15:01:30 +0000 (16:01 +0100)
committerMungo Carstairs <gmungoc@gmail.com>
Tue, 17 Apr 2018 15:01:30 +0000 (16:01 +0100)
src/jalview/gui/Preferences.java
src/jalview/hmmer/HMMAlign.java
src/jalview/hmmer/HMMBuild.java
src/jalview/hmmer/HMMSearch.java
src/jalview/hmmer/HmmerCommand.java
src/jalview/util/FileUtils.java
test/jalview/hmmer/HMMERTest.java

index 296c4bf..5382f8d 100755 (executable)
@@ -258,7 +258,12 @@ public class Preferences extends GPreferences
     });
     if (cygwinPath != null)
     {
-      cygwinPath.setText(Cache.getProperty(CYGWIN_PATH));
+      String path = Cache.getProperty(CYGWIN_PATH);
+      if (path == null)
+      {
+        path = FileUtils.getPathTo("bash");
+      }
+      cygwinPath.setText(path);
       cygwinPath.addActionListener(new ActionListener()
       {
         @Override
index 163e85b..6300c4b 100644 (file)
@@ -157,9 +157,9 @@ public class HMMAlign extends HmmerCommand
       }
     }
     args.add("-o");
-    args.add(resultFile.getAbsolutePath());
-    args.add(modelFile.getAbsolutePath());
-    args.add(alignmentFile.getAbsolutePath());
+    args.add(getFilePath(resultFile));
+    args.add(getFilePath(modelFile));
+    args.add(getFilePath(alignmentFile));
     
     return runCommand(args);
   }
@@ -177,7 +177,7 @@ public class HMMAlign extends HmmerCommand
   private SequenceI[] importData(File resultFile,
           List<AlignmentOrder> allOrders) throws IOException
   {
-    StockholmFile file = new StockholmFile(resultFile.getAbsolutePath(),
+    StockholmFile file = new StockholmFile(getFilePath(resultFile),
             DataSourceType.FILE);
     SequenceI[] result = file.getSeqsAsArray();
     AlignmentOrder msaorder = new AlignmentOrder(result);
index c596cec..b99ad2e 100644 (file)
@@ -312,8 +312,8 @@ public class HMMBuild extends HmmerCommand
       args.add(ARG_DNA);
     }
 
-    args.add(hmmFile.getAbsolutePath());
-    args.add(sequencesFile.getAbsolutePath());
+    args.add(getFilePath(hmmFile));
+    args.add(getFilePath(sequencesFile));
 
     return runCommand(args);
   }
index 6f73f2f..b4e7427 100644 (file)
@@ -125,9 +125,9 @@ public class HMMSearch extends HmmerCommand
     List<String> args = new ArrayList<>();
     args.add(command);
     args.add("-o");
-    args.add(searchOutputFile.getAbsolutePath());
+    args.add(getFilePath(searchOutputFile));
     args.add("-A");
-    args.add(hitsAlignmentFile.getAbsolutePath());
+    args.add(getFilePath(hitsAlignmentFile));
 
     boolean dbFound = false;
     String dbPath = "";
@@ -216,8 +216,8 @@ public class HMMSearch extends HmmerCommand
       // writer.close();
     }
 
-    args.add(hmmFile.getAbsolutePath());
-    args.add(databaseFile.getAbsolutePath());
+    args.add(getFilePath(hmmFile));
+    args.add(getFilePath(databaseFile));
 
     return runCommand(args);
   }
@@ -264,7 +264,7 @@ public class HMMSearch extends HmmerCommand
                 MessageManager.getString("label.trim_termini_desc"), true,
                 true, true, null));
       }
-      HMMAlign hmmalign = new HMMAlign(frame, alignArgs);
+      HmmerCommand hmmalign = new HMMAlign(frame, alignArgs);
       hmmalign.run();
       frame = null;
       hmmTemp.delete();
index 9bec181..dfd5395 100644 (file)
@@ -97,19 +97,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 +139,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 +150,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;
   }
@@ -258,7 +258,7 @@ public abstract class HmmerCommand implements Runnable
                 MessageManager.getString("warn.hmm_command_failed"));
     }
 
-    return file == null ? null : file.getAbsolutePath();
+    return file == null ? null : getFilePath(file);
   }
 
   /**
@@ -279,4 +279,31 @@ public abstract class HmmerCommand implements Runnable
       writer.close();
     }
   }
+
+  /**
+   * 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;
+  }
 }
index b81ec71..1684763 100644 (file)
@@ -37,6 +37,28 @@ public final class FileUtils
   }
 
   /**
+   * Answers the path to the folder containing the given executable file, by
+   * searching the PATH environment variable. Answers null if no such executable
+   * can be found.
+   * 
+   * @param cmd
+   * @return
+   */
+  public static String getPathTo(String cmd)
+  {
+    String paths = System.getenv("PATH");
+    // backslash is to escape regular expression argument
+    for (String path : paths.split("\\" + File.pathSeparator))
+    {
+      if (getExecutable(cmd, path) != null)
+      {
+        return path;
+      }
+    }
+    return null;
+  }
+
+  /**
    * A convenience method to create a temporary file that is deleted on exit of
    * the JVM
    * 
index 8f96d04..109575c 100644 (file)
@@ -100,7 +100,7 @@ public class HMMERTest {
 
   public void testHMMAlign()
   {
-    HMMAlign thread = new HMMAlign(frame,
+    HmmerCommand thread = new HMMAlign(frame,
             new ArrayList<ArgumentI>());
     thread.run();