in SmvLibrary/Utility.cs [444:662]
public static SMVActionResult ExecuteAction(SMVAction action, bool fromWorker, bool workerUseDb, string workerTaskId)
{
// NOTE: The code in this function must be thread safe.
if (action == null)
{
return null;
}
// If there is a plugin, call PreAction first.
if (plugin != null)
{
plugin.PreAction(action);
}
if (fromWorker)
{
taskId = workerTaskId;
}
using (MemoryStream stream = new MemoryStream())
{
// We use a logger for writing messages since we can't output to the console in this function (As this
// may be running in multiple threads).
StreamWriter logger = new StreamWriter(stream);
IDictionary<string, string> variables = action.variables;
DateTime startTime = DateTime.Now;
string actionPath = variables["workingDir"];
string actionOutput = string.Empty;
int cumulativeExitCode = 0;
// Get the name of the action.
string name = action.name;
if (variables.ContainsKey("analysisProperty"))
{
name = action.name + " - " + variables["analysisProperty"];
}
variables["name"] = action.name;
Log.LogInfo("Running action: " + name, logger);
// Get the path to the action.
if (action.Path != null)
{
actionPath = action.Path.value;
}
actionPath = ExpandVariables(actionPath, variables);
variables["actionPath"] = actionPath;
// Launch a cmd.exe process to run commands in.
if (Console.InputEncoding.Equals(Encoding.UTF8))
{
Console.InputEncoding = new UTF8Encoding(false);
}
// Run the commands.
if (action.Command != null)
{
foreach (SMVCommand cmd in action.Command)
{
JobObject jobObject = null;
if (useJobObject)
{
//Update maxTime and maxMemory allowed
int maxMemory = int.MaxValue;
int maxTime = int.MaxValue;
updateAttribute(ref maxTime, cmd.maxTime, "Time");
Log.LogDebug("Maximum time allowed for this command = " + maxTime);
updateAttribute(ref maxMemory, cmd.maxMemory, "Memory");
//Converting memory from MB to bytes, if input is valid
if (maxMemory < int.MaxValue)
{
maxMemory *= (1024 * 1024);
}
Log.LogDebug("Maximum memory allowed for this command = " + maxMemory);
jobObject = new JobObject();
jobObject.setConstraints(maxMemory, maxTime);
}
Process process = LaunchProcess("cmd.exe", "", actionPath, action.Env, logger, jobObject);
process.OutputDataReceived += (sender, e) => { Log.LogMessage(e.Data, logger); };
process.ErrorDataReceived += (sender, e) => { Log.LogMessage(e.Data, logger); };
// Get the command and arguments, and expand all environment as well as SMV variables.
string cmdAttr = ExpandVariables(Environment.ExpandEnvironmentVariables(cmd.value), variables);
string argumentsAttr = string.Empty;
if (!string.IsNullOrEmpty(cmd.arguments))
{
argumentsAttr = ExpandVariables(Environment.ExpandEnvironmentVariables(cmd.arguments), variables);
}
try
{
Log.LogInfo(String.Format(CultureInfo.InvariantCulture, "Launching {0} with arguments: {1}", cmdAttr, argumentsAttr), logger);
process.StandardInput.WriteLine(String.Join(" ", new String[] { cmdAttr, argumentsAttr }));
process.StandardInput.WriteLine("Exit %errorlevel%");
process.StandardInput.Close();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
TimeSpan span = process.ExitTime - process.StartTime;
Log.LogMessage(string.Format("Command Exit code: {0}", process.ExitCode), logger);
cumulativeExitCode += Math.Abs(process.ExitCode);
if (useDb || (fromWorker && workerUseDb))
{
try
{
using (var database = new SmvDbEntities())
{
var masterEntry = new TaskAction
{
ActionID = Guid.NewGuid().ToString(),
TaskID = taskId,
ActionName = action.name,
Success = cumulativeExitCode.ToString(),
ActionTime = span.ToString(),
WorkingDirectory = variables["workingDir"],
AnalysisDirectory = variables["analysisProperty"]
};
database.TaskActions.Add(masterEntry);
database.SaveChanges();
}
}
catch (Exception e)
{
if (fromWorker)
{
Log.LogError("Exception while updating database " + e);
}
else
{
scheduler.Dispose();
Log.LogFatalError("Exception while updating database " + e);
}
}
}
if (useJobObject)
{
jobObject.QueryExtendedLimitInformation();
jobObject.Close();
jobObject.Dispose();
}
}
catch (Exception e)
{
Log.LogInfo(e.ToString(), logger);
Log.LogInfo("Could not start process: " + cmdAttr, logger);
if (useJobObject)
{
jobObject.Close();
jobObject.Dispose();
}
return null;
}
}
}
logger.Flush();
stream.Position = 0;
string output = new StreamReader(stream).ReadToEnd();
if (debugMode)
{
Log.WriteToFile(Path.Combine(actionPath, string.Format("smvexecute-{0}.log", action.name)), output, false);
}
Log.LogDebug("cumulative exit code is " + cumulativeExitCode);
DateTime endTime = DateTime.Now;
action.result = new SMVActionResult(action.name, output, (cumulativeExitCode == 0),
cumulativeExitCode != 0 && action.breakOnError, (int)(endTime - startTime).TotalSeconds);
// Call plugin post action only if we were successful in executing the action.
if (cumulativeExitCode == 0)
{
// get the output directory and set the output of the action from the build log.
if (action.name.Equals("NormalBuild"))
{
string logPath = Path.Combine(variables["workingDir"], variables["smvLogFileNamePrefix"] + ".log");
action.result.output = Utility.ReadFile(logPath);
if (!variables.ContainsKey("outputDir")){
variables["outputDir"] = ExtractBuildPath(variables["workingDir"], action.result.output, logger);
Utility.SetSmvVar("outputDir", variables["outputDir"]);
}
}
// Get the output directory and the analysis directory.
if (action.name.Equals("InterceptedBuild"))
{
string logPath = Path.Combine(variables["workingDir"], variables["smvLogFileNamePrefix"] + ".log");
action.result.output = Utility.ReadFile(logPath);
}
// Call the plugin's post action.
if (plugin != null)
{
plugin.PostAction(action);
}
}
else
{
// are we sure we want to exit here... the cloud worker instance becomes
// unhealthy after exiting here...
if (action.breakOnError)
{
if (!fromWorker)
{
scheduler.Dispose();
plugin.Finally(true);
Log.LogFatalError(String.Format("Action: {0}, failed.", name));
}
}
}
return action.result;
}
}