ln.build/ln.build/commands/CommandRunner.cs

169 lines
6.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ln.logging;
namespace ln.build.commands
{
public enum CRThrow { NEVER, NEGATIVE, NONZERO }
public class CommandRunner
{
public static CRThrow DefaultThrow { get; set; } = CRThrow.NONZERO;
public CRThrow Throw { get; set; } = DefaultThrow;
public string Executable { get; }
List<Argument> arguments = new List<Argument>();
public IEnumerable<Argument> Arguments => arguments;
public Logger Logger { get; }
Func<int,bool> TestExitCode = null;
public CommandRunner(string executable,params Argument[] arguments) : this(Logger.Default, executable, arguments){}
public CommandRunner(Logger logger, string executable,params Argument[] arguments) : this(logger, null, executable, arguments){}
public CommandRunner(Logger logger, Func<int,bool> testExitCode, string executable,params Argument[] args)
{
Logger = logger;
Executable = executable;
arguments.AddRange(args);
TestExitCode = testExitCode;
}
public void AddArgument(Argument argument) => arguments.Add(argument);
public void AddArguments(params Argument[] args) => arguments.AddRange(args);
public string FindFileInPath(CommandEnvironment environment, string filename)
{
Logger.Log(LogLevel.DEBUG, "Looking up {0} in paths {1}", filename, environment.Get("PATH",""));
foreach (string path in environment.Get("PATH","").Split(Path.PathSeparator))
{
string fullpath = Path.Combine(path,filename);
if (File.Exists(fullpath))
return fullpath;
}
return filename;
}
IEnumerable<string> GetArguments(CommandEnvironment environment, bool mask) => Arguments.SelectMany((e)=>e.GetArguments(environment, mask));
public int Run() => Run(new CommandEnvironment(), null, null);
public int Run(CommandEnvironment environment) => Run(environment, null, null);
public int Run(CommandEnvironment environment, Stream stdout) => Run(environment, stdout, stdout);
public int Run(Stream stdout, Stream stderr) => Run(new CommandEnvironment(), stdout, stderr);
public int Run(CommandEnvironment environment, Stream stdout, Stream stderr)
{
ProcessStartInfo psi = new ProcessStartInfo(FindFileInPath(environment, Executable), string.Join(' ', GetArguments(environment, false)))
{
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
WorkingDirectory = environment.WorkingDirectory
};
psi.Environment.Clear();
foreach (KeyValuePair<string,string> ev in environment)
psi.Environment.Add(ev.Key,ev.Value);
environment.Logger.Log(LogLevel.INFO, "Executing: {0} {1}", psi.FileName, string.Join(' ', GetArguments(environment, true)));
Process process = Process.Start(psi);
process.WaitForExit();
if (stdout != null)
process.StandardOutput.BaseStream.CopyTo(stdout);
if (stderr != null)
process.StandardError.BaseStream.CopyTo(stderr);
environment.Logger.Log(LogLevel.INFO, "Result: {0}", process.ExitCode);
if (
((TestExitCode != null) && !TestExitCode(process.ExitCode)) ||
((Throw == CRThrow.NEGATIVE) && (process.ExitCode < 0)) ||
((Throw == CRThrow.NONZERO) && (process.ExitCode != 0))
)
throw new Exception(String.Format("{0} execution gave result {1}", psi.FileName, process.ExitCode));
return process.ExitCode;
}
public int Run(out string stdout,out string stderr) => Run(new CommandEnvironment(), out stdout, out stderr);
public int Run(CommandEnvironment environment, out string stdout,out string stderr)
{
MemoryStream outstream,errstream;
outstream = new MemoryStream();
errstream = new MemoryStream();
int status = Run(environment, outstream, errstream);
using (StreamReader sr = new StreamReader(outstream))
stdout = sr.ReadToEnd();
using (StreamReader sr = new StreamReader(errstream))
stderr = sr.ReadToEnd();
return status;
}
public class Argument
{
string value;
public bool MaskValue { get; set; }
protected Argument(){ }
public Argument(string argument) : this(argument, false) { }
public Argument(string argument, bool maskValue)
{
value = argument;
MaskValue = maskValue;
}
public void SetValue(string argValue) => value = argValue;
public virtual string[] GetArguments(CommandEnvironment environment, bool mask) => (value != null) ? new string[]{ (mask && MaskValue) ? "*****" : value } : new string[0];
public static implicit operator Argument(String s) => new Argument(s);
}
public class Option : Argument
{
public string OptionArgument { get; }
public Func<CommandEnvironment,string> GetValue { get; }
public Option(string optionArgument, string optionValue) : this(optionArgument, optionValue, false){ }
public Option(string optionArgument, string optionValue, bool maskValue)
{
OptionArgument = optionArgument;
GetValue = (e) => optionValue;
MaskValue = maskValue;
}
public Option(string optionArgument, Func<CommandEnvironment,string> getOptionValue) : this(optionArgument, getOptionValue, false) { }
public Option(string optionArgument, Func<CommandEnvironment,string> getOptionValue, bool maskValue)
{
OptionArgument = optionArgument;
GetValue = getOptionValue;
MaskValue = maskValue;
}
public override string[] GetArguments(CommandEnvironment environment, bool mask)
{
string value = GetValue(environment);
if ((value == null) || String.Empty.Equals(value))
{
return new string[0];
} else {
return new string[]{ OptionArgument, (mask && MaskValue) ? "*****" : value };
}
}
}
}
}