//- Contract: call only when output objects are known to be cached //- (because fetchResult returned non-Stale). public void fetchOutputObjects(IVerb verb, IEnumerable <BuildObjectValuePointer> values, Disposition disp) { if (this.alreadyFetchedVerbs.Contains(verb)) { return; } IEnumerable <BuildObject> objects = verb.getOutputs(); IEnumerable <BuildObject> failureObjs = verb.getFailureOutputs(); objects = objects.Concat(failureObjs); Dictionary <string, BuildObject> objectDict = new Dictionary <string, BuildObject>(); foreach (BuildObject obj in objects) { objectDict.Add(obj.getRelativePath(), obj); } HashSet <BuildObject> recorded = new HashSet <BuildObject>(); foreach (BuildObjectValuePointer value in values) { if (objectDict.ContainsKey(value.relativePath)) { BuildObject obj = objectDict[value.relativePath]; obj.prepareObjDirectory(); this.fetchObject(value, obj); nuObjectContents.bless(obj, value.objectHash, disp); recorded.Add(obj); } else { throw new Exception("Distressing: some BOVPs aren't in obj.getOutputs"); } } IEnumerable <BuildObject> unrecorded = objects.Except(recorded).Except(failureObjs); Util.Assert(unrecorded.Count() == 0 || disp is Failed); foreach (BuildObject obj in unrecorded) { nuObjectContents.bless(obj, null, disp); } this.alreadyFetchedVerbs.Add(verb); }
/// <summary> /// Adds the output objects from a cached verb execution to the /// repository, and ensures they are present in the item cache. /// </summary> /// <param name="verb">The verb whose outputs to add.</param> /// <param name="resultRecord"> /// The result summary record of the verb execution. /// </param> /// <remarks> /// Call only when output objects are known to be cached /// (i.e. because FetchResult returned non-Stale). /// REVIEW: This function probably shouldn't be in this file. /// It does something similar for cached verb results that /// the scheduler's recordResult method does for new verb /// executions. Move this there and/or refactor? /// </remarks> public void AddVerbResults(IVerb verb, ResultSummaryRecord resultRecord) { if (this.alreadyAddedVerbs.Contains(verb)) { // We only need to add a cached verb execution's outputs once. return; } Disposition disposition = resultRecord.Disposition; // REVIEW: In the below, some of these IEnumerables should be // HashSets, and the HashSet should be a simple List. // Create a collection of the potential outputs. IEnumerable <BuildObject> outputs = verb.getOutputs(); IEnumerable <BuildObject> failureOutputs = verb.getFailureOutputs(); outputs = outputs.Concat(failureOutputs); Dictionary <string, BuildObject> potentialOutputs = new Dictionary <string, BuildObject>(); foreach (BuildObject obj in outputs) { potentialOutputs.Add(obj.getRelativePath(), obj); } // Compare the actual outputs with the potential outputs, // and add the actual ones to the repository. HashSet <BuildObject> recorded = new HashSet <BuildObject>(); foreach (BuildObjectValuePointer actualOutput in resultRecord.Outputs) { if (potentialOutputs.ContainsKey(actualOutput.RelativePath)) { BuildObject obj = potentialOutputs[actualOutput.RelativePath]; // TODO: Verify that the object exists in the item cache! this.AddObject(obj, disposition, actualOutput.ObjectHash); recorded.Add(obj); // Store a copy of this verb output as a file in the real nuobj directory. Util.Assert(actualOutput.RelativePath.StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal)); this.itemCache.FetchItemToFile(ItemCacheContainer.Objects, actualOutput.ObjectHash, IronRootDirectory.PathTo(actualOutput.RelativePath)); } else { // Complain if we find interloping outputs. throw new Exception("Distressing: some actual verb outputs aren't in the verb's list of potential outputs"); } } // Create a collection of missing outputs. IEnumerable <BuildObject> unrecorded = outputs.Except(recorded).Except(failureOutputs); // For non-Failed verb runs, complain if all expected outputs don't // show up in the actual outputs. Util.Assert(unrecorded.Count() == 0 || disposition is Failed); // For cached verb runs with permanent failures (i.e. disposition // is Failed), we want to mark all of the expected outputs as Failed // even if no corresponding actual output was produced during the // failed verb run. foreach (BuildObject obj in unrecorded) { this.AddObject(obj, disposition, null); } // Remember that we've already added this verb's outputs. this.alreadyAddedVerbs.Add(verb); }
/// <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); }
/// <summary> /// Adds the output objects from a cached verb execution to the /// repository, and ensures they are present in the item cache. /// </summary> /// <param name="verb">The verb whose outputs to add.</param> /// <param name="resultRecord"> /// The result summary record of the verb execution. /// </param> /// <remarks> /// Call only when output objects are known to be cached /// (i.e. because FetchResult returned non-Stale). /// REVIEW: This function probably shouldn't be in this file. /// It does something similar for cached verb results that /// the scheduler's recordResult method does for new verb /// executions. Move this there and/or refactor? /// </remarks> public void AddVerbResults(IVerb verb, ResultSummaryRecord resultRecord) { if (this.alreadyAddedVerbs.Contains(verb)) { // We only need to add a cached verb execution's outputs once. return; } Disposition disposition = resultRecord.Disposition; // REVIEW: In the below, some of these IEnumerables should be // HashSets, and the HashSet should be a simple List. // Create a collection of the potential outputs. IEnumerable<BuildObject> outputs = verb.getOutputs(); IEnumerable<BuildObject> failureOutputs = verb.getFailureOutputs(); outputs = outputs.Concat(failureOutputs); Dictionary<string, BuildObject> potentialOutputs = new Dictionary<string, BuildObject>(); foreach (BuildObject obj in outputs) { potentialOutputs.Add(obj.getRelativePath(), obj); } // Compare the actual outputs with the potential outputs, // and add the actual ones to the repository. HashSet<BuildObject> recorded = new HashSet<BuildObject>(); foreach (BuildObjectValuePointer actualOutput in resultRecord.Outputs) { if (potentialOutputs.ContainsKey(actualOutput.RelativePath)) { BuildObject obj = potentialOutputs[actualOutput.RelativePath]; // TODO: Verify that the object exists in the item cache! this.AddObject(obj, disposition, actualOutput.ObjectHash); recorded.Add(obj); // Store a copy of this verb output as a file in the real nuobj directory. Util.Assert(actualOutput.RelativePath.StartsWith(BuildEngine.theEngine.getObjRoot(), StringComparison.Ordinal)); this.itemCache.FetchItemToFile(ItemCacheContainer.Objects, actualOutput.ObjectHash, IronRootDirectory.PathTo(actualOutput.RelativePath)); } else { // Complain if we find interloping outputs. throw new Exception("Distressing: some actual verb outputs aren't in the verb's list of potential outputs"); } } // Create a collection of missing outputs. IEnumerable<BuildObject> unrecorded = outputs.Except(recorded).Except(failureOutputs); // For non-Failed verb runs, complain if all expected outputs don't // show up in the actual outputs. Util.Assert(unrecorded.Count() == 0 || disposition is Failed); // For cached verb runs with permanent failures (i.e. disposition // is Failed), we want to mark all of the expected outputs as Failed // even if no corresponding actual output was produced during the // failed verb run. foreach (BuildObject obj in unrecorded) { this.AddObject(obj, disposition, null); } // Remember that we've already added this verb's outputs. this.alreadyAddedVerbs.Add(verb); }
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); }