/// <summary> /// Performs the completion work for Async workers, and is called /// after the runAsync method returns. Returns the ultimate /// disposition of the activity. /// </summary> /// <remarks> /// This method runs synchronously on the main thread. /// It can tidy up the state after async work, and store results /// in the Repository. /// Thou shalt not return Stale. /// </remarks> /// <returns>The disposition of this verb's worker's work.</returns> public Disposition Complete() { this.verb.RecordProcessInvokeCpuTime(this.pinv.CpuTime); string stdout = null; if (this.returnStandardOut) { stdout = this.pinv.GetStdout(); } string stderr = null; if (this.returnStandardError) { stderr = this.pinv.GetStderr(); } Disposition disposition; if (this.exitCodeHandling == ProcessExitCodeHandling.NonzeroIsOkay || this.pinv.ExitCode == 0) { disposition = new Fresh(); } else { // Sheesh. Some tools emit error messages to stdout. // REVIEW: Provide full command line here rather than just executable (like old version did)? Failed f = new Failed(this.pinv.GetStdout() + this.pinv.GetStderr()); f.AddError("Executable: " + this.executable + "\n"); disposition = f; } return(this.verb.Complete(this.workingDirectory, this.pinv.CpuTime, stdout, stderr, disposition)); }
/// <summary> /// Performs the completion work for Async workers, and is called /// after the runAsync method returns. Returns the ultimate /// disposition of the activity. /// </summary> /// <remarks> /// This method runs synchronously on the main thread. /// It can tidy up the state after async work, and store results /// in the Repository. /// Thou shalt not return Stale. /// </remarks> /// <returns>The disposition of this verb's worker's work.</returns> public Disposition Complete() { this.verb.RecordProcessInvokeCpuTime(this.pinv.CpuTime); string stdout = null; if (this.returnStandardOut) { stdout = this.pinv.GetStdout(); } string stderr = null; if (this.returnStandardError) { stderr = this.pinv.GetStderr(); } Disposition disposition; if (this.exitCodeHandling == ProcessExitCodeHandling.NonzeroIsOkay || this.pinv.ExitCode == 0) { disposition = new Fresh(); } else { // Sheesh. Some tools emit error messages to stdout. // REVIEW: Provide full command line here rather than just executable (like old version did)? Failed f = new Failed(this.pinv.GetStdout() + this.pinv.GetStderr()); f.AddError("Executable: " + this.executable + "\n"); disposition = f; } return this.verb.Complete(this.workingDirectory, this.pinv.CpuTime, stdout, stderr, disposition); }
public ProcessInvoker( string executable, string[] args, RcHandling rcHandling, BuildObject failureBase, string finalStdoutPath = null, string dbgText = null, bool allowAbsoluteExe = false, bool allowAbsoluteArgs = false, string workingDir = null) { Util.Assert(allowAbsoluteExe || !executable.Contains(":")); //- Hey, this looks like an absolute path! Use .getRelativePath() to tolerate crossing machine boundaries. foreach (string arg in args) { //- Pardon my distasteful heuristic to avoid flagging /flag:value args. Util.Assert(allowAbsoluteArgs || arg.Length < 2 || arg[1] != ':'); //- Hey, this looks like an absolute path! Use .getRelativePath() to tolerate crossing machine boundaries. } this.rcHandling = rcHandling; stdout = new StringBuilder(); stderr = new StringBuilder(); using (Job job = new Job()) { using (Process proc = new Process()) { proc.StartInfo.FileName = Path.Combine(BuildEngine.theEngine.getIronRoot(), executable); //- TODO Is there a better way to escape the args to avoid problems with spaces? proc.StartInfo.Arguments = String.Join(" ", args); proc.StartInfo.WorkingDirectory = workingDir == null?BuildEngine.theEngine.getIronRoot() : workingDir; proc.StartInfo.RedirectStandardOutput = true; if (finalStdoutPath != null) { _tmpStdoutPath = finalStdoutPath + ".tmp"; stdoutFile = new StreamWriter(_tmpStdoutPath); proc.OutputDataReceived += new DataReceivedEventHandler(stdoutRedirectHandler); } else { //- collect stdout here for diagnostics. proc.OutputDataReceived += new DataReceivedEventHandler(stdoutHandler); } proc.StartInfo.RedirectStandardError = true; proc.ErrorDataReceived += new DataReceivedEventHandler(stderrHandler); proc.StartInfo.UseShellExecute = false; string commandLine = proc.StartInfo.FileName + " " + proc.StartInfo.Arguments; if (alwaysEmitDiagnostics) { //- In diagnostic mode, we emit the command line twice, once ahead in case Boogie decides //- to run away and never come back. BuildObject failureBatObj = failureBase.makeOutputObject(".bat"); failureBatObj.prepareObjDirectory(); File.WriteAllText(failureBatObj.getFilesystemPath(), commandLine); } proc.Start(); job.AddProcess(proc); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); cpuTime = job.GetCpuTime().TotalSeconds; exitCode = proc.ExitCode; if (stdoutFile != null) { stdoutFile.Close(); } if (passed()) { disposition = new Fresh(); } else { //- sheesh. Some tools emit error messages to stdout. Failed f = new Failed(getStdoutString() + stderr.ToString()); f.AddError("Command line: " + commandLine + "\n"); disposition = f; } #pragma warning disable 429 //- alwaysEmitDiagnostics is a compile-time constant that can hide expression !passed() if (failureBase != null && (alwaysEmitDiagnostics || !passed())) #pragma warning restore 429 { failureBase.prepareObjDirectory(); File.WriteAllText(failureBase.makeOutputObject(".bat").getFilesystemPath(), commandLine); File.WriteAllText(failureBase.makeOutputObject(".txt").getFilesystemPath(), dbgText); File.WriteAllText(failureBase.makeOutputObject(".stdout").getFilesystemPath(), getStdoutString()); File.WriteAllText(failureBase.makeOutputObject(".stderr").getFilesystemPath(), getStderr()); } } } if (passed() && _tmpStdoutPath != null) { File.Delete(finalStdoutPath); File.Move(_tmpStdoutPath, finalStdoutPath); _tmpStdoutPath = null; } }