public override IVerbWorker getWorker(WorkingDirectory workingDirectory) { Disposition disposition = new Fresh(); if (this.verifyVerb != null) { VerificationResult verificationResult = VerificationResult.fromXmlFile(this.verifyVerb.getOutputs().Single()); if (!verificationResult.pass) { disposition = new Failed(); } } if (!(disposition is Failed)) { foreach (var o in this.buildVerb.getOutputs()) { if (o.getExtension() == ".exe") { File.Copy(workingDirectory.PathTo(o), workingDirectory.PathTo(this.exeOutput), overwrite: true); } else { var dest = this.RelocateBuildObjectToExeDirectory(o); File.Copy( workingDirectory.PathTo(o), workingDirectory.PathTo(dest), overwrite: true); } } } return(new VerbSyncWorker(workingDirectory, disposition)); }
public static new Disposition readXml(XmlReader xr) { List <string> messages = new List <string>(); if (!xr.IsEmptyElement) { while (xr.Read()) { if (xr.NodeType == XmlNodeType.Element) { if (xr.Name.Equals(_xml_MessageTag)) { messages.Add(xr.ReadElementContentAsString()); } else { throw new Exception("Unrecognized Disposition::Failed tag " + xr.Name); } } else if (xr.NodeType == XmlNodeType.EndElement) { Util.Assert(xr.Name.Equals(_xml_tag)); break; } } } Failed f = new Failed(); f.messages = messages; return(f); }
/// <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)); }
void markFailed(IVerb verb) { //- at least one of verb's inputs has a permanent failure, so we didn't //- even try to execute it. Disposition disposition = new Failed("upstream failure"); ResultSummaryRecord summary = new ResultSummaryRecord(verb, disposition, new BuildObjectValuePointer[] {}); //- NB never store upstream failures to the persistent cache, because //- they depend on our knowledge this run that the upstream verb failed. //- If, in another run, the verb or its inputs are modified, produce the //- same outputs, but returns success, we'll be stuck pulling this //- upstream failure out of cache but calling this verb a failure. //-string inputHash = computeInputHash(verb, false); //-if (inputHash != null) //-{ //- Util.Assert(false); //- //- //- //- //- //- resultCache.storeResult(inputHash, summary); //-} //-else //-{ unrecordableFailures[verb] = disposition; //-} verbIsComplete(verb, disposition, BlessingRequest.Bless); }
public static Disposition readXml(XmlReader xr) { Util.Assert(xr.Name.Equals(_xml_tag)); string value = xr.GetAttribute(_xml_value_attr); if (value.Equals(Fresh.Value)) { return(new Fresh()); } else if (value.Equals(Failed.Value)) { return(Failed.readXml(xr)); } else { throw new Exception("Invalid disposition value " + value); } }
/// <summary> /// Mark a verb as having failed. /// </summary> /// <param name="verb">The verb that failed.</param> private void markFailed(IVerb verb) { // At least one of verb's inputs has a permanent failure, so we didn't // even try to execute it. Disposition disposition = new Failed("upstream failure"); ResultSummaryRecord summary = new ResultSummaryRecord(verb, disposition, new BuildObjectValuePointer[] { }); // NB never store upstream failures to the persistent cache, because // they depend on our knowledge this run that the upstream verb failed. // If, in another run, the verb or its inputs are modified, produce the // same outputs, but returns success, we'll be stuck pulling this // upstream failure out of cache but calling this verb a failure. ////string inputHash = computeInputHash(verb, false); ////if (inputHash != null) ////{ //// Util.Assert(false); //// // "Upstream failures" will never have a computable inputHash, because their inputs //// // can't be considered known. Even if the upstream verb wrote something to disk, //// // what if the upstream verb changes to no longer fail but still emit the same thing? //// // We wouldn't want to conclude that, because the inputs hadn't changed, this //// // verb still had an upstream failure. //// repository.StoreResult(inputHash, summary); ////} ////else ////{ this.unrecordableFailures[verb] = disposition; ////} // Mark all the verb's outputs as Failed in the repository. foreach (BuildObject obj in verb.getOutputs()) { if (obj is VirtualBuildObject) { this.repository.StoreVirtual(obj, disposition, null); } else { this.repository.AddObject(obj, disposition, null); } } this.verbIsComplete(verb, disposition); }
public Disposition Complete(WorkingDirectory workingDirectory, double cpuTimeSeconds, string stdout, string stderr, Disposition disposition) { var dis = disposition; string cSharpPath = Path.ChangeExtension(workingDirectory.PathTo(this.expandedSource), CSharpExt); if (!File.Exists(cSharpPath)) { // Dafny has a bug where compilation fails but result code is still 0. dis = new Failed(); } if (dis is Fresh) { this.RewriteCSharpFile(cSharpPath, workingDirectory.PathTo(this.output)); } return(dis); }
public static new Disposition readXml(XmlReader xr) { List<string> messages = new List<string>(); if (!xr.IsEmptyElement) { while (xr.Read()) { if (xr.NodeType == XmlNodeType.Element) { if (xr.Name.Equals(_xml_MessageTag)) { messages.Add(xr.ReadElementContentAsString()); } else { throw new Exception("Unrecognized Disposition::Failed tag " + xr.Name); } } else if (xr.NodeType == XmlNodeType.EndElement) { Util.Assert(xr.Name.Equals(Disposition._xml_tag)); break; } } } Failed f = new Failed(); f.messages = messages; return f; }
/// <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); }
Disposition recordResult(IVerb verb, Disposition executionDisposition) { //- record how each output appeared. List <BuildObjectValuePointer> outputs = new List <BuildObjectValuePointer>(); List <string> missingOutputs = new List <string>(); IEnumerable <BuildObject> expectedOutputs = verb.getOutputs(); if (executionDisposition is Failed) { expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs()); } bool hasVirtualOutputs = false; foreach (BuildObject outobj in expectedOutputs) { if (!(outobj is VirtualBuildObject)) { if (File.Exists(outobj.getFilesystemPath())) { //- Try to catch accidental case mismatches that would burn us when we //- try to fetch the file back in. string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.getFilesystemPath(), false); Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName())); outputs.Add(resultCache.storeObject(outobj)); } else { missingOutputs.Add(String.Format("Missing expected output {0}", outobj.getRelativePath())); } } else { hasVirtualOutputs = true; if (nuObjContents.getDisposition(outobj) is Fresh) { //- nothing to cache; virtual objects only survive in nuObjContents, the in-process cache } else { missingOutputs.Add(String.Format("Missing expected virtual {0}", outobj.getRelativePath())); } } } if (!(executionDisposition is Failed) && missingOutputs.Count() > 0) { executionDisposition = new Failed(missingOutputs); } ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs); string inputHash = computeInputHash(verb, true); Util.Assert(inputHash != null); if (!hasVirtualOutputs) { resultCache.storeResult(inputHash, summary); } else { Say("Not caching verb persistently: " + verb); } verbIsComplete(verb, executionDisposition, BlessingRequest.Bless); return(executionDisposition); }
/// <summary> /// Record how each output of a task appeared. /// </summary> /// <param name="completion">The task completion notification.</param> /// <returns>Overall result of the verb execution.</returns> private Disposition recordResult(VerbRunner.TaskCompletion completion) { WorkingDirectory workingDirectory = completion.workingDirectory; IVerb verb = completion.verb; Disposition executionDisposition = completion.disposition; List <BuildObjectValuePointer> outputs = new List <BuildObjectValuePointer>(); List <string> missingOutputs = new List <string>(); IEnumerable <BuildObject> expectedOutputs = verb.getOutputs(); if (executionDisposition is Failed) { expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs()); } bool hasVirtualOutputs = false; foreach (BuildObject outobj in expectedOutputs) { if (!(outobj is VirtualBuildObject)) { // For expected file outputs, check for existence in working directory. // REVIEW: Add method to WorkingDirectory which does this? if (File.Exists(workingDirectory.PathTo(outobj))) { // Try to catch accidental case mismatches that would burn us when we // try to fetch the file back in. ////string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.deprecatedGetFilesystemPath(), false); ////Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName())); // REVIEW: Do we need to worry about case mismatches anymore? See comments above. outputs.Add(this.repository.Store(workingDirectory, outobj, executionDisposition)); // Store a copy of this verb output as a file in the real nuobj directory. Util.Assert(outobj.getRelativePath().StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal)); string nuobjPath = IronRootDirectory.PathTo(outobj); Directory.CreateDirectory(Path.GetDirectoryName(nuobjPath)); File.Copy(workingDirectory.PathTo(outobj), nuobjPath, true); } else { missingOutputs.Add(string.Format("Missing expected output {0}", outobj.getRelativePath())); } } else { hasVirtualOutputs = true; if (this.repository.GetDisposition(outobj) is Fresh) { // Nothing to cache; virtual objects only survive in the Repository, the in-process store. } else { missingOutputs.Add(string.Format("Missing expected virtual {0}", outobj.getRelativePath())); } } } if (!(executionDisposition is Failed) && missingOutputs.Count() > 0) { executionDisposition = new Failed(missingOutputs); } ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs); string inputHash = this.computeInputHash(verb, true); Util.Assert(inputHash != null); if (!hasVirtualOutputs) { this.repository.StoreResult(inputHash, summary); } else { this.Say("Not caching verb persistently: " + verb); } this.verbIsComplete(verb, executionDisposition); return(executionDisposition); }
public override IVerbWorker getWorker(WorkingDirectory workingDirectory) { Disposition disposition = new Fresh(); if (this.verifyVerb != null) { VerificationResult verificationResult = VerificationResult.fromXmlFile(this.verifyVerb.getOutputs().Single()); if (!verificationResult.pass) { disposition = new Failed(); } } if (!(disposition is Failed)) { foreach (var o in this.buildVerb.getOutputs()) { if (o.getExtension() == ".exe") { File.Copy(workingDirectory.PathTo(o), workingDirectory.PathTo(this.exeOutput), overwrite: true); } else { var dest = this.RelocateBuildObjectToExeDirectory(o); File.Copy( workingDirectory.PathTo(o), workingDirectory.PathTo(dest), overwrite: true); } } } return new VerbSyncWorker(workingDirectory, 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; } }
/// <summary> /// Record how each output of a task appeared. /// </summary> /// <param name="completion">The task completion notification.</param> /// <returns>Overall result of the verb execution.</returns> private Disposition recordResult(VerbRunner.TaskCompletion completion) { WorkingDirectory workingDirectory = completion.workingDirectory; IVerb verb = completion.verb; Disposition executionDisposition = completion.disposition; List<BuildObjectValuePointer> outputs = new List<BuildObjectValuePointer>(); List<string> missingOutputs = new List<string>(); IEnumerable<BuildObject> expectedOutputs = verb.getOutputs(); if (executionDisposition is Failed) { expectedOutputs = expectedOutputs.Concat(verb.getFailureOutputs()); } bool hasVirtualOutputs = false; foreach (BuildObject outobj in expectedOutputs) { if (!(outobj is VirtualBuildObject)) { // For expected file outputs, check for existence in working directory. // REVIEW: Add method to WorkingDirectory which does this? if (File.Exists(workingDirectory.PathTo(outobj))) { // Try to catch accidental case mismatches that would burn us when we // try to fetch the file back in. ////string fsname = PathNormalizer.dbg_normalizePath_nocache(outobj.deprecatedGetFilesystemPath(), false); ////Util.Assert(Path.GetFileName(fsname).Equals(outobj.getFileName())); // REVIEW: Do we need to worry about case mismatches anymore? See comments above. outputs.Add(this.repository.Store(workingDirectory, outobj, executionDisposition)); // Store a copy of this verb output as a file in the real nuobj directory. Util.Assert(outobj.getRelativePath().StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal)); string nuobjPath = IronRootDirectory.PathTo(outobj); Directory.CreateDirectory(Path.GetDirectoryName(nuobjPath)); File.Copy(workingDirectory.PathTo(outobj), nuobjPath, true); } else { missingOutputs.Add(string.Format("Missing expected output {0}", outobj.getRelativePath())); } } else { hasVirtualOutputs = true; if (this.repository.GetDisposition(outobj) is Fresh) { // Nothing to cache; virtual objects only survive in the Repository, the in-process store. } else { missingOutputs.Add(string.Format("Missing expected virtual {0}", outobj.getRelativePath())); } } } if (!(executionDisposition is Failed) && missingOutputs.Count() > 0) { executionDisposition = new Failed(missingOutputs); } ResultSummaryRecord summary = new ResultSummaryRecord(verb, executionDisposition, outputs); string inputHash = this.computeInputHash(verb, true); Util.Assert(inputHash != null); if (!hasVirtualOutputs) { this.repository.StoreResult(inputHash, summary); } else { this.Say("Not caching verb persistently: " + verb); } this.verbIsComplete(verb, executionDisposition); return executionDisposition; }
public ObjectFailedException(BuildObject obj, Failed failed) : base(obj.ToString() + ": " + failed.ToString()) { }
public Disposition Complete(WorkingDirectory workingDirectory, double cpuTimeSeconds, string stdout, string stderr, Disposition disposition) { var dis = disposition; string cSharpPath = Path.ChangeExtension(workingDirectory.PathTo(this.expandedSource), CSharpExt); if (!File.Exists(cSharpPath)) { // Dafny has a bug where compilation fails but result code is still 0. dis = new Failed(); } if (dis is Fresh) { this.RewriteCSharpFile(cSharpPath, workingDirectory.PathTo(this.output)); } return dis; }