From ff638b98db095ffd7dc792f5d91fe5a0de6fc2ba Mon Sep 17 00:00:00 2001 From: Mungo Carstairs Date: Tue, 17 Apr 2018 16:01:30 +0100 Subject: [PATCH] JAL-2937 working Cygwin bash execution of hmmbuild on Windows --- src/jalview/gui/Preferences.java | 7 +++- src/jalview/hmmer/HMMAlign.java | 8 ++-- src/jalview/hmmer/HMMBuild.java | 4 +- src/jalview/hmmer/HMMSearch.java | 10 ++--- src/jalview/hmmer/HmmerCommand.java | 77 +++++++++++++++++++++++------------ src/jalview/util/FileUtils.java | 22 ++++++++++ test/jalview/hmmer/HMMERTest.java | 2 +- 7 files changed, 92 insertions(+), 38 deletions(-) diff --git a/src/jalview/gui/Preferences.java b/src/jalview/gui/Preferences.java index 296c4bf..5382f8d 100755 --- a/src/jalview/gui/Preferences.java +++ b/src/jalview/gui/Preferences.java @@ -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 diff --git a/src/jalview/hmmer/HMMAlign.java b/src/jalview/hmmer/HMMAlign.java index 163e85b..6300c4b 100644 --- a/src/jalview/hmmer/HMMAlign.java +++ b/src/jalview/hmmer/HMMAlign.java @@ -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 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); diff --git a/src/jalview/hmmer/HMMBuild.java b/src/jalview/hmmer/HMMBuild.java index c596cec..b99ad2e 100644 --- a/src/jalview/hmmer/HMMBuild.java +++ b/src/jalview/hmmer/HMMBuild.java @@ -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); } diff --git a/src/jalview/hmmer/HMMSearch.java b/src/jalview/hmmer/HMMSearch.java index 6f73f2f..b4e7427 100644 --- a/src/jalview/hmmer/HMMSearch.java +++ b/src/jalview/hmmer/HMMSearch.java @@ -125,9 +125,9 @@ public class HMMSearch extends HmmerCommand List 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(); diff --git a/src/jalview/hmmer/HmmerCommand.java b/src/jalview/hmmer/HmmerCommand.java index 9bec181..dfd5395 100644 --- a/src/jalview/hmmer/HmmerCommand.java +++ b/src/jalview/hmmer/HmmerCommand.java @@ -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 command) + public boolean runCommand(List commands) throws IOException { - List commands = Platform.isWindows() ? wrapWithCygwin(command) - : command; + List 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 wrapWithCygwin(List command) + protected List wrapWithCygwin(List 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 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; + } } diff --git a/src/jalview/util/FileUtils.java b/src/jalview/util/FileUtils.java index b81ec71..1684763 100644 --- a/src/jalview/util/FileUtils.java +++ b/src/jalview/util/FileUtils.java @@ -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 * diff --git a/test/jalview/hmmer/HMMERTest.java b/test/jalview/hmmer/HMMERTest.java index 8f96d04..109575c 100644 --- a/test/jalview/hmmer/HMMERTest.java +++ b/test/jalview/hmmer/HMMERTest.java @@ -100,7 +100,7 @@ public class HMMERTest { public void testHMMAlign() { - HMMAlign thread = new HMMAlign(frame, + HmmerCommand thread = new HMMAlign(frame, new ArrayList()); thread.run(); -- 1.7.10.2