// Copyright Epic Games, Inc. All Rights Reserved.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using EpicGames.Core;
using UnrealBuildBase;
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(Unreal.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()
{
foreach (DirectoryReference ExtensionDir in Unreal.GetExtensionDirs(Unreal.EngineDirectory))
{
DirectoryItem BaseDirectory = DirectoryItem.GetItemByDirectoryReference(ExtensionDir);
BaseDirectory.CacheDirectories();
DirectoryItem BasePluginsDirectory = DirectoryItem.Combine(BaseDirectory, "Plugins");
Enqueue(() => ScanPluginFolder(BasePluginsDirectory));
DirectoryItem BaseSourceDirectory = DirectoryItem.Combine(BaseDirectory, "Source");
BaseSourceDirectory.CacheDirectories();
DirectoryItem BaseSourceRuntimeDirectory = DirectoryItem.Combine(BaseSourceDirectory, "Runtime");
Enqueue(() => ScanDirectoryTree(BaseSourceRuntimeDirectory));
DirectoryItem BaseSourceDeveloperDirectory = DirectoryItem.Combine(BaseSourceDirectory, "Developer");
Enqueue(() => ScanDirectoryTree(BaseSourceDeveloperDirectory));
DirectoryItem BaseSourceEditorDirectory = DirectoryItem.Combine(BaseSourceDirectory, "Editor");
Enqueue(() => ScanDirectoryTree(BaseSourceEditorDirectory));
}
}
///
/// Scans a project directory, adding tasks for subdirectories
///
/// The project directory to search
static void ScanProjectDirectory(DirectoryItem ProjectDirectory)
{
foreach (DirectoryReference ExtensionDir in Unreal.GetExtensionDirs(ProjectDirectory.Location))
{
DirectoryItem BaseDirectory = DirectoryItem.GetItemByDirectoryReference(ExtensionDir);
BaseDirectory.CacheDirectories();
DirectoryItem BasePluginsDirectory = DirectoryItem.Combine(BaseDirectory, "Plugins");
Enqueue(() => ScanPluginFolder(BasePluginsDirectory));
DirectoryItem BaseSourceDirectory = DirectoryItem.Combine(BaseDirectory, "Source");
Enqueue(() => ScanDirectoryTree(BaseSourceDirectory));
}
}
///
/// Scans a plugin parent directory, adding tasks for subdirectories
///
/// The directory which may contain plugin directories
static void ScanPluginFolder(DirectoryItem Directory)
{
if (CancelToken.IsCancellationRequested || Directory.TryGetFile(".ubtignore", out FileItem? _))
{
return;
}
foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories())
{
if (SubDirectory.EnumerateFiles().Any((fi) => fi.HasExtension(".uplugin")))
{
Enqueue(() => ScanDirectoryTree(DirectoryItem.Combine(SubDirectory, "Source")));
}
else if (!SubDirectory.TryGetFile(".ubtignore", out FileItem? OutFile))
{
Enqueue(() => ScanPluginFolder(SubDirectory));
}
}
}
///
/// Scans an arbitrary directory tree
///
/// Root of the directory tree
static void ScanDirectoryTree(DirectoryItem Directory)
{
if (CancelToken.IsCancellationRequested || Directory.TryGetFile(".ubtignore", out FileItem? _))
{
return;
}
foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories())
{
Enqueue(() => ScanDirectoryTree(SubDirectory));
}
}
}
}