/// <summary> /// Return the sequence of methods to call while building the target object. /// </summary> /// <param name="context">Current build context.</param> /// <returns>Sequence of methods to call.</returns> public IEnumerable <SelectedMethod> SelectMethods(IBuilderContext context) { foreach (Pair <MethodInfo, IEnumerable <InjectionParameterValue> > method in methods) { Type typeToBuild = BuildKey.GetType(context.BuildKey); SelectedMethod selectedMethod; ReflectionHelper typeReflector = new ReflectionHelper(method.First.DeclaringType); MethodReflectionHelper methodReflector = new MethodReflectionHelper(method.First); if (!methodReflector.MethodHasOpenGenericParameters && !typeReflector.IsOpenGeneric) { selectedMethod = new SelectedMethod(method.First); } else { Type[] closedMethodParameterTypes = methodReflector.GetClosedParameterTypes(typeToBuild.GetGenericArguments()); selectedMethod = new SelectedMethod( typeToBuild.GetMethod(method.First.Name, closedMethodParameterTypes)); } SpecifiedMemberSelectorHelper.AddParameterResolvers( typeToBuild, context.PersistentPolicies, method.Second, selectedMethod); yield return(selectedMethod); } }
public IEnumerable <SelectedProperty> SelectProperties(IBuilderContext context) { var t = BuildKey.GetType(context.BuildKey); var propertyNames = new HashSet <string>(); foreach (SelectedProperty prop in specifiedPropertiesPolicy.SelectProperties(context)) { if (!propertyNames.Contains(prop.Property.Name)) { propertyNames.Add(prop.Property.Name); yield return(prop); } } foreach (SelectedProperty prop in defaultProlicy.SelectProperties(context)) { if (!propertyNames.Contains(prop.Property.Name)) { yield return(prop); } } foreach (PropertyInfo prop in t.GetProperties(BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance)) { if (prop.GetIndexParameters().Length == 0 && prop.CanWrite && !ShoudBeIgnored(prop) && !propertyNames.Contains(prop.Name) && !prop.PropertyType.IsValueType && CanBeResolved(prop)) { yield return(CreateSelectedProperty(context, prop)); } } }
/// <summary> /// Checks if the cache contains stored outputs for a given builder with any dependency fingerprint /// </summary> /// <param name="builder">Builder key</param> /// <returns>Returns <c>true</c> if there are stored outputs for the given builder.</returns> public bool ContainsAny(BuildKey builder) { lock (cache) { return cache.ContainsKey(builder); } }
static void ExtractMetaToCache( List <ChangeInfo> changes, Dictionary <string, ChangeInfo> cache) { HashSet <string> indexedKeys = BuildIndexedKeys(changes); for (int i = changes.Count - 1; i >= 0; i--) { ChangeInfo currentChange = changes[i]; if (!MetaPath.IsMetaPath(currentChange.Path)) { continue; } string realPath = MetaPath.GetPathFromMetaPath(currentChange.Path); if (!indexedKeys.Contains(BuildKey.BuildCacheKey( currentChange.ChangeTypes, realPath))) { continue; } // found foo.c and foo.c.meta // with the same chage types - move .meta to cache cache.Add(BuildKey.ForChange(currentChange), currentChange); changes.RemoveAt(i); } }
private static IInstanceInterceptionPolicy FindInterceptorPolicy(IBuilderContext context, out Type typeToIntercept) { typeToIntercept = null; Type currentType = BuildKey.GetType(context.BuildKey); Type originalType = BuildKey.GetType(context.OriginalBuildKey); // First, try for a match against the current build key IInstanceInterceptionPolicy policy; policy = context.Policies.Get <IInstanceInterceptionPolicy>(context.BuildKey, false) ?? context.Policies.Get <IInstanceInterceptionPolicy>(currentType, false); if (policy != null) { typeToIntercept = currentType; return(policy); } // Next, try the original build key policy = context.Policies.Get <IInstanceInterceptionPolicy>(context.OriginalBuildKey, false) ?? context.Policies.Get <IInstanceInterceptionPolicy>(originalType, false); if (policy != null) { typeToIntercept = originalType; } return(policy); }
/// <summary> /// Runs this builder /// </summary> /// <param name="context"> </param> /// <returns>Returns a set of generated files, in suite relative paths</returns> public ISet<TargetRelativePath> Run(IBuildContext context) { var currentFingerprint = wrappedBuilder.Dependencies.CreateFingerprint(); var buildKey = new BuildKey(wrappedBuilder.GetType(), wrappedBuilder.Uid); cache.LockForBuilder(buildKey); try { if (cache.Contains(buildKey, currentFingerprint)) { log.DebugFormat("Restoring cached build outputs for {0}", buildKey); return cache.Restore(buildKey, targetDir); } else { log.DebugFormat("Running builder {0}", buildKey); var files = wrappedBuilder.Run(context); log.DebugFormat("Storing build outputs of {0}", buildKey); cache.Store(buildKey, currentFingerprint, files, targetDir); return files; } } finally { cache.UnlockForBuilder(buildKey); } }
public async Task QueueTriageBuildAsync(BuildKey buildKey) { var buildMessage = new BuildMessage(buildKey); var text = JsonConvert.SerializeObject(buildMessage); var queue = new QueueClient(_connectionString, QueueNameTriageBuild); await queue.SendMessageEncodedAsync(text).ConfigureAwait(false); }
public void CanChangeTypeOfTypeBuildKey() { Type t = typeof(string); Type t2 = (Type)BuildKey.ReplaceType(t, typeof(int)); Assert.AreEqual(typeof(int), t2); }
public SelectedConstructor SelectConstructor(IBuilderContext context) { var target = BuildKey.GetType(context.BuildKey); var typeTracker = context.Policies.Get <TypeTrackerPolicy>(context.BuildKey).TypeTracker; var constructor = SelectConstructor(target, typeTracker); if (constructor == null) { return(null); } // Unity includes a policy used when explicitly configuring injection. Here we borrow // it to specify the constructor we want to user. Normally the user specifies a specific // constructor and the policies for filling its parameters, here we do it automatically. var parameters = new List <InjectionParameterValue>(); foreach (var parameter in constructor.GetParameters()) { parameters.Add(new TypeToBeResolved( parameter.ParameterType, parameterResolver.GetResolver(parameter))); } return(new SpecifiedConstructorSelectorPolicy(constructor, parameters.ToArray()) .SelectConstructor(context)); }
/// <summary> /// Called during the chain of responsibility for a build operation. The /// PreBuildUp method is called when the chain is being executed in the /// forward direction. /// </summary> /// <param name="context">Context of the build operation.</param> public override void PreBuildUp(IBuilderContext context) { if (context.Existing == null) { context.Existing = Activator.CreateInstance(BuildKey.GetType(context.BuildKey)); } }
public void BuildKeyMethodsWrapTypeObjects() { Type t = typeof(string); Type t2 = BuildKey.GetType(t); Assert.AreEqual(t, t2); }
public async Task ProcessBuildAsync(BuildKey buildKey) { var modelBuildAttempts = await TriageContextUtil .GetModelBuildAttemptsQuery(buildKey) .Include(x => x.ModelBuild) .ToListAsync() .ConfigureAwait(false); var modelBuild = modelBuildAttempts.FirstOrDefault()?.ModelBuild; if (modelBuild is null) { // This happens when we have no data on the build at all Logger.LogWarning($"No model for the build {buildKey}"); return; } var failed = modelBuild.BuildResult == BuildResult.Failed || modelBuild.BuildResult == BuildResult.Canceled; if (!failed) { Logger.LogWarning($"Build did not fail so no retry is needed"); return; } await RetryOsxDeprovisionAsync(modelBuild, modelBuildAttempts); }
public void JobNameInFolder() { var jobId = JobId.ParseName("job/cat/job/dog"); var buildId = new BuildId(42, jobId); var buildKey = new BuildKey(buildId); Assert.False(AzureUtil.IsIllegalKey(buildKey.Key)); }
static void ExtractMetaToCache( MergeChangesCategory category, Dictionary <string, MergeChangeInfo> cache) { List <MergeChangeInfo> changes = category.GetChanges(); HashSet <string> indexedKeys = BuildIndexedKeys( changes); for (int i = changes.Count - 1; i >= 0; i--) { MergeChangeInfo currentChange = changes[i]; string path = currentChange.GetPath(); if (!MetaPath.IsMetaPath(path)) { continue; } string realPath = MetaPath.GetPathFromMetaPath(path); if (!indexedKeys.Contains(BuildKey.BuildCacheKey( currentChange.CategoryType, realPath))) { continue; } // found foo.c and foo.c.meta - move .meta to cache cache.Add(BuildKey.ForChange(currentChange), currentChange); changes.RemoveAt(i); } }
/// <summary> /// Store build outputs in the cache by reading them from the file system /// </summary> /// <param name="builder">Builder key (first part of the key)</param> /// <param name="fingerprint">Dependency fingerprint created when the builder was executed (second part of the key)</param> /// <param name="outputs">Target-relative path of the build outputs to be cached</param> /// <param name="targetRoot">File system abstraction of the root target directory</param> public void Store(BuildKey builder, IDependencyFingerprint fingerprint, IEnumerable<TargetRelativePath> outputs, IFileSystemDirectory targetRoot) { MemoryCacheItem item = GetOrCreate(builder); var map = new ConcurrentDictionary<TargetRelativePath, byte[]>(); Parallel.ForEach(outputs, outputPath => { if (targetRoot.Exists(outputPath)) { using (var stream = targetRoot.ReadBinaryFile(outputPath)) { var buf = new byte[stream.Length]; stream.Read(buf, 0, buf.Length); map.TryAdd(outputPath, buf); } } else { map.TryAdd(outputPath, null); } }); item.Update(fingerprint, map); }
public void Simple() { var jobId = JobId.ParseName("dog"); var buildId = new BuildId(42, jobId); var buildKey = new BuildKey(buildId); Assert.False(AzureUtil.IsIllegalKey(buildKey.Key)); Assert.Equal("42-dog", buildKey.Key); }
public IQueryable <ModelTestRun> GetModelTestRunQuery(BuildKey buildKey, int testRunId) { var buildId = GetModelBuildId(buildKey); return(Context .ModelTestRuns .Where(x => x.ModelBuildId == buildId && x.TestRunId == testRunId)); }
public override void PreBuildUp(IBuilderContext context) { Type type; if (context.Existing == null && BuildKey.TryGetType(context.BuildKey, out type)) { context.Existing = Activator.CreateInstance(type); } }
/// <summary> /// Called during the chain of responsibility for a build operation. The /// PreBuildUp method is called when the chain is being executed in the /// forward direction. /// </summary> /// <remarks>In this class, PreBuildUp is responsible for figuring out if the /// class is proxiable, and if so, replacing it with a proxy class.</remarks> /// <param name="context">Context of the build operation.</param> public override void PreBuildUp(IBuilderContext context) { Guard.ArgumentNotNull(context, "context"); if (context.Existing != null) { return; } Type typeToBuild; if (!BuildKey.TryGetType(context.BuildKey, out typeToBuild)) { return; } ITypeInterceptionPolicy interceptionPolicy = GetInterceptionPolicy(context); if (interceptionPolicy == null) { return; } if (!interceptionPolicy.Interceptor.CanIntercept(typeToBuild)) { return; } lock (lockObj) { if (interceptionPolicy.ProxyType == null) { Type typeToIntercept = typeToBuild; if (typeToIntercept.IsGenericType) { typeToIntercept = typeToIntercept.GetGenericTypeDefinition(); } interceptionPolicy.ProxyType = interceptionPolicy.Interceptor.CreateProxyType(typeToIntercept); } } Type interceptingType = interceptionPolicy.ProxyType; if (interceptingType.IsGenericTypeDefinition) { interceptingType = interceptingType.MakeGenericType(typeToBuild.GetGenericArguments()); } SelectedConstructor currentConstructor = GetCurrentConstructor(context); SelectedConstructor newConstructor = FindNewConstructor(currentConstructor, interceptingType); IConstructorSelectorPolicy newSelector = new ConstructorWithResolverKeysSelectorPolicy(newConstructor); context.Policies.Set(newSelector, context.BuildKey); }
private async Task <string> GetReportForHelixAsync(ModelTrackingIssue modelTrackingIssue, int limit) { Debug.Assert(modelTrackingIssue.TrackingKind == TrackingKind.HelixLogs); var matches = await Context .ModelTrackingIssueMatches .Where(x => x.ModelTrackingIssueId == modelTrackingIssue.Id) .OrderByDescending(x => x.ModelBuildAttempt.ModelBuild.BuildNumber) .Take(limit) .Select(x => new { AzureOrganization = x.ModelBuildAttempt.ModelBuild.AzureOrganization, AzureProject = x.ModelBuildAttempt.ModelBuild.AzureProject, DefinitionId = x.ModelBuildAttempt.ModelBuild.DefinitionId, DefinitionName = x.ModelBuildAttempt.ModelBuild.DefinitionName, GitHubOrganization = x.ModelBuildAttempt.ModelBuild.GitHubOrganization, GitHubRepository = x.ModelBuildAttempt.ModelBuild.GitHubRepository, GitHubPullRequestNumber = x.ModelBuildAttempt.ModelBuild.PullRequestNumber, GitHubTargetBranch = x.ModelBuildAttempt.ModelBuild.GitHubTargetBranch, BuildNumber = x.ModelBuildAttempt.ModelBuild.BuildNumber, HelixLogKind = x.HelixLogKind, HelixLogUri = x.HelixLogUri, }) .ToListAsync().ConfigureAwait(false); var map = new Dictionary <BuildKey, (BuildInfo BuildInfo, HelixLogInfo?HelixLogInfo)>(); var set = new HashSet <HelixLogKind>(); foreach (var item in matches) { var key = new BuildKey(item.AzureOrganization, item.AzureProject, item.BuildNumber); if (!map.TryGetValue(key, out var tuple)) { var buildInfo = new BuildInfo( key.Organization, key.Project, key.Number, new GitHubBuildInfo(item.GitHubOrganization, item.GitHubRepository, item.GitHubPullRequestNumber, item.GitHubTargetBranch)); tuple = (buildInfo, null); } set.Add(item.HelixLogKind); tuple.HelixLogInfo = tuple.HelixLogInfo is { } log ? log.SetUri(item.HelixLogKind, item.HelixLogUri) : new HelixLogInfo(item.HelixLogKind, item.HelixLogUri); map[key] = tuple; } var reportBuilder = new ReportBuilder(); return(reportBuilder.BuildSearchHelix( map.Values.OrderByDescending(x => x.BuildInfo.Number), set.ToArray(), markdown: true)); }
internal ClientDiffInfo GetExistingMeta(ClientDiffInfo diff) { ClientDiffInfo result; if (!mCache.TryGetValue(BuildKey.ForMetaDiff(diff), out result)) { return(null); } return(result); }
private static ITypeInterceptionPolicy GetInterceptionPolicy(IBuilderContext context) { ITypeInterceptionPolicy policy; policy = context.Policies.Get <ITypeInterceptionPolicy>(context.BuildKey, false); if (policy == null) { policy = context.Policies.Get <ITypeInterceptionPolicy>(BuildKey.GetType(context.BuildKey), false); } return(policy); }
internal MergeChangeInfo GetExistingMeta(MergeChangeInfo change) { MergeChangeInfo result; if (!mCache.TryGetValue(BuildKey.ForMetaChange(change), out result)) { return(null); } return(result); }
/// <summary> /// Construct a build plan. /// </summary> /// <param name="context">The current build context.</param> /// <param name="buildKey">The current build key.</param> /// <returns>The created build plan.</returns> public IBuildPlanPolicy CreatePlan(IBuilderContext context, object buildKey) { DynamicBuildPlanGenerationContext generatorContext = new DynamicBuildPlanGenerationContext( BuildKey.GetType(buildKey)); IBuilderContext planContext = GetContext(context, buildKey, generatorContext); planContext.Strategies.ExecuteBuildUp(planContext); return(new DynamicMethodBuildPlan(generatorContext.GetBuildMethod())); }
private static void AddPublicationsToPolicy(object buildKey, EventBrokerInfoPolicy policy) { var t = BuildKey.GetType(buildKey); foreach (var eventInfo in t.GetEvents()) { var attrs = (PublishesAttribute[])eventInfo.GetCustomAttributes(typeof(PublishesAttribute), true); foreach (var attr in attrs) { policy.AddPublication(attr.EventName, eventInfo); } } }
public async Task TriageAsync(BuildKey buildKey, int modelTrackingIssueId) { var attempts = await TriageContextUtil .GetModelBuildAttemptsQuery(buildKey) .Include(x => x.ModelBuild) .ToListAsync() .ConfigureAwait(false); foreach (var attempt in attempts) { await TriageAsync(attempt.GetBuildAttemptKey(), modelTrackingIssueId).ConfigureAwait(false); } }
private static void AddSubscriptionsToPolicy(object buildKey, EventBrokerInfoPolicy policy) { var t = BuildKey.GetType(buildKey); foreach (var method in t.GetMethods()) { var attrs = (SubscribesToAttribute[])method.GetCustomAttributes(typeof(SubscribesToAttribute), true); foreach (var attr in attrs) { policy.AddSubscription(attr.EventName, method); } } }
/// <summary> /// Creates an instance of the build plan's type by creating a WCF channel that implements it /// </summary> /// <param name="context">Context used to build up the object.</param> public void BuildUp(IBuilderContext context) { if (context.Existing == null) { IChannelManager channelManager = context.Lifetime.OfType <IChannelManager>().Single(); if (BuildKey.GetType(context.BuildKey).IsAssignableFrom(_ServiceInterfaceType) == false) { throw new Exception("A WcfProxyBuildPlanPolicy has been paired with an incorrect BuildKey. Its build key type is not assignable from the service interface type."); } context.Existing = channelManager.CreateChannel(_ServiceInterfaceType, _ChannelFactoryFactory); } }
/// <summary> /// Checks if the cache contains stored outputs for a given builder with a given dependency fingerprint /// /// <para>If <see cref="IBuildCache.Restore"/> will be also called, the cache must be locked first using /// the <see cref="IBuildCache.LockForBuilder"/> method.</para> /// </summary> /// <param name="builder">Builder key</param> /// <param name="fingerprint">Current dependency fingerprint</param> /// <returns>Returns <c>true</c> if there are stored outputs for the given builder and fingerprint combination.</returns> public bool Contains(BuildKey builder, IDependencyFingerprint fingerprint) { lock (cache) { MemoryCacheItem item; if (cache.TryGetValue(builder, out item)) { return item.MatchesFingerprint(fingerprint); } else { return false; } } }
public void SetUp() { cache = new MemoryBuildCache(); T = new BuildKey(typeof(IBuilder), "test"); root = new TestFileSystemDirectory("root"); fingerprint = new Mock <IDependencyFingerprint>(); otherFingerprint = new Mock <IDependencyFingerprint>(); fingerprint.Setup(f => f.Equals(fingerprint.Object)).Returns(true); fingerprint.Setup(f => f.Equals(otherFingerprint.Object)).Returns(false); otherFingerprint.Setup(f => f.Equals(fingerprint.Object)).Returns(false); otherFingerprint.Setup(f => f.Equals(otherFingerprint.Object)).Returns(true); }
public void SetUp() { cache = new MemoryBuildCache(); T = new BuildKey(typeof(IBuilder), "test"); root = new TestFileSystemDirectory("root"); fingerprint = new Mock<IDependencyFingerprint>(); otherFingerprint = new Mock<IDependencyFingerprint>(); fingerprint.Setup(f => f.Equals(fingerprint.Object)).Returns(true); fingerprint.Setup(f => f.Equals(otherFingerprint.Object)).Returns(false); otherFingerprint.Setup(f => f.Equals(fingerprint.Object)).Returns(false); otherFingerprint.Setup(f => f.Equals(otherFingerprint.Object)).Returns(true); }
/// <summary> /// Store build outputs in the cache by reading them from the file system /// </summary> /// <param name="builder">Builder key (first part of the key)</param> /// <param name="fingerprint">Dependency fingerprint created when the builder was executed (second part of the key)</param> /// <param name="outputs">Target-relative path of the build outputs to be cached</param> /// <param name="targetRoot">File system abstraction of the root target directory</param> public void Store(BuildKey builder, IDependencyFingerprint fingerprint, IEnumerable<TargetRelativePath> outputs, IFileSystemDirectory targetRoot) { var lck = GetOrCreateLock(builder); lck.EnterWriteLock(); try { var cacheDir = cacheRoot.GetChildDirectory(GetCacheDirectoryName(builder), createIfMissing: true); SaveDependencyFingerprint(fingerprint, cacheDir); SaveOutputs(outputs, targetRoot, cacheDir); } finally { lck.ExitWriteLock(); } }
HashSet <string> BuildIndexedKeys(List <ClientDiffInfo> diffs) { HashSet <string> result = new HashSet <string>(); foreach (ClientDiffInfo diff in diffs) { if (MetaPath.IsMetaPath(diff.DiffWithMount.Difference.Path)) { continue; } result.Add(BuildKey.ForDiff(diff)); } return(result); }
public override void PreBuildUp(IBuilderContext context) { Guard.ArgumentNotNull(context, "context"); Type typeToBuild = BuildKey.GetType(context.BuildKey); if (typeToBuild.IsArray && typeToBuild.GetArrayRank() == 1) { Type elementType = typeToBuild.GetElementType(); MethodInfo resolverMethod = genericResolveArrayMethod.MakeGenericMethod(elementType); ArrayResolver resolver = (ArrayResolver)Delegate.CreateDelegate(typeof(ArrayResolver), resolverMethod); context.Existing = resolver(context); context.BuildComplete = true; } }
static HashSet <string> BuildIndexedKeys( List <MergeChangeInfo> changes) { HashSet <string> result = new HashSet <string>(); foreach (MergeChangeInfo change in changes) { if (MetaPath.IsMetaPath(change.GetPath())) { continue; } result.Add(BuildKey.ForChange(change)); } return(result); }
public IEnumerable <SelectedProperty> SelectProperties(IBuilderContext context) { var target = BuildKey.GetType(context.BuildKey); var typeTracker = context.Policies.Get <TypeTrackerPolicy>(context.BuildKey).TypeTracker; // Unity includes a policy used when explicitly configuring injection. Here we borrow // it to specify the properties we want to fill. Normally the user specifies specific // properties and the policies for filling them, here we do it automatically. var policy = new SpecifiedPropertiesSelectorPolicy(); foreach (var property in target.GetProperties()) { // Ignore indexed properties if (property.GetIndexParameters().Length > 0) { continue; } // Ignore read only properties if (!property.CanWrite) { continue; } // Ignore properties we can't fill anyway if (!typeTracker.HasDependency(property.PropertyType)) { continue; } // Ignore properties that would create obvious circular dependencies. It is still // possible to create one if you have multiple classes referring to each other though. if (property.PropertyType == property.DeclaringType || property.DeclaringType.IsAssignableFrom(property.PropertyType)) { continue; } policy.AddPropertyAndValue(property, new TypeToBeResolved( property.PropertyType, GetResolver(property))); } return(policy.SelectProperties(context)); }
/// <summary> /// Checks if the cache contains stored outputs for a given builder with a given dependency fingerprint /// /// <para>If <see cref="IBuildCache.Restore"/> will be also called, the cache must be locked first using /// the <see cref="IBuildCache.LockForBuilder"/> method.</para> /// </summary> /// <param name="builder">Builder key</param> /// <param name="fingerprint">Current dependency fingerprint</param> /// <returns>Returns <c>true</c> if there are stored outputs for the given builder and fingerprint combination.</returns> public bool Contains(BuildKey builder, IDependencyFingerprint fingerprint) { var lck = GetOrCreateLock(builder); lck.EnterReadLock(); try { var dirName = GetCacheDirectoryName(builder); if (cacheRoot.Value.ChildDirectories.Contains(dirName)) { var cacheDir = cacheRoot.Value.GetChildDirectory(dirName); if (cacheDir.Files.Contains(DepsFileName)) { using (var depsStream = cacheDir.ReadBinaryFile(DepsFileName)) using (var memStream = new MemoryStream()) { fingerprint.Save(protocolSerializer, memStream); if (depsStream.Length != memStream.Length) return false; var buf1 = memStream.ToArray(); var buf2 = new byte[depsStream.Length]; depsStream.Read(buf2, 0, (int) depsStream.Length); for (int i = 0; i < buf1.Length; i++) if (buf1[i] != buf2[i]) return false; return true; } } } return false; } finally { lck.ExitReadLock(); } }
/// <summary> /// Runs this builder /// </summary> /// <param name="context"> </param> /// <returns>Returns a set of generated files, in suite relative paths</returns> public ISet<TargetRelativePath> Run(IBuildContext context) { var buildKey = new BuildKey(wrappedBuilder.BuilderType, wrappedBuilder.Uid); cache.LockForBuilder(buildKey); try { if (wrappedBuilder.CanRun()) { try { var currentFingerprint = wrappedBuilder.Dependencies.Fingerprint; if (cache.Contains(buildKey, currentFingerprint)) { log.DebugFormat("Restoring cached build outputs for {0}", buildKey); return cache.Restore(buildKey, targetDir, aggressive, agressiveModeExceptions); } else { log.DebugFormat("Running builder {0}", buildKey); var files = wrappedBuilder.Run(context); log.DebugFormat("Storing build outputs of {0}", buildKey); cache.Store(buildKey, currentFingerprint, files, targetDir); return files; } } catch (Exception ex) { log.ErrorFormat("Failed to run builder {0}: {1}", wrappedBuilder.Uid, ex); // Fallback to any cached value if (SupportsFallback && cache.ContainsAny(buildKey)) { log.DebugFormat("Restoring cached build outputs for {0} without fingerprint check", buildKey); return cache.Restore(buildKey, targetDir, aggressive, agressiveModeExceptions); } else { throw; } } } else { // Fallback to any cached value if (cache.ContainsAny(buildKey)) { log.DebugFormat("Restoring cached build outputs for {0} without fingerprint check", buildKey); return cache.Restore(buildKey, targetDir, aggressive, agressiveModeExceptions); } else { throw new BuilderCantRunException(wrappedBuilder.Uid); } } } finally { cache.UnlockForBuilder(buildKey); } }
/// <summary> /// Verifies if the builder is able to run. Can be used to fallback to cached results without getting en error. /// </summary> /// <returns>If <c>true</c>, the builder thinks it can run.</returns> public bool CanRun() { var buildKey = new BuildKey(wrappedBuilder.BuilderType, wrappedBuilder.Uid); cache.LockForBuilder(buildKey); try { return wrappedBuilder.CanRun() || cache.ContainsAny(buildKey); } finally { cache.UnlockForBuilder(buildKey); } }
/// <summary> /// Locks the cache for a given builder. /// /// <para>Until calling <see cref="IBuildCache.UnlockForBuilder"/>, it is guaranteed that no /// <see cref="IBuildCache.Store"/> operation will be ran for the given builder from other /// threads.</para> /// </summary> /// <param name="builder">Builder key</param> public void LockForBuilder(BuildKey builder) { GetOrCreate(builder).EnterUpgradeableLock(); }
/// <summary> /// Removes the lock put by the <see cref="IBuildCache.LockForBuilder"/> method. /// </summary> /// <param name="builder">Builder key</param> public void UnlockForBuilder(BuildKey builder) { GetOrCreate(builder).ExitUpgradeableLock(); }
/// <summary> /// Locks the cache for a given builder. /// /// <para>Until calling <see cref="IBuildCache.UnlockForBuilder"/>, it is guaranteed that no /// <see cref="IBuildCache.Store"/> operation will be ran for the given builder from other /// threads.</para> /// </summary> /// <param name="builder">Builder key</param> public void LockForBuilder(BuildKey builder) { var lck = GetOrCreateLock(builder); lck.EnterUpgradeableReadLock(); }
/// <summary> /// Checks if the cache contains stored outputs for a given builder with any dependency fingerprint /// </summary> /// <param name="builder">Builder key</param> /// <returns>Returns <c>true</c> if there are stored outputs for the given builder.</returns> public bool ContainsAny(BuildKey builder) { var lck = GetOrCreateLock(builder); lck.EnterReadLock(); try { var dirName = GetCacheDirectoryName(builder); if (cacheRoot.Value.ChildDirectories.Contains(dirName)) { var cacheDir = cacheRoot.Value.GetChildDirectory(dirName); if (cacheDir.Files.Contains(DepsFileName)) { return true; } } return false; } finally { lck.ExitReadLock(); } }
/// <summary> /// Checks if the cache contains stored outputs for a given builder with a given dependency fingerprint /// /// <para>If <see cref="IBuildCache.Restore"/> will be also called, the cache must be locked first using /// the <see cref="IBuildCache.LockForBuilder"/> method.</para> /// </summary> /// <param name="builder">Builder key</param> /// <param name="fingerprint">Current dependency fingerprint</param> /// <returns>Returns <c>true</c> if there are stored outputs for the given builder and fingerprint combination.</returns> public bool Contains(BuildKey builder, IDependencyFingerprint fingerprint) { var lck = GetOrCreateLock(builder); lck.EnterReadLock(); try { var dirName = GetCacheDirectoryName(builder); if (cacheRoot.ChildDirectories.Contains(dirName)) { var cacheDir = cacheRoot.GetChildDirectory(dirName); if (cacheDir.Files.Contains(DepsFileName)) { using (var depsStream = cacheDir.ReadBinaryFile(DepsFileName)) { var fpType = fingerprint.GetType(); var storedFp = Activator.CreateInstance(fpType, protocolSerializer, depsStream); bool fingerprintEquals = fingerprint.Equals(storedFp); if (!fingerprintEquals && EnableFingerprintDiff) { log.DebugFormat("[{0}] Fingerprint differs", dirName); log.DebugFormat("[{1}] Cached: {0}", storedFp, dirName); log.DebugFormat("[{1}] Current: {0}", fingerprint, dirName); } return fingerprintEquals; } } } return false; } finally { lck.ExitReadLock(); } }
/// <summary> /// Restores the stored files for a given builder to a file system directory /// /// <para>The cache only stores the latest stored results and this is what will be restored /// to the target directory. To verify if it was generated with the correct dependency fingerprint, /// use <see cref="IBuildCache.Contains"/>.</para> /// <para>To ensure thread safety, use <see cref="IBuildCache.LockForBuilder"/>.</para> /// </summary> /// <param name="builder">Builder key</param> /// <param name="targetRoot">Target file system directory</param> /// <param name="aggressive">If <c>true</c>, files in the target directory won't be checked by hash before overriding them</param> /// <param name="aggressiveExceptions">Exceptions to the aggresivve mode. Can be <c>null</c> if not used.</param> /// <returns>Returns the target root relative paths of all the restored files</returns> public ISet<TargetRelativePath> Restore(BuildKey builder, IFileSystemDirectory targetRoot, bool aggressive, Regex[] aggressiveExceptions = null) { var lck = GetOrCreateLock(builder); lck.EnterReadLock(); try { var dirName = GetCacheDirectoryName(builder); if (cacheRoot.Value.ChildDirectories.Contains(dirName)) { var cacheDir = cacheRoot.Value.GetChildDirectory(dirName); if (cacheDir.Files.Contains(NamesFileName)) { using (var reader = cacheDir.ReadTextFile(NamesFileName)) { var result = new HashSet<TargetRelativePath>(); int idx = 0; string line; while ((line = reader.ReadLine()) != null) { var parts = line.Split(';'); if (parts.Length == 2) { var relativeRoot = parts[0]; var relativePath = parts[1]; var fullPath = Path.Combine(relativeRoot, relativePath); var cacheFileName = idx.ToString(CultureInfo.InvariantCulture); // It is possible that only a file name (a virtual file) was cached without any contents: if (cacheDir.Exists(cacheFileName)) { if (aggressive && BeAggressive(aggressiveExceptions, fullPath)) cacheDir.CopyFile(cacheFileName, targetRoot, fullPath); else { if (aggressive) log.DebugFormat("Agressive cache restore is disabled for file {0}", fullPath); CopyIfDifferent(cacheDir, cacheFileName, targetRoot, fullPath); } } result.Add(new TargetRelativePath(relativeRoot, relativePath)); } idx++; } return result; } } } return new HashSet<TargetRelativePath>(); } finally { lck.ExitReadLock(); } }
/// <summary> /// Removes the lock put by the <see cref="IBuildCache.LockForBuilder"/> method. /// </summary> /// <param name="builder">Builder key</param> public void UnlockForBuilder(BuildKey builder) { ReaderWriterLockSlim lck; if (locks.TryGetValue(builder, out lck)) lck.ExitUpgradeableReadLock(); }
/// <summary> /// Gets the directory name associated with a given Builder key /// </summary> /// <param name="builder">Builder key</param> /// <returns>Returns a valid directory name</returns> private static string GetCacheDirectoryName(BuildKey builder) { return builder.ToString().Replace("/", "___"); }
/// <summary> /// Gets an existing lock or creates a new one /// </summary> /// <param name="builder">Builder key used as a key to get locks</param> /// <returns>Returns a reader-writer lock</returns> private ReaderWriterLockSlim GetOrCreateLock(BuildKey builder) { ReaderWriterLockSlim lck; if (!locks.TryGetValue(builder, out lck)) locks.Add(builder, lck = new ReaderWriterLockSlim()); return lck; }
/// <summary> /// Restores the stored files for a given builder to a file system directory /// /// <para>The cache only stores the latest stored results and this is what will be restored /// to the target directory. To verify if it was generated with the correct dependency fingerprint, /// use <see cref="IBuildCache.Contains"/>.</para> /// <para>To ensure thread safety, use <see cref="IBuildCache.LockForBuilder"/>.</para> /// </summary> /// <param name="builder">Builder key</param> /// <param name="targetRoot">Target file system directory</param> /// <param name="aggressive">If <c>true</c>, files in the target directory won't be checked by hash before overriding them</param> /// <param name="aggressiveExceptions">Exceptions to the aggresivve mode. Can be <c>null</c> if not used.</param> /// <returns>Returns the target root relative paths of all the restored files</returns> public ISet<TargetRelativePath> Restore(BuildKey builder, IFileSystemDirectory targetRoot, bool aggressive, Regex[] aggressiveExceptions = null) { MemoryCacheItem item; lock (cache) cache.TryGetValue(builder, out item); if (item != null) { var outputs = item.Outputs; var paths = new HashSet<TargetRelativePath>(); foreach (var pair in outputs) { if (pair.Value != null) { using (var stream = targetRoot.CreateBinaryFile(pair.Key)) stream.Write(pair.Value, 0, pair.Value.Length); } paths.Add(pair.Key); } return paths; } else { return new HashSet<TargetRelativePath>(); } }
private MemoryCacheItem GetOrCreate(BuildKey builder) { Contract.Ensures(Contract.Result<MemoryCacheItem>() != null); lock (cache) { MemoryCacheItem item; if (!cache.TryGetValue(builder, out item)) { item = new MemoryCacheItem(); cache.Add(builder, item); } Contract.Assume(item != null); return item; } }