public static void BuildContent(string workingPath, string buildCachePath, string runtimeCachePath, List <ContentBuildInput> contentToBuild, out List <ContentBuildResult> warnings, out List <ContentBuildResult> errors) { errors = new List <ContentBuildResult>(); warnings = new List <ContentBuildResult>(); bool newContentBuilt = false; ContentBuildCacheIO.Load(buildCachePath, out var loadedBuildCache); var newBuildCache = new Dictionary <string, ContentElement>(); foreach (var content in contentToBuild) { var currentTimeStamp = File.GetLastWriteTime(content.Path).Ticks; if (loadedBuildCache.TryGetValue(content.Path, out var cachedContent) && currentTimeStamp == cachedContent.LastModifiedTimestamp) { //We can just used the cached version of this content. newBuildCache.Add(content.Path, cachedContent); Console.WriteLine($"Content up to date: {content.Path}"); } else { //This is a new or modified content element, so we'll have to build it. ContentElement newElement; newElement.LastModifiedTimestamp = currentTimeStamp; using (var stream = File.OpenRead(content.Path)) { try { //Just like the ContentArchive, we're being a little lazy- this isn't very extensible, but there are very few content types. //If that changes, it would be pretty easy to open this up. switch (content.Type) { case ContentType.Font: newElement.Content = FontBuilder.Build(stream); break; case ContentType.Mesh: newElement.Content = MeshBuilder.Build(stream); break; case ContentType.Image: newElement.Content = Texture2DBuilder.Build(stream); break; case ContentType.GLSL: newElement.Content = GLSLBuilder.Build(stream); break; default: throw new ArgumentException("Requested content type does not have a registered builder."); } newBuildCache.Add(content.Path, newElement); newContentBuilt = true; Console.WriteLine($"Content built: {content.Path}"); } catch (Exception e) { //You could be a little more clever with these errors by letting builders report more specific problems. We can worry about that if this ever gets generalized. errors.Add(new ContentBuildResult { File = content.Path, Message = "Content build failed: " + e.Message }); } } } } //If we have new OR less content, the files should be rewritten. if (newContentBuilt || newBuildCache.Count < loadedBuildCache.Count) { var archive = new Dictionary <string, IContent>(); foreach (var pair in newBuildCache) { //Prune out all of the extra path bits and save it. var relativePath = ProjectBuilder.GetRelativePathFromDirectory(pair.Key, workingPath); archive.Add(relativePath.Replace(Path.DirectorySeparatorChar, '\\'), pair.Value.Content); } const int retryCount = 10; const int retryDelay = 200; for (int i = 0; i < retryCount; ++i) { try { ContentBuildCacheIO.Save(newBuildCache, buildCachePath); using (var stream = File.OpenWrite(runtimeCachePath)) { ContentArchive.Save(archive, stream); } break; } catch (IOException e) { Console.WriteLine($"Failed to write content caches (attempt {i}): {e.Message}, retrying..."); Thread.Sleep(retryDelay); } } } }
public static void BuildContent(string workingPath, string buildCachePath, string runtimeCachePath, List <string> fontPaths, out List <ContentBuildResult> warnings, out List <ContentBuildResult> errors) { errors = new List <ContentBuildResult>(); warnings = new List <ContentBuildResult>(); bool newContentBuilt = false; ContentBuildCacheIO.Load(buildCachePath, out var loadedBuildCache); var newBuildCache = new Dictionary <string, ContentElement>(); foreach (var path in fontPaths) { var currentTimeStamp = File.GetLastWriteTime(path).Ticks; if (loadedBuildCache.TryGetValue(path, out var cachedContent) && currentTimeStamp == cachedContent.LastModifiedTimestamp) { //We can just used the cached version of this content. newBuildCache.Add(path, cachedContent); Console.WriteLine($"Content up to date: {path}"); } else { //This is a new or modified content element, so we'll have to build it. ContentElement newElement; newElement.LastModifiedTimestamp = currentTimeStamp; using (var stream = File.OpenRead(path)) { //You could be a little more clever with these errors by letting builders report more specific problems. We can worry about that if this ever gets generalized. try { newElement.Content = FontBuilder.Build(stream); newBuildCache.Add(path, newElement); newContentBuilt = true; Console.WriteLine($"Content built: {path}"); } catch (Exception e) { errors.Add(new ContentBuildResult { File = path, Message = "Content build failed: " + e.Message }); } } } } //If we have new OR less content, the files should be rewritten. if (newContentBuilt || newBuildCache.Count < loadedBuildCache.Count) { var archive = new Dictionary <string, IContent>(); foreach (var pair in newBuildCache) { //Prune out all of the extra path bits and save it. var relativePath = ProjectBuilder.GetRelativePathFromDirectory(pair.Key, workingPath); archive.Add(relativePath, pair.Value.Content); } const int retryCount = 10; const int retryDelay = 200; for (int i = 0; i < retryCount; ++i) { try { ContentBuildCacheIO.Save(newBuildCache, buildCachePath); using (var stream = File.OpenWrite(runtimeCachePath)) { ContentArchive.Save(archive, stream); } break; } catch (IOException e) { Console.WriteLine($"Failed to write content caches (attempt {i}): {e.Message}, retrying..."); Thread.Sleep(retryDelay); } } } }