コード例 #1
0
 /// <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));
     }
 }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        /// <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);
            }
        }
コード例 #4
0
        /// <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>
        /// <returns>Returns the target root relative paths of all the restored files</returns>
        public ISet <TargetRelativePath> Restore(BuildKey builder, IFileSystemDirectory targetRoot)
        {
            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>());
            }
        }
コード例 #5
0
        /// <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();
            }
        }
コード例 #6
0
        /// <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);
        }
コード例 #7
0
ファイル: FileBuildCache.cs プロジェクト: zvrana/bari
        /// <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>
        /// <returns>Returns the target root relative paths of all the restored files</returns>
        public ISet <TargetRelativePath> Restore(BuildKey builder, IFileSystemDirectory targetRoot)
        {
            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(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))
                                    {
                                        CopyIfDifferent(cacheDir, cacheFileName, targetRoot, fullPath);
                                    }

                                    result.Add(new TargetRelativePath(relativeRoot, relativePath));
                                }
                                idx++;
                            }

                            return(result);
                        }
                    }
                }

                return(new HashSet <TargetRelativePath>());
            }
            finally
            {
                lck.ExitReadLock();
            }
        }
コード例 #8
0
        /// <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);
            }
        }
コード例 #9
0
 /// <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);
         }
     }
 }
コード例 #10
0
        /// <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();
            }
        }
コード例 #11
0
        /// <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.Value.GetChildDirectory(GetCacheDirectoryName(builder), createIfMissing: true);

                SaveDependencyFingerprint(fingerprint, cacheDir);
                SaveOutputs(outputs, targetRoot, cacheDir);
            }
            finally
            {
                lck.ExitWriteLock();
            }
        }
コード例 #12
0
        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);
            }
        }
コード例 #13
0
ファイル: FileBuildCache.cs プロジェクト: zvrana/bari
        /// <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();
            }
        }
コード例 #14
0
        /// <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();
            }
        }
コード例 #15
0
        /// <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();
        }
コード例 #16
0
 /// <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("/", "___"));
 }
コード例 #17
0
 /// <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();
 }
コード例 #18
0
 /// <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();
 }
コード例 #19
0
        /// <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);
            }
        }