// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using EpicGames.Core;
namespace UnrealBuildTool
{
///
/// Class which compiles (and caches) rules assemblies for different folders.
///
public class RulesCompiler
{
///
/// Enum for types of rules files. Should match extensions in RulesFileExtensions.
///
public enum RulesFileType
{
///
/// .build.cs files
///
Module,
///
/// .target.cs files
///
Target,
///
/// .automation.csproj files
///
AutomationModule
}
///
/// Cached list of rules files in each directory of each type
///
class RulesFileCache
{
public List ModuleRules = new List();
public List TargetRules = new List();
public List AutomationModules = new List();
}
/// Map of root folders to a cached list of all UBT-related source files in that folder or any of its sub-folders.
/// We cache these file names so we can avoid searching for them later on.
static Dictionary RootFolderToRulesFileCache = new Dictionary();
///
///
///
const string FrameworkAssemblyExtension = ".dll";
///
///
///
///
///
///
///
///
/// Whether to include targets generated by UAT to accomodate content-only projects that need to be compiled to include plugins
///
public static List FindAllRulesSourceFiles(RulesFileType RulesFileType, List GameFolders, List ForeignPlugins, List AdditionalSearchPaths, bool bIncludeEngine = true, bool bIncludeTempTargets = true)
{
List Folders = new List();
// Add all engine source (including third party source)
if (bIncludeEngine)
{
Folders.AddRange(UnrealBuildTool.GetExtensionDirs(UnrealBuildTool.EngineDirectory, "Source"));
}
// @todo plugin: Disallow modules from including plugin modules as dependency modules? (except when the module is part of that plugin)
// Get all the root folders for plugins
List RootFolders = new List();
if (bIncludeEngine)
{
RootFolders.AddRange(UnrealBuildTool.GetExtensionDirs(UnrealBuildTool.EngineDirectory));
}
if (GameFolders != null)
{
RootFolders.AddRange(GameFolders.SelectMany(x => UnrealBuildTool.GetExtensionDirs(x)));
}
// Find all the plugin source directories
foreach (DirectoryReference RootFolder in RootFolders)
{
DirectoryReference PluginsFolder = DirectoryReference.Combine(RootFolder, "Plugins");
foreach (FileReference PluginFile in Plugins.EnumeratePlugins(PluginsFolder))
{
Folders.Add(DirectoryReference.Combine(PluginFile.Directory, "Source"));
}
}
// Add all the extra plugin folders
if (ForeignPlugins != null)
{
foreach (FileReference ForeignPlugin in ForeignPlugins)
{
Folders.Add(DirectoryReference.Combine(ForeignPlugin.Directory, "Source"));
}
}
// Add in the game folders to search
if (GameFolders != null)
{
foreach (DirectoryReference GameFolder in GameFolders)
{
Folders.AddRange(UnrealBuildTool.GetExtensionDirs(GameFolder, "Source"));
if (bIncludeTempTargets)
{
DirectoryReference GameIntermediateSourceFolder = DirectoryReference.Combine(GameFolder, "Intermediate", "Source");
Folders.Add(GameIntermediateSourceFolder);
}
}
}
// Process the additional search path, if sent in
if (AdditionalSearchPaths != null)
{
foreach (DirectoryReference AdditionalSearchPath in AdditionalSearchPaths)
{
if (AdditionalSearchPath != null)
{
if (DirectoryReference.Exists(AdditionalSearchPath))
{
Folders.Add(AdditionalSearchPath);
}
else
{
throw new BuildException("Couldn't find AdditionalSearchPath for rules source files '{0}'", AdditionalSearchPath);
}
}
}
}
// Iterate over all the folders to check
List SourceFiles = new List();
HashSet UniqueSourceFiles = new HashSet();
foreach (DirectoryReference Folder in Folders)
{
IReadOnlyList SourceFilesForFolder = FindAllRulesFiles(Folder, RulesFileType);
foreach (FileReference SourceFile in SourceFilesForFolder)
{
if (UniqueSourceFiles.Add(SourceFile))
{
SourceFiles.Add(SourceFile);
}
}
}
return SourceFiles;
}
///
/// Invalidate the cache for the givcen directory
///
/// Directory to invalidate
public static void InvalidateRulesFileCache(string DirectoryPath)
{
DirectoryReference Directory = new DirectoryReference(DirectoryPath);
RootFolderToRulesFileCache.Remove(Directory);
DirectoryLookupCache.InvalidateCachedDirectory(Directory);
}
///
/// Prefetch multiple directories in parallel
///
/// The directories to cache
private static void PrefetchRulesFiles(IEnumerable Directories)
{
ThreadPoolWorkQueue? Queue = null;
try
{
foreach(DirectoryReference Directory in Directories)
{
if(!RootFolderToRulesFileCache.ContainsKey(Directory))
{
RulesFileCache Cache = new RulesFileCache();
RootFolderToRulesFileCache[Directory] = Cache;
if(Queue == null)
{
Queue = new ThreadPoolWorkQueue();
}
DirectoryItem DirectoryItem = DirectoryItem.GetItemByDirectoryReference(Directory);
Queue.Enqueue(() => FindAllRulesFilesRecursively(DirectoryItem, Cache, Queue));
}
}
}
finally
{
if(Queue != null)
{
Queue.Dispose();
Queue = null;
}
}
}
///
/// Finds all the rules of the given type under a given directory
///
/// Directory to search
/// Type of rules to return
/// List of rules files of the given type
private static IReadOnlyList FindAllRulesFiles(DirectoryReference Directory, RulesFileType Type)
{
// Check to see if we've already cached source files for this folder
RulesFileCache? Cache;
if (!RootFolderToRulesFileCache.TryGetValue(Directory, out Cache))
{
Cache = new RulesFileCache();
using(ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue())
{
DirectoryItem BaseDirectory = DirectoryItem.GetItemByDirectoryReference(Directory);
Queue.Enqueue(() => FindAllRulesFilesRecursively(BaseDirectory, Cache, Queue));
}
Cache.ModuleRules.Sort((A, B) => A.FullName.CompareTo(B.FullName));
Cache.TargetRules.Sort((A, B) => A.FullName.CompareTo(B.FullName));
Cache.AutomationModules.Sort((A, B) => A.FullName.CompareTo(B.FullName));
RootFolderToRulesFileCache[Directory] = Cache;
}
// Get the list of files of the type we're looking for
if (Type == RulesCompiler.RulesFileType.Module)
{
return Cache.ModuleRules;
}
else if (Type == RulesCompiler.RulesFileType.Target)
{
return Cache.TargetRules;
}
else if (Type == RulesCompiler.RulesFileType.AutomationModule)
{
return Cache.AutomationModules;
}
else
{
throw new BuildException("Unhandled rules type: {0}", Type);
}
}
///
/// Search through a directory tree for any rules files
///
/// The root directory to search from
/// Receives all the discovered rules files
/// Queue for adding additional tasks to
private static void FindAllRulesFilesRecursively(DirectoryItem Directory, RulesFileCache Cache, ThreadPoolWorkQueue Queue)
{
// Scan all the files in this directory
bool bSearchSubFolders = true;
foreach (FileItem File in Directory.EnumerateFiles())
{
if (File.HasExtension(".build.cs"))
{
lock(Cache.ModuleRules)
{
Cache.ModuleRules.Add(File.Location);
}
bSearchSubFolders = false;
}
else if (File.HasExtension(".target.cs"))
{
lock(Cache.TargetRules)
{
Cache.TargetRules.Add(File.Location);
}
}
else if (File.HasExtension(".automation.csproj"))
{
lock(Cache.AutomationModules)
{
Cache.AutomationModules.Add(File.Location);
}
bSearchSubFolders = false;
}
}
// If we didn't find anything to stop the search, search all the subdirectories too
if (bSearchSubFolders)
{
foreach (DirectoryItem SubDirectory in Directory.EnumerateDirectories())
{
Queue.Enqueue(() => FindAllRulesFilesRecursively(SubDirectory, Cache, Queue));
}
}
}
///
/// Find all the module rules files under a given directory
///
/// The directory to search under
/// The module context for each found rules instance
/// Map of module files to their context
private static void AddModuleRulesWithContext(DirectoryReference BaseDirectory, ModuleRulesContext ModuleContext, Dictionary ModuleFileToContext)
{
IReadOnlyList RulesFiles = FindAllRulesFiles(BaseDirectory, RulesFileType.Module);
foreach (FileReference RulesFile in RulesFiles)
{
ModuleFileToContext[RulesFile] = ModuleContext;
}
}
///
/// Find all the module rules files under a given directory
///
/// The directory to search under
/// Name of the subdirectory to look under
/// The module context for each found rules instance
/// The UHT module type
/// Map of module files to their context
private static void AddEngineModuleRulesWithContext(DirectoryReference BaseDirectory, string SubDirectoryName, ModuleRulesContext BaseModuleContext, UHTModuleType? DefaultUHTModuleType, Dictionary ModuleFileToContext)
{
DirectoryReference Directory = DirectoryReference.Combine(BaseDirectory, SubDirectoryName);
if (DirectoryLookupCache.DirectoryExists(Directory))
{
ModuleRulesContext ModuleContext = new ModuleRulesContext(BaseModuleContext) { DefaultUHTModuleType = DefaultUHTModuleType };
AddModuleRulesWithContext(Directory, ModuleContext, ModuleFileToContext);
}
}
///
/// The cached rules assembly for engine modules and targets.
///
private static RulesAssembly? EngineRulesAssembly;
///
/// Map of assembly names we've already compiled and loaded to their Assembly and list of game folders. This is used to prevent
/// trying to recompile the same assembly when ping-ponging between different types of targets
///
private static Dictionary LoadedAssemblyMap = new Dictionary();
///
/// Creates the engine rules assembly
///
/// Whether to use a precompiled engine
/// Whether to skip compilation for this assembly
/// New rules assembly
public static RulesAssembly CreateEngineRulesAssembly(bool bUsePrecompiled, bool bSkipCompile)
{
if (EngineRulesAssembly == null)
{
List EnginePlugins = new List();
List MarketplacePlugins = new List();
DirectoryReference MarketplaceDirectory = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Plugins", "Marketplace");
foreach (PluginInfo PluginInfo in Plugins.ReadEnginePlugins(UnrealBuildTool.EngineDirectory))
{
if (PluginInfo.File.IsUnderDirectory(MarketplaceDirectory))
{
MarketplacePlugins.Add(PluginInfo);
}
else
{
EnginePlugins.Add(PluginInfo);
}
}
EngineRulesAssembly = CreateEngineRulesAssemblyInternal(UnrealBuildTool.GetExtensionDirs(UnrealBuildTool.EngineDirectory), ProjectFileGenerator.EngineProjectFileNameBase, EnginePlugins, UnrealBuildTool.IsEngineInstalled() || bUsePrecompiled, bSkipCompile, null);
if (MarketplacePlugins.Count > 0)
{
EngineRulesAssembly = CreateMarketplaceRulesAssembly(MarketplacePlugins, bSkipCompile, EngineRulesAssembly);
}
}
return EngineRulesAssembly;
}
///
/// Creates a rules assembly
///
/// The root directories to create rules for
/// A prefix for the assembly file name
/// List of plugins to include in this assembly
/// Whether the assembly should be marked as installed
/// Whether to skip compilation for this assembly
/// The parent rules assembly
/// New rules assembly
private static RulesAssembly CreateEngineRulesAssemblyInternal(List RootDirectories, string AssemblyPrefix, IReadOnlyList Plugins, bool bReadOnly, bool bSkipCompile, RulesAssembly? Parent)
{
// Scope hierarchy
RulesScope Scope= new RulesScope("Engine", null);
RulesScope PluginsScope = new RulesScope("Engine Plugins", Scope);
RulesScope ProgramsScope = new RulesScope("Engine Programs", PluginsScope);
// Find the shared modules, excluding the programs directory. These are used to create an assembly with the bContainsEngineModules flag set to true.
Dictionary ModuleFileToContext = new Dictionary();
ModuleRulesContext DefaultModuleContext = new ModuleRulesContext(Scope, RootDirectories[0]);
foreach (DirectoryReference RootDirectory in RootDirectories)
{
using (Timeline.ScopeEvent("Finding engine modules"))
{
DirectoryReference SourceDirectory = DirectoryReference.Combine(RootDirectory, "Source");
AddEngineModuleRulesWithContext(SourceDirectory, "Runtime", DefaultModuleContext, UHTModuleType.EngineRuntime, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "Developer", DefaultModuleContext, UHTModuleType.EngineDeveloper, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "Editor", DefaultModuleContext, UHTModuleType.EngineEditor, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "ThirdParty", DefaultModuleContext, UHTModuleType.EngineThirdParty, ModuleFileToContext);
}
}
// Add all the plugin modules too (don't need to loop over RootDirectories since the plugins come in already found
using (Timeline.ScopeEvent("Finding plugin modules"))
{
ModuleRulesContext PluginsModuleContext = new ModuleRulesContext(PluginsScope, RootDirectories[0]);
FindModuleRulesForPlugins(Plugins, PluginsModuleContext, ModuleFileToContext);
}
// Create the assembly
DirectoryReference AssemblyDir = RootDirectories[0];
FileReference EngineAssemblyFileName = FileReference.Combine(AssemblyDir, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "Rules" + FrameworkAssemblyExtension);
RulesAssembly EngineAssembly = new RulesAssembly(Scope, RootDirectories, Plugins, ModuleFileToContext, new List(), EngineAssemblyFileName, bContainsEngineModules: true, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: Parent);
List ProgramTargetFiles = new List();
Dictionary ProgramModuleFiles = new Dictionary();
foreach (DirectoryReference RootDirectory in RootDirectories)
{
DirectoryReference SourceDirectory = DirectoryReference.Combine(RootDirectory, "Source");
DirectoryReference ProgramsDirectory = DirectoryReference.Combine(SourceDirectory, "Programs");
// Also create a scope for them, and update the UHT module type
ModuleRulesContext ProgramsModuleContext = new ModuleRulesContext(ProgramsScope, RootDirectory);
ProgramsModuleContext.DefaultUHTModuleType = UHTModuleType.Program;
using (Timeline.ScopeEvent("Finding program modules"))
{
// Find all the rules files
AddModuleRulesWithContext(ProgramsDirectory, ProgramsModuleContext, ProgramModuleFiles);
}
using (Timeline.ScopeEvent("Finding program targets"))
{
ProgramTargetFiles.AddRange(FindAllRulesFiles(SourceDirectory, RulesFileType.Target));
}
}
// Create a path to the assembly that we'll either load or compile
FileReference ProgramAssemblyFileName = FileReference.Combine(AssemblyDir, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "ProgramRules" + FrameworkAssemblyExtension);
RulesAssembly ProgramAssembly = new RulesAssembly(ProgramsScope, RootDirectories, new List().AsReadOnly(), ProgramModuleFiles, ProgramTargetFiles, ProgramAssemblyFileName, bContainsEngineModules: false, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: EngineAssembly);
// Return the combined assembly
return ProgramAssembly;
}
///
/// Creates a rules assembly
///
/// List of plugins to include in this assembly
/// Whether to skip compilation for this assembly
/// The parent rules assembly
/// New rules assembly
private static RulesAssembly CreateMarketplaceRulesAssembly(IReadOnlyList Plugins, bool bSkipCompile, RulesAssembly Parent)
{
RulesScope MarketplaceScope = new RulesScope("Marketplace", Parent.Scope);
// Add all the plugin modules too (don't need to loop over RootDirectories since the plugins come in already found
Dictionary ModuleFileToContext = new Dictionary();
using (Timeline.ScopeEvent("Finding marketplace plugin modules"))
{
ModuleRulesContext PluginsModuleContext = new ModuleRulesContext(MarketplaceScope, UnrealBuildTool.EngineDirectory);
FindModuleRulesForPlugins(Plugins, PluginsModuleContext, ModuleFileToContext);
}
// Create the assembly
RulesAssembly Result = Parent;
if (ModuleFileToContext.Count > 0)
{
FileReference AssemblyFileName = FileReference.Combine(UnrealBuildTool.WritableEngineDirectory, "Intermediate", "Build", "BuildRules", "MarketplaceRules.dll");
Result = new RulesAssembly(MarketplaceScope, new List { UnrealBuildTool.EngineDirectory }, Plugins, ModuleFileToContext, new List(), AssemblyFileName, bContainsEngineModules: true, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: false, bSkipCompile: bSkipCompile, Parent: Parent);
}
return Result;
}
///
/// Creates a rules assembly with the given parameters.
///
/// The project file to create rules for. Null for the engine.
/// Whether to use a precompiled engine
/// Whether to skip compilation for this assembly
/// New rules assembly
public static RulesAssembly CreateProjectRulesAssembly(FileReference ProjectFileName, bool bUsePrecompiled, bool bSkipCompile)
{
// Check if there's an existing assembly for this project
RulesAssembly? ProjectRulesAssembly;
if (!LoadedAssemblyMap.TryGetValue(ProjectFileName, out ProjectRulesAssembly))
{
ProjectDescriptor Project = ProjectDescriptor.FromFile(ProjectFileName);
// Create the parent assembly
RulesAssembly Parent = CreateEngineRulesAssembly(bUsePrecompiled, bSkipCompile);
DirectoryReference MainProjectDirectory = ProjectFileName.Directory;
//DirectoryReference MainProjectSourceDirectory = DirectoryReference.Combine(MainProjectDirectory, "Source");
// Create a scope for things in this assembly
RulesScope Scope = new RulesScope("Project", Parent.Scope);
// Create a new context for modules created by this assembly
ModuleRulesContext DefaultModuleContext = new ModuleRulesContext(Scope, MainProjectDirectory);
DefaultModuleContext.bCanBuildDebugGame = true;
DefaultModuleContext.bCanHotReload = true;
DefaultModuleContext.bClassifyAsGameModuleForUHT = true;
DefaultModuleContext.bCanUseForSharedPCH = false;
// gather modules from project and platforms
Dictionary ModuleFiles = new Dictionary();
List TargetFiles = new List();
// Find all the project directories
List ProjectDirectories = UnrealBuildTool.GetExtensionDirs(ProjectFileName.Directory);
if (Project.AdditionalRootDirectories != null)
{
ProjectDirectories.AddRange(Project.AdditionalRootDirectories);
}
// Find all the rules/plugins under the project source directories
foreach (DirectoryReference ProjectDirectory in ProjectDirectories)
{
DirectoryReference ProjectSourceDirectory = DirectoryReference.Combine(ProjectDirectory, "Source");
AddModuleRulesWithContext(ProjectSourceDirectory, DefaultModuleContext, ModuleFiles);
TargetFiles.AddRange(FindAllRulesFiles(ProjectSourceDirectory, RulesFileType.Target));
}
// Find all the project plugins
List ProjectPlugins = new List();
ProjectPlugins.AddRange(Plugins.ReadProjectPlugins(MainProjectDirectory));
// Add the project's additional plugin directories plugins too
if (Project.AdditionalPluginDirectories != null)
{
foreach (DirectoryReference AdditionalPluginDirectory in Project.AdditionalPluginDirectories)
{
ProjectPlugins.AddRange(Plugins.ReadAdditionalPlugins(AdditionalPluginDirectory));
}
}
// Find all the plugin module rules
FindModuleRulesForPlugins(ProjectPlugins, DefaultModuleContext, ModuleFiles);
// Add the games project's intermediate source folder
DirectoryReference ProjectIntermediateSourceDirectory = DirectoryReference.Combine(MainProjectDirectory, "Intermediate", "Source");
if (DirectoryReference.Exists(ProjectIntermediateSourceDirectory))
{
AddModuleRulesWithContext(ProjectIntermediateSourceDirectory, DefaultModuleContext, ModuleFiles);
TargetFiles.AddRange(FindAllRulesFiles(ProjectIntermediateSourceDirectory, RulesFileType.Target));
}
// Compile the assembly. If there are no module or target files, just use the parent assembly.
FileReference AssemblyFileName = FileReference.Combine(MainProjectDirectory, "Intermediate", "Build", "BuildRules", ProjectFileName.GetFileNameWithoutExtension() + "ModuleRules" + FrameworkAssemblyExtension);
if(ModuleFiles.Count == 0 && TargetFiles.Count == 0)
{
ProjectRulesAssembly = Parent;
}
else
{
ProjectRulesAssembly = new RulesAssembly(Scope, new List { MainProjectDirectory }, ProjectPlugins, ModuleFiles, TargetFiles, AssemblyFileName, bContainsEngineModules: false, DefaultBuildSettings: null, bReadOnly: UnrealBuildTool.IsProjectInstalled(), bSkipCompile: bSkipCompile, Parent: Parent);
}
LoadedAssemblyMap.Add(ProjectFileName, ProjectRulesAssembly);
}
return ProjectRulesAssembly;
}
///
/// Creates a rules assembly with the given parameters.
///
/// The plugin file to create rules for
/// Whether to skip compilation for this assembly
/// The parent rules assembly
/// Whether the plugin contains engine modules. Used to initialize the default value for ModuleRules.bTreatAsEngineModule.
/// The new rules assembly
public static RulesAssembly CreatePluginRulesAssembly(FileReference PluginFileName, bool bSkipCompile, RulesAssembly Parent, bool bContainsEngineModules)
{
// Check if there's an existing assembly for this project
RulesAssembly? PluginRulesAssembly;
if (!LoadedAssemblyMap.TryGetValue(PluginFileName, out PluginRulesAssembly))
{
// Find all the rules source files
Dictionary ModuleFiles = new Dictionary();
List TargetFiles = new List();
// Create a list of plugins for this assembly. If it already exists in the parent assembly, just create an empty assembly.
List ForeignPlugins = new List();
if (!Parent.EnumeratePlugins().Any(x => x.File == PluginFileName))
{
ForeignPlugins.Add(new PluginInfo(PluginFileName, PluginType.External));
}
// Create a new scope for the plugin. It should not reference anything else.
RulesScope Scope = new RulesScope("Plugin", Parent.Scope);
// Find all the modules
ModuleRulesContext PluginModuleContext = new ModuleRulesContext(Scope, PluginFileName.Directory);
PluginModuleContext.bClassifyAsGameModuleForUHT = !bContainsEngineModules;
FindModuleRulesForPlugins(ForeignPlugins, PluginModuleContext, ModuleFiles);
// Compile the assembly
FileReference AssemblyFileName = FileReference.Combine(PluginFileName.Directory, "Intermediate", "Build", "BuildRules", Path.GetFileNameWithoutExtension(PluginFileName.FullName) + "ModuleRules" + FrameworkAssemblyExtension);
PluginRulesAssembly = new RulesAssembly(Scope, new List { PluginFileName.Directory }, ForeignPlugins, ModuleFiles, TargetFiles, AssemblyFileName, bContainsEngineModules, DefaultBuildSettings: null, bReadOnly: false, bSkipCompile: bSkipCompile, Parent: Parent);
LoadedAssemblyMap.Add(PluginFileName, PluginRulesAssembly);
}
return PluginRulesAssembly;
}
///
/// Compile a rules assembly for the current target
///
/// The project file being compiled
/// The target being built
/// Whether to skip compiling any rules assemblies
/// Whether to use a precompiled engine build
/// Foreign plugin to be compiled
/// The compiled rules assembly
public static RulesAssembly CreateTargetRulesAssembly(FileReference? ProjectFile, string TargetName, bool bSkipRulesCompile, bool bUsePrecompiled, FileReference? ForeignPlugin)
{
RulesAssembly RulesAssembly;
if (ProjectFile != null)
{
RulesAssembly = CreateProjectRulesAssembly(ProjectFile, bUsePrecompiled, bSkipRulesCompile);
}
else
{
RulesAssembly = CreateEngineRulesAssembly(bUsePrecompiled, bSkipRulesCompile);
}
if (ForeignPlugin != null)
{
RulesAssembly = CreatePluginRulesAssembly(ForeignPlugin, bSkipRulesCompile, RulesAssembly, true);
}
return RulesAssembly;
}
///
/// Finds all the module rules for plugins under the given directory.
///
/// The directory to search
/// The default context for any files that are enumerated
/// Dictionary which is filled with mappings from the module file to its corresponding context
private static void FindModuleRulesForPlugins(IReadOnlyList Plugins, ModuleRulesContext DefaultContext, Dictionary ModuleFileToContext)
{
PrefetchRulesFiles(Plugins.Select(x => DirectoryReference.Combine(x.Directory, "Source")));
foreach (PluginInfo Plugin in Plugins)
{
List PluginModuleFiles = FindAllRulesFiles(DirectoryReference.Combine(Plugin.Directory, "Source"), RulesFileType.Module).ToList();
foreach (FileReference ChildFile in Plugin.ChildFiles)
{
PluginModuleFiles.AddRange(FindAllRulesFiles(DirectoryReference.Combine(ChildFile.Directory, "Source"), RulesFileType.Module));
}
foreach (FileReference ModuleFile in PluginModuleFiles)
{
ModuleRulesContext PluginContext = new ModuleRulesContext(DefaultContext);
PluginContext.DefaultOutputBaseDir = Plugin.Directory;
PluginContext.Plugin = Plugin;
ModuleFileToContext[ModuleFile] = PluginContext;
}
}
}
///
/// Gets the filename that declares the given type.
///
/// The type to search for.
/// The filename that declared the given type, or null
public static string? GetFileNameFromType(Type ExistingType)
{
FileReference? FileName;
if (EngineRulesAssembly != null && EngineRulesAssembly.TryGetFileNameFromType(ExistingType, out FileName))
{
return FileName.FullName;
}
foreach (RulesAssembly RulesAssembly in LoadedAssemblyMap.Values)
{
if (RulesAssembly.TryGetFileNameFromType(ExistingType, out FileName))
{
return FileName.FullName;
}
}
return null;
}
}
}