510 lines
13 KiB
Haxe
510 lines
13 KiB
Haxe
import haxe.io.BytesOutput;
|
|
import haxe.io.Eof;
|
|
import haxe.io.Path;
|
|
import sys.io.Process;
|
|
import sys.FileSystem;
|
|
#if haxe4
|
|
import sys.thread.Thread;
|
|
#elseif neko
|
|
import neko.vm.Thread;
|
|
#else
|
|
import cpp.vm.Thread;
|
|
#end
|
|
|
|
class ProcessManager
|
|
{
|
|
static function dup(inArgs:Array<String>)
|
|
{
|
|
if (inArgs==null)
|
|
return [];
|
|
return inArgs.copy();
|
|
}
|
|
|
|
// Command may be a pseudo command, like "xcrun --sdk abc", or 'python "some script"'
|
|
// Here we split the first word into command and move the rest into args, being careful
|
|
// to preserve quoted words
|
|
static function combineCommand(command:String, args:Array<String>)
|
|
{
|
|
var parts = new Array<String>();
|
|
var c = command;
|
|
var quoted = ~/^\s*"([^"]+)"(.*)/;
|
|
var word = ~/^\s*(\S+)(.*)/;
|
|
while(c.length>0)
|
|
{
|
|
if (quoted.match(c))
|
|
{
|
|
parts.push( quoted.matched(1) );
|
|
c = quoted.matched(2);
|
|
}
|
|
else if (word.match(c))
|
|
{
|
|
parts.push( word.matched(1) );
|
|
c = word.matched(2);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
if (parts.length>1)
|
|
{
|
|
command = parts.shift();
|
|
while(parts.length>0)
|
|
args.unshift( parts.pop() );
|
|
}
|
|
return PathManager.escape(command);
|
|
}
|
|
|
|
private static function formatMessage(command:String, args:Array<String>, colorize:Bool = true):String
|
|
{
|
|
var message = "";
|
|
|
|
if (colorize)
|
|
{
|
|
message = "\x1b[33;1m" + command + "\x1b[0m";
|
|
}
|
|
else
|
|
{
|
|
message = command;
|
|
}
|
|
|
|
for (arg in args)
|
|
{
|
|
if (colorize)
|
|
{
|
|
var ext = Path.extension(arg);
|
|
if (ext == "cpp" || ext == "c" || ext == "h" || ext == "hpp" || ext == "m" || ext == "mm")
|
|
{
|
|
var split = arg.split ("/");
|
|
if (split.length > 1)
|
|
{
|
|
arg = "\x1b[33m" + split.slice(0, split.length - 1).join("/") + "/\x1b[33;1m" + split[split.length - 1] + "\x1b[0m";
|
|
}
|
|
else
|
|
{
|
|
arg = "\x1b[1m" + arg + "\x1b[0m";
|
|
}
|
|
}
|
|
else if (StringTools.startsWith(arg, "-D"))
|
|
{
|
|
arg = "\x1b[1m" + arg + "\x1b[0m";
|
|
}
|
|
else
|
|
{
|
|
arg = "\x1b[0m" + arg + "\x1b[0m";
|
|
}
|
|
}
|
|
|
|
if (arg.indexOf(" ") > -1)
|
|
{
|
|
message += " \"" + arg + "\"";
|
|
}
|
|
else
|
|
{
|
|
message += " " + arg;
|
|
}
|
|
}
|
|
return message;
|
|
}
|
|
|
|
public static function runCommand(path:String, command:String, args:Array<String>, print:Bool = true, safeExecute:Bool = true, ignoreErrors:Bool = false,?inText:String):Int
|
|
{
|
|
args = dup(args);
|
|
command = combineCommand(command,args);
|
|
|
|
|
|
if (print && !Log.verbose && !Log.quiet)
|
|
{
|
|
Log.info(inText==null ? "" : inText,formatMessage(command, args));
|
|
}
|
|
|
|
if (safeExecute)
|
|
{
|
|
try
|
|
{
|
|
if (path != null && path != "" && !FileSystem.exists(FileSystem.fullPath(path)) && !FileSystem.exists(FileSystem.fullPath(new Path(path).dir)))
|
|
{
|
|
Log.error("The specified target path \"" + path + "\" does not exist");
|
|
return 1;
|
|
}
|
|
return _runCommand(path, command, args, inText);
|
|
}
|
|
catch (e:Dynamic)
|
|
{
|
|
if (!ignoreErrors)
|
|
{
|
|
//var text = formatMessage(command, args);
|
|
//Log.error("Error while running command\n" + text , e);
|
|
if (Log.verbose)
|
|
{
|
|
Log.error ("", e);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return _runCommand(path, command, args, inText);
|
|
}
|
|
}
|
|
|
|
public static function readStderr(inCommand:String,inArgs:Array<String>)
|
|
{
|
|
inArgs = dup(inArgs);
|
|
inCommand = combineCommand(inCommand,inArgs);
|
|
|
|
var result = new Array<String>();
|
|
var proc = new Process(inCommand,inArgs);
|
|
try
|
|
{
|
|
while(true)
|
|
{
|
|
var out = proc.stderr.readLine();
|
|
result.push(out);
|
|
}
|
|
} catch(e:Dynamic){}
|
|
proc.close();
|
|
return result;
|
|
}
|
|
|
|
public static function readStdout(command:String,args:Array<String>)
|
|
{
|
|
args = dup(args);
|
|
command = combineCommand(command,args);
|
|
|
|
|
|
var result = new Array<String>();
|
|
var proc = new Process(command,args);
|
|
try
|
|
{
|
|
while(true)
|
|
{
|
|
var out = proc.stdout.readLine();
|
|
result.push(out);
|
|
}
|
|
} catch(e:Dynamic){}
|
|
proc.close();
|
|
return result;
|
|
}
|
|
|
|
public static function runProcess(path:String, command:String, args:Array<String>, waitForOutput:Bool = true, print:Bool = true, safeExecute:Bool = true, ignoreErrors:Bool = false, ?text:String):String
|
|
{
|
|
args = dup(args);
|
|
command = combineCommand(command,args);
|
|
|
|
if (print && !Log.verbose)
|
|
{
|
|
Log.info(formatMessage(command, args));
|
|
}
|
|
|
|
|
|
if (safeExecute)
|
|
{
|
|
try
|
|
{
|
|
if (path != null && path != "" && !FileSystem.exists(FileSystem.fullPath(path)) && !FileSystem.exists(FileSystem.fullPath(new Path(path).dir)))
|
|
{
|
|
Log.error("The specified target path \"" + path + "\" does not exist");
|
|
}
|
|
return _runProcess(path, command, args, waitForOutput, ignoreErrors, text);
|
|
}
|
|
catch (e:Dynamic)
|
|
{
|
|
if (!ignoreErrors)
|
|
{
|
|
//Log.error("Error while running command\n" + formatMessage(command,args), e);
|
|
if (Log.verbose)
|
|
{
|
|
Log.error ("", e);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return _runProcess(path, command, args, waitForOutput, ignoreErrors, text);
|
|
}
|
|
}
|
|
public static function runProcessLine(path:String, command:String, args:Array<String>, waitForOutput:Bool = true, print:Bool = true, safeExecute:Bool = true, ignoreErrors:Bool = false):String
|
|
{
|
|
var result = runProcess(path, command, args, waitForOutput, print, safeExecute, ignoreErrors);
|
|
if (result!=null)
|
|
return result.split("\n")[0];
|
|
return result;
|
|
}
|
|
|
|
|
|
private static function _runCommand(path:String, command:String, args:Array<String>, inText:String):Int
|
|
{
|
|
var oldPath:String = "";
|
|
|
|
if (path != null && path != "")
|
|
{
|
|
Log.info("", " - \x1b[1mChanging directory:\x1b[0m " + path + "");
|
|
|
|
oldPath = Sys.getCwd();
|
|
Sys.setCwd(path);
|
|
}
|
|
|
|
if (Log.quiet && inText!=null)
|
|
{
|
|
Log.info(inText);
|
|
}
|
|
else
|
|
{
|
|
var text = inText==null ? "Running command" : inText;
|
|
Log.info("", " - \x1b[1m" + text + ":\x1b[0m " + formatMessage(command, args));
|
|
}
|
|
|
|
var result = 0;
|
|
|
|
if (args != null && args.length > 0)
|
|
{
|
|
result = Sys.command(command, args);
|
|
}
|
|
else
|
|
{
|
|
result = Sys.command(command);
|
|
}
|
|
|
|
if (oldPath != "")
|
|
{
|
|
Sys.setCwd(oldPath);
|
|
}
|
|
|
|
if (result != 0)
|
|
{
|
|
throw ("Error while running command\n" + formatMessage(command, args) + (path != "" ? " [" + path + "]" : ""));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static function _runProcess(path:String, command:String, args:Array<String>, waitForOutput:Bool, ignoreErrors:Bool, inText:String):String
|
|
{
|
|
var oldPath:String = "";
|
|
|
|
if (path != null && path != "")
|
|
{
|
|
Log.info("", " - \x1b[1m - Changing directory:\x1b[0m " + path + "");
|
|
|
|
oldPath = Sys.getCwd();
|
|
Sys.setCwd(path);
|
|
}
|
|
|
|
if ( !Log.quiet)
|
|
{
|
|
var text = inText==null ? "Running process" : inText;
|
|
Log.info("", " - \x1b[1m" + text + ":\x1b[0m " + formatMessage(command, args));
|
|
}
|
|
else
|
|
{
|
|
Log.info("",inText);
|
|
}
|
|
|
|
var output = "";
|
|
var result = 0;
|
|
|
|
var process:Process = null;
|
|
try
|
|
{
|
|
process = new Process(command, args);
|
|
}
|
|
catch(e:Dynamic)
|
|
{
|
|
if (ignoreErrors)
|
|
return null;
|
|
Log.error(e+"");
|
|
}
|
|
|
|
var buffer = new BytesOutput();
|
|
|
|
if (waitForOutput)
|
|
{
|
|
var waiting = true;
|
|
while (waiting)
|
|
{
|
|
try
|
|
{
|
|
var current = process.stdout.readAll(1024);
|
|
buffer.write(current);
|
|
if (current.length == 0)
|
|
{
|
|
waiting = false;
|
|
}
|
|
}
|
|
catch (e:Eof)
|
|
{
|
|
waiting = false;
|
|
}
|
|
}
|
|
|
|
result = process.exitCode();
|
|
process.close();
|
|
|
|
//if (result == 0)
|
|
//{
|
|
output = buffer.getBytes().toString();
|
|
if (output == "")
|
|
{
|
|
var error = process.stderr.readAll().toString();
|
|
if (ignoreErrors)
|
|
{
|
|
output = error;
|
|
}
|
|
else
|
|
{
|
|
if (error==null || error=="")
|
|
error = "Error while running command\n" + formatMessage(command, args);
|
|
Log.error(error);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
//}
|
|
}
|
|
|
|
if (oldPath != "")
|
|
{
|
|
Sys.setCwd(oldPath);
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
// This function will return 0 on success, or non-zero error code
|
|
public static function runProcessThreaded(command:String, args:Array<String>, inText:String = null):Int
|
|
{
|
|
args = dup(args);
|
|
command = combineCommand(command,args);
|
|
|
|
Log.lock();
|
|
|
|
// Other thread may have already thrown an error
|
|
if (BuildTool.threadExitCode!=0)
|
|
{
|
|
Log.unlock();
|
|
return BuildTool.threadExitCode;
|
|
}
|
|
|
|
if (inText != null)
|
|
Log.info(inText,"");
|
|
|
|
if (!Log.quiet)
|
|
Log.v(" - \x1b[1mRunning command:\x1b[0m " + formatMessage(command, args));
|
|
Log.unlock();
|
|
|
|
var output = new Array<String>();
|
|
var process:Process = null;
|
|
try
|
|
{
|
|
process = new Process(command, args);
|
|
}
|
|
catch(e:Dynamic)
|
|
{
|
|
Log.lock();
|
|
if (BuildTool.threadExitCode == 0)
|
|
{
|
|
Log.info('${Log.RED}${Log.BOLD}$e${Log.NORMAL}\n');
|
|
BuildTool.setThreadError(-1);
|
|
}
|
|
Log.unlock();
|
|
return -1;
|
|
}
|
|
|
|
|
|
var err = process.stderr;
|
|
var out = process.stdout;
|
|
var reader = BuildTool.helperThread.value;
|
|
|
|
// Read stderr in separate thread to avoid blocking
|
|
if (reader==null)
|
|
{
|
|
var controller = Thread.current();
|
|
BuildTool.helperThread.value = reader = Thread.create(function()
|
|
{
|
|
while(true)
|
|
{
|
|
var stream = Thread.readMessage(true);
|
|
var output:Array<String> = null;
|
|
try
|
|
{
|
|
while(true)
|
|
{
|
|
var line = stream.readLine();
|
|
if (output==null)
|
|
output = [ line ];
|
|
else
|
|
output.push(line);
|
|
}
|
|
}
|
|
catch(e:Dynamic){ }
|
|
controller.sendMessage(output);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Start-up the error reader
|
|
reader.sendMessage(err);
|
|
|
|
try
|
|
{
|
|
while(true)
|
|
{
|
|
var line = out.readLine();
|
|
output.push(line);
|
|
}
|
|
}
|
|
catch(e:Dynamic){ }
|
|
|
|
if (output.length==1 && ~/^\S+\.(cpp|c|cc)$/.match(output[0]))
|
|
{
|
|
// Microsoft prints the name of the cpp file for some reason
|
|
output = [];
|
|
}
|
|
|
|
var errOut:Array<String> = Thread.readMessage(true);
|
|
|
|
var code = process.exitCode();
|
|
process.close();
|
|
|
|
if (code != 0)
|
|
{
|
|
if (BuildTool.threadExitCode == 0)
|
|
{
|
|
Log.lock();
|
|
var message = "";
|
|
if (Log.verbose)
|
|
{
|
|
Log.println("");
|
|
message += "Error while running command\n";
|
|
message += formatMessage(command,args) + "\n\n";
|
|
}
|
|
if (output.length > 0)
|
|
{
|
|
message += output.join("\n") + "\n";
|
|
}
|
|
if (errOut != null)
|
|
{
|
|
message += errOut.join("\n") + '${Log.NORMAL}';
|
|
}
|
|
Log.error(message,"",null,false);
|
|
Log.unlock();
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
if (errOut!=null && errOut.length>0)
|
|
output = output.concat(errOut);
|
|
|
|
if (output.length>0)
|
|
{
|
|
Log.info(output.join("\n"));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|