internal static CacheEntry CreateFromStream(BinaryReader reader) { CacheEntryTypes entryType = (CacheEntryTypes)reader.ReadByte(); CacheEntry entry = null; switch (entryType) { case CacheEntryTypes.BuildItem: entry = new BuildItemCacheEntry(); break; case CacheEntryTypes.BuildResult: entry = new BuildResultCacheEntry(); break; case CacheEntryTypes.Property: entry = new PropertyCacheEntry(); break; default: ErrorUtilities.VerifyThrow(false, "Should not get to the default of CacheEntryCustomSerializer CreateFromStream"); break; } entry.CreateFromStream(reader); return(entry); }
/// <summary> /// Get a cached build result if available for the given request. This method is thread safe. /// </summary> /// <param name="buildRequest"></param> /// <param name="actuallyBuiltTargets"></param> /// <returns></returns> internal BuildResult GetCachedBuildResult(BuildRequest buildRequest, out ArrayList actuallyBuiltTargets) { actuallyBuiltTargets = null; PropertyCacheEntry defaultTargetsCacheEntry, initialTargetsCacheEntry, projectIdCacheEntry; // No writes here, but since we're reading multiple values we want to get a consistent view of the cache cacheScopeReaderWriterLock.AcquireReaderLock(Timeout.Infinite); try { defaultTargetsCacheEntry = (PropertyCacheEntry)GetCacheEntry(Constants.defaultTargetCacheName); initialTargetsCacheEntry = (PropertyCacheEntry)GetCacheEntry(Constants.initialTargetCacheName); projectIdCacheEntry = (PropertyCacheEntry)GetCacheEntry(Constants.projectIdCacheName); } finally { cacheScopeReaderWriterLock.ReleaseReaderLock(); } // If we ever built anything in this project we must have the default and initial targets. if (defaultTargetsCacheEntry == null && initialTargetsCacheEntry == null) { return(null); } ErrorUtilities.VerifyThrow(projectIdCacheEntry != null, "We should always have the projectId cache entry"); ErrorUtilities.VerifyThrow(defaultTargetsCacheEntry != null && initialTargetsCacheEntry != null, "We should have both the initial and default targets in the cache"); ArrayList targetsToBuild = new ArrayList(initialTargetsCacheEntry.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); if (buildRequest.TargetNames == null || buildRequest.TargetNames.Length == 0) { targetsToBuild.AddRange(defaultTargetsCacheEntry.Value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); } else { targetsToBuild.AddRange(buildRequest.TargetNames); } // Create variable to hold the cached outputs Hashtable outputsByTargetName = new Hashtable(targetsToBuild.Count); Hashtable resultByTarget = new Hashtable(targetsToBuild.Count, StringComparer.OrdinalIgnoreCase); bool overallSuccess = true; bool missingValues = false; // No writes here, but since we're reading multiple values we want to get a consistent view of the cache cacheScopeReaderWriterLock.AcquireReaderLock(Timeout.Infinite); try { for (int i = 0; i < targetsToBuild.Count; i++) { string targetName = EscapingUtilities.UnescapeAll((string)targetsToBuild[i]); if (ContainsCacheEntry(targetName)) { BuildResultCacheEntry cacheEntry = (BuildResultCacheEntry)GetCacheEntry(targetName); overallSuccess = overallSuccess && cacheEntry.BuildResult; resultByTarget[targetName] = (cacheEntry.BuildResult) ? Target.BuildState.CompletedSuccessfully : Target.BuildState.CompletedUnsuccessfully; // Restore output items for successful targets if (cacheEntry.BuildResult) { outputsByTargetName[targetName] = cacheEntry.BuildItems; } // We found a failed target - cut the loop short else { break; } } else { missingValues = true; break; } } } finally { cacheScopeReaderWriterLock.ReleaseReaderLock(); } if (missingValues) { return(null); } actuallyBuiltTargets = targetsToBuild; return(new BuildResult(outputsByTargetName, resultByTarget, overallSuccess, buildRequest.HandleId, buildRequest.RequestId, int.Parse(projectIdCacheEntry.Value, CultureInfo.InvariantCulture), false /* use results cache */, defaultTargetsCacheEntry.Value, initialTargetsCacheEntry.Value, 0, 0, 0)); }
/// <summary> /// This method adds cached results for each target results for which are contained inside /// the build result. This method is thread safe. /// </summary> internal void AddCacheEntryForBuildResults(BuildResult buildResult) { ErrorUtilities.VerifyThrow(buildResult != null, "Expect a non-null build result"); // Don't cache results if they are marked as uncacheable if (!buildResult.UseResultCache) { return; } cacheScopeReaderWriterLock.AcquireWriterLock(Timeout.Infinite); try { if (!ContainsCacheEntry(Constants.defaultTargetCacheName)) { // If the project file is malformed the build may fail without initializing the initialtargets or // the default targests fields. The retrieval code expects non-null values // so it is necessary to replace null with empty string ErrorUtilities.VerifyThrow(buildResult.EvaluationResult == false || buildResult.InitialTargets != null && buildResult.DefaultTargets != null, "Expect initial targets to be non-null for successful builds"); string defaultTargets = buildResult.DefaultTargets == null ? String.Empty : buildResult.DefaultTargets; PropertyCacheEntry defaultTargetsCacheEntry = new PropertyCacheEntry(Constants.defaultTargetCacheName, defaultTargets); AddCacheEntryInternal(defaultTargetsCacheEntry); string initialTargets = buildResult.InitialTargets == null ? String.Empty : buildResult.InitialTargets; PropertyCacheEntry initialTargetsCacheEntry = new PropertyCacheEntry(Constants.initialTargetCacheName, initialTargets); AddCacheEntryInternal(initialTargetsCacheEntry); } if (!ContainsCacheEntry(Constants.projectIdCacheName)) { PropertyCacheEntry projectIdCacheEntry = new PropertyCacheEntry(Constants.projectIdCacheName, buildResult.ProjectId.ToString(CultureInfo.InvariantCulture)); AddCacheEntryInternal(projectIdCacheEntry); } IDictionary outputsByTargetName = buildResult.OutputsByTarget; //Create single entry for each target in the request foreach (DictionaryEntry entry in buildResult.ResultByTarget) { Target.BuildState buildState = (Target.BuildState)entry.Value; // Only cache successful and failed targets if ((buildState == Target.BuildState.CompletedSuccessfully) || (buildState == Target.BuildState.CompletedUnsuccessfully)) { BuildItem[] targetOutputs = null; // Only cache output items for successful targets if (buildState == Target.BuildState.CompletedSuccessfully) { ErrorUtilities.VerifyThrow(buildResult.OutputsByTarget.Contains(entry.Key), "We must have build results for successful targets"); BuildItem[] outputItems = (BuildItem[])buildResult.OutputsByTarget[entry.Key]; // It's essential that we clear out any pointers to the project from the BuildItem; // otherwise the cache will hold onto the project, and not save any memory. if (outputItems != null) { for (int i = 0; i < outputItems.Length; i++) { outputItems[i] = outputItems[i].VirtualClone(true /* remove references to minimise transitive size */); } } targetOutputs = (BuildItem[])buildResult.OutputsByTarget[entry.Key]; } BuildResultCacheEntry cacheEntry = new BuildResultCacheEntry((string)entry.Key, targetOutputs, (buildState == Target.BuildState.CompletedSuccessfully)); if (Engine.debugMode) { Console.WriteLine("+++Adding cache entry for " + (string)entry.Key + " in " + this.ScopeName + " result: " + (buildState == Target.BuildState.CompletedSuccessfully)); } AddCacheEntryInternal(cacheEntry); } } } finally { cacheScopeReaderWriterLock.ReleaseWriterLock(); } }