1package org.jetbrains 2 3import org.gradle.api.tasks.AbstractExecTask 4import org.gradle.api.tasks.TaskAction 5import org.gradle.internal.os.OperatingSystem 6 7import java.nio.file.Files 8import java.nio.file.Path 9import java.nio.file.Paths 10 11class CrossPlatformExec extends AbstractExecTask { 12 private static final def windowsExtensions = ['bat', 'cmd', 'exe']; 13 private static final def unixExtensions = [null, 'sh']; 14 15 private boolean windows; 16 17 public CrossPlatformExec() { 18 super(CrossPlatformExec.class); 19 windows = OperatingSystem.current().windows; 20 } 21 22 @Override 23 @TaskAction 24 protected void exec() { 25 List<String> commandLine = this.getCommandLine(); 26 27 if (!commandLine.isEmpty()) { 28 commandLine[0] = findCommand(commandLine[0], windows); 29 } 30 31 if (windows) { 32 if (!commandLine.isEmpty() && commandLine[0]) { 33 commandLine 34 } 35 commandLine.add(0, '/c'); 36 commandLine.add(0, 'cmd'); 37 } 38 39 this.setCommandLine(commandLine); 40 41 super.exec(); 42 } 43 44 private static String findCommand(String command, boolean windows) { 45 command = normalizeCommandPaths(command); 46 def extensions = windows ? windowsExtensions : unixExtensions; 47 48 return extensions.findResult(command) { extension -> 49 Path commandFile 50 if (extension) { 51 commandFile = Paths.get(command + '.' + extension); 52 } else { 53 commandFile = Paths.get(command); 54 } 55 56 return resolveCommandFromFile(commandFile, windows); 57 }; 58 } 59 60 private static String resolveCommandFromFile(Path commandFile, boolean windows) { 61 if (!Files.isExecutable(commandFile)) { 62 return null; 63 } 64 65 return commandFile.toAbsolutePath().normalize(); 66 } 67 68 private static String normalizeCommandPaths(String command) { 69 // need to escape backslash so it works with regex 70 String backslashSeparator = '\\\\'; 71 72 String forwardSlashSeparator = '/'; 73 74 // escape separator if it's a backslash 75 char backslash = '\\'; 76 String separator = File.separatorChar == backslash ? backslashSeparator : File.separator 77 78 return command 79 // first replace all of the backslashes with forward slashes 80 .replaceAll(backslashSeparator, forwardSlashSeparator) 81 // then replace all forward slashes with whatever the separator actually is 82 .replaceAll(forwardSlashSeparator, separator); 83 } 84}