// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using Tools.DotNETCommon; namespace UnrealBuildTool { /// /// Prefetches metadata from the filesystem, by populating FileItem and DirectoryItem objects for requested directory trees. Since /// static class FileMetadataPrefetch { /// /// Queue for tasks added to the thread pool /// static ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue(); /// /// Used to cancel any queued tasks /// static CancellationTokenSource CancelSource = new CancellationTokenSource(); /// /// The cancellation token /// static CancellationToken CancelToken = CancelSource.Token; /// /// Set of all the directory trees that have been queued up, to save adding any more than once. /// static HashSet QueuedDirectories = new HashSet(); /// /// Enqueue the engine directory for prefetching /// public static void QueueEngineDirectory() { lock(QueuedDirectories) { if(QueuedDirectories.Add(UnrealBuildTool.EngineDirectory)) { Enqueue(() => ScanEngineDirectory()); } } } /// /// Enqueue a project directory for prefetching /// /// The project directory to prefetch public static void QueueProjectDirectory(DirectoryReference ProjectDirectory) { lock(QueuedDirectories) { if(QueuedDirectories.Add(ProjectDirectory)) { Enqueue(() => ScanProjectDirectory(DirectoryItem.GetItemByDirectoryReference(ProjectDirectory))); } } } /// /// Enqueue a directory tree for prefetching /// /// Directory to start searching from public static void QueueDirectoryTree(DirectoryReference Directory) { lock(QueuedDirectories) { if(QueuedDirectories.Add(Directory)) { Enqueue(() => ScanDirectoryTree(DirectoryItem.GetItemByDirectoryReference(Directory))); } } } /// /// Wait for the prefetcher to complete all reqeusted tasks /// public static void Wait() { Queue.Wait(); } /// /// Stop prefetching items, and cancel all pending tasks. synchronous. /// public static void Stop() { CancelSource.Cancel(); Queue.Wait(); } /// /// Enqueue a task which checks for the cancellation token first /// /// Action to enqueue static void Enqueue(System.Action Action) { Queue.Enqueue(() => { if(!CancelToken.IsCancellationRequested){ Action(); } }); } /// /// Scans the engine directory, adding tasks for subdirectories /// static void ScanEngineDirectory() { DirectoryItem EngineDirectory = DirectoryItem.GetItemByDirectoryReference(UnrealBuildTool.EngineDirectory); EngineDirectory.CacheDirectories(); DirectoryItem EnginePluginsDirectory = DirectoryItem.Combine(EngineDirectory, "Plugins"); Enqueue(() => ScanPluginFolder(EnginePluginsDirectory)); DirectoryItem EngineRuntimeDirectory = DirectoryItem.GetItemByDirectoryReference(UnrealBuildTool.EngineSourceRuntimeDirectory); Enqueue(() => ScanDirectoryTree(EngineRuntimeDirectory)); DirectoryItem EngineDeveloperDirectory = DirectoryItem.GetItemByDirectoryReference(UnrealBuildTool.EngineSourceDeveloperDirectory); Enqueue(() => ScanDirectoryTree(EngineDeveloperDirectory)); DirectoryItem EngineEditorDirectory = DirectoryItem.GetItemByDirectoryReference(UnrealBuildTool.EngineSourceEditorDirectory); Enqueue(() => ScanDirectoryTree(EngineEditorDirectory)); } /// /// Scans a project directory, adding tasks for subdirectories /// /// The project directory to search static void ScanProjectDirectory(DirectoryItem ProjectDirectory) { DirectoryItem ProjectPluginsDirectory = DirectoryItem.Combine(ProjectDirectory, "Plugins"); Enqueue(() => ScanPluginFolder(ProjectPluginsDirectory)); DirectoryItem ProjectSourceDirectory = DirectoryItem.Combine(ProjectDirectory, "Source"); Enqueue(() => ScanDirectoryTree(ProjectSourceDirectory)); } /// /// Scans a plugin parent directory, adding tasks for subdirectories /// /// The directory which may contain plugin directories static void ScanPluginFolder(DirectoryItem Directory) { foreach(DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { if(SubDirectory.EnumerateFiles().Any(x => x.HasExtension(".uplugin"))) { Enqueue(() => ScanDirectoryTree(DirectoryItem.Combine(SubDirectory, "Source"))); } else { Enqueue(() => ScanPluginFolder(SubDirectory)); } } } /// /// Scans an arbitrary directory tree /// /// Root of the directory tree static void ScanDirectoryTree(DirectoryItem Directory) { foreach(DirectoryItem SubDirectory in Directory.EnumerateDirectories()) { Enqueue(() => ScanDirectoryTree(SubDirectory)); } Directory.CacheFiles(); } } }