2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
2019-04-24 08:31:23 -04:00
using System.IO ;
2014-03-14 14:13:41 -04:00
using System.Linq ;
2020-08-11 01:36:57 -04:00
using System.Reflection ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2021-06-08 19:05:33 -04:00
using UnrealBuildBase ;
2014-03-14 14:13:41 -04:00
namespace UnrealBuildTool
{
/// <summary>
2016-12-13 11:58:16 -05:00
/// Class which compiles (and caches) rules assemblies for different folders.
2014-03-14 14:13:41 -04:00
/// </summary>
2014-12-11 03:13:57 -05:00
public class RulesCompiler
2014-03-14 14:13:41 -04:00
{
/// <summary>
2015-09-06 12:49:32 -04:00
/// Enum for types of rules files. Should match extensions in RulesFileExtensions.
2014-03-14 14:13:41 -04:00
/// </summary>
public enum RulesFileType
{
2017-01-30 16:52:08 -05:00
/// <summary>
/// .build.cs files
/// </summary>
2014-03-14 14:13:41 -04:00
Module ,
2017-01-30 16:52:08 -05:00
/// <summary>
/// .target.cs files
/// </summary>
2014-03-14 14:13:41 -04:00
Target ,
2017-01-30 16:52:08 -05:00
/// <summary>
/// .automation.csproj files
/// </summary>
2014-12-11 03:13:57 -05:00
AutomationModule
2014-03-14 14:13:41 -04:00
}
2015-09-06 12:49:32 -04:00
/// <summary>
/// Cached list of rules files in each directory of each type
/// </summary>
2014-03-14 14:13:41 -04:00
class RulesFileCache
{
2015-09-06 12:49:32 -04:00
public List < FileReference > ModuleRules = new List < FileReference > ( ) ;
public List < FileReference > TargetRules = new List < FileReference > ( ) ;
public List < FileReference > AutomationModules = new List < FileReference > ( ) ;
2014-03-14 14:13:41 -04:00
}
/// 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.
2015-09-03 08:47:24 -04:00
static Dictionary < DirectoryReference , RulesFileCache > RootFolderToRulesFileCache = new Dictionary < DirectoryReference , RulesFileCache > ( ) ;
2017-08-31 12:08:38 -04:00
/// <summary>
///
/// </summary>
const string FrameworkAssemblyExtension = ".dll" ;
2017-01-30 16:52:08 -05:00
/// <summary>
///
/// </summary>
/// <param name="RulesFileType"></param>
/// <param name="GameFolders"></param>
/// <param name="ForeignPlugins"></param>
/// <param name="AdditionalSearchPaths"></param>
/// <param name="bIncludeEngine"></param>
2019-03-11 10:57:27 -04:00
/// <param name="bIncludeTempTargets">Whether to include targets generated by UAT to accomodate content-only projects that need to be compiled to include plugins</param>
2017-01-30 16:52:08 -05:00
/// <returns></returns>
2020-09-20 15:18:41 -04:00
public static List < FileReference > FindAllRulesSourceFiles ( RulesFileType RulesFileType , List < DirectoryReference > GameFolders , List < FileReference > ForeignPlugins , List < DirectoryReference > AdditionalSearchPaths , bool bIncludeEngine = true , bool bIncludeTempTargets = true )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
List < DirectoryReference > Folders = new List < DirectoryReference > ( ) ;
2014-03-14 14:13:41 -04:00
// Add all engine source (including third party source)
2015-09-24 12:37:21 -04:00
if ( bIncludeEngine )
2015-08-31 12:39:51 -04:00
{
2021-06-09 12:54:42 -04:00
Folders . AddRange ( UnrealBuildTool . GetExtensionDirs ( UnrealBuild . EngineDirectory , "Source" ) ) ;
2017-06-28 13:14:02 -04:00
}
2014-03-14 14:13:41 -04:00
// @todo plugin: Disallow modules from including plugin modules as dependency modules? (except when the module is part of that plugin)
2015-04-22 09:37:04 -04:00
// Get all the root folders for plugins
2015-09-03 08:47:24 -04:00
List < DirectoryReference > RootFolders = new List < DirectoryReference > ( ) ;
2015-09-24 12:37:21 -04:00
if ( bIncludeEngine )
2015-08-31 12:39:51 -04:00
{
2021-06-09 12:54:42 -04:00
RootFolders . AddRange ( UnrealBuildTool . GetExtensionDirs ( UnrealBuild . EngineDirectory ) ) ;
2017-06-28 13:14:02 -04:00
}
2015-09-24 12:37:21 -04:00
if ( GameFolders ! = null )
2015-08-31 12:39:51 -04:00
{
2020-04-08 15:07:46 -04:00
RootFolders . AddRange ( GameFolders . SelectMany ( x = > UnrealBuildTool . GetExtensionDirs ( x ) ) ) ;
2015-08-31 12:39:51 -04:00
}
2015-04-22 09:37:04 -04:00
// Find all the plugin source directories
2015-09-24 12:37:21 -04:00
foreach ( DirectoryReference RootFolder in RootFolders )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
DirectoryReference PluginsFolder = DirectoryReference . Combine ( RootFolder , "Plugins" ) ;
2015-09-24 12:37:21 -04:00
foreach ( FileReference PluginFile in Plugins . EnumeratePlugins ( PluginsFolder ) )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
Folders . Add ( DirectoryReference . Combine ( PluginFile . Directory , "Source" ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2015-05-01 10:58:14 -04:00
// Add all the extra plugin folders
2015-09-24 12:37:21 -04:00
if ( ForeignPlugins ! = null )
2015-05-01 10:58:14 -04:00
{
2015-09-24 12:37:21 -04:00
foreach ( FileReference ForeignPlugin in ForeignPlugins )
2015-05-01 10:58:14 -04:00
{
2015-09-03 08:47:24 -04:00
Folders . Add ( DirectoryReference . Combine ( ForeignPlugin . Directory , "Source" ) ) ;
2015-05-01 10:58:14 -04:00
}
}
2014-03-14 14:13:41 -04:00
// Add in the game folders to search
2015-09-24 12:37:21 -04:00
if ( GameFolders ! = null )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
foreach ( DirectoryReference GameFolder in GameFolders )
2014-03-14 14:13:41 -04:00
{
2020-04-08 15:07:46 -04:00
Folders . AddRange ( UnrealBuildTool . GetExtensionDirs ( GameFolder , "Source" ) ) ;
2019-07-12 16:09:23 -04:00
if ( bIncludeTempTargets )
2019-03-11 10:57:27 -04:00
{
DirectoryReference GameIntermediateSourceFolder = DirectoryReference . Combine ( GameFolder , "Intermediate" , "Source" ) ;
Folders . Add ( GameIntermediateSourceFolder ) ;
}
2014-03-14 14:13:41 -04:00
}
}
// Process the additional search path, if sent in
2015-09-24 12:37:21 -04:00
if ( AdditionalSearchPaths ! = null )
2014-03-14 14:13:41 -04:00
{
2016-03-08 09:00:48 -05:00
foreach ( DirectoryReference AdditionalSearchPath in AdditionalSearchPaths )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( AdditionalSearchPath ! = null )
2014-03-14 14:13:41 -04:00
{
2017-01-30 16:52:08 -05:00
if ( DirectoryReference . Exists ( AdditionalSearchPath ) )
2014-03-14 14:13:41 -04:00
{
Folders . Add ( AdditionalSearchPath ) ;
}
else
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "Couldn't find AdditionalSearchPath for rules source files '{0}'" , AdditionalSearchPath ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
// Iterate over all the folders to check
2015-09-03 08:47:24 -04:00
List < FileReference > SourceFiles = new List < FileReference > ( ) ;
2015-09-06 12:49:32 -04:00
HashSet < FileReference > UniqueSourceFiles = new HashSet < FileReference > ( ) ;
2015-09-24 12:37:21 -04:00
foreach ( DirectoryReference Folder in Folders )
2014-03-14 14:13:41 -04:00
{
2015-09-08 07:59:23 -04:00
IReadOnlyList < FileReference > SourceFilesForFolder = FindAllRulesFiles ( Folder , RulesFileType ) ;
2015-09-24 12:37:21 -04:00
foreach ( FileReference SourceFile in SourceFilesForFolder )
2015-09-06 12:49:32 -04:00
{
2015-09-24 12:37:21 -04:00
if ( UniqueSourceFiles . Add ( SourceFile ) )
2014-03-14 14:13:41 -04:00
{
2015-09-06 12:49:32 -04:00
SourceFiles . Add ( SourceFile ) ;
2014-03-14 14:13:41 -04:00
}
}
}
return SourceFiles ;
}
2017-01-30 16:52:08 -05:00
/// <summary>
/// Invalidate the cache for the givcen directory
/// </summary>
/// <param name="DirectoryPath">Directory to invalidate</param>
2015-11-25 18:47:20 -05:00
public static void InvalidateRulesFileCache ( string DirectoryPath )
{
DirectoryReference Directory = new DirectoryReference ( DirectoryPath ) ;
RootFolderToRulesFileCache . Remove ( Directory ) ;
DirectoryLookupCache . InvalidateCachedDirectory ( Directory ) ;
}
2018-12-20 10:46:51 -05:00
/// <summary>
/// Prefetch multiple directories in parallel
/// </summary>
/// <param name="Directories">The directories to cache</param>
private static void PrefetchRulesFiles ( IEnumerable < DirectoryReference > Directories )
{
2020-12-20 18:47:42 -04:00
ThreadPoolWorkQueue ? Queue = null ;
2018-12-20 10:46:51 -05:00
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 ;
}
}
}
/// <summary>
/// Finds all the rules of the given type under a given directory
/// </summary>
/// <param name="Directory">Directory to search</param>
/// <param name="Type">Type of rules to return</param>
/// <returns>List of rules files of the given type</returns>
2015-09-08 07:59:23 -04:00
private static IReadOnlyList < FileReference > FindAllRulesFiles ( DirectoryReference Directory , RulesFileType Type )
{
// Check to see if we've already cached source files for this folder
2020-12-20 18:47:42 -04:00
RulesFileCache ? Cache ;
2015-09-08 07:59:23 -04:00
if ( ! RootFolderToRulesFileCache . TryGetValue ( Directory , out Cache ) )
{
Cache = new RulesFileCache ( ) ;
2018-12-20 10:46:51 -05:00
using ( ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue ( ) )
{
DirectoryItem BaseDirectory = DirectoryItem . GetItemByDirectoryReference ( Directory ) ;
Queue . Enqueue ( ( ) = > FindAllRulesFilesRecursively ( BaseDirectory , Cache , Queue ) ) ;
}
2020-01-14 12:06:17 -05:00
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 ) ) ;
2018-01-20 11:19:29 -05:00
RootFolderToRulesFileCache [ Directory ] = Cache ;
}
2015-09-08 07:59:23 -04:00
// Get the list of files of the type we're looking for
2015-09-24 12:37:21 -04:00
if ( Type = = RulesCompiler . RulesFileType . Module )
2015-09-08 07:59:23 -04:00
{
return Cache . ModuleRules ;
}
2015-09-24 12:37:21 -04:00
else if ( Type = = RulesCompiler . RulesFileType . Target )
2015-09-08 07:59:23 -04:00
{
return Cache . TargetRules ;
}
2015-09-24 12:37:21 -04:00
else if ( Type = = RulesCompiler . RulesFileType . AutomationModule )
2015-09-08 07:59:23 -04:00
{
return Cache . AutomationModules ;
}
else
{
throw new BuildException ( "Unhandled rules type: {0}" , Type ) ;
}
}
2018-12-20 10:46:51 -05:00
/// <summary>
/// Search through a directory tree for any rules files
/// </summary>
/// <param name="Directory">The root directory to search from</param>
/// <param name="Cache">Receives all the discovered rules files</param>
/// <param name="Queue">Queue for adding additional tasks to</param>
private static void FindAllRulesFilesRecursively ( DirectoryItem Directory , RulesFileCache Cache , ThreadPoolWorkQueue Queue )
2015-09-06 12:49:32 -04:00
{
// Scan all the files in this directory
bool bSearchSubFolders = true ;
2018-12-20 10:46:51 -05:00
foreach ( FileItem File in Directory . EnumerateFiles ( ) )
2015-09-06 12:49:32 -04:00
{
2015-09-24 12:37:21 -04:00
if ( File . HasExtension ( ".build.cs" ) )
2015-09-06 12:49:32 -04:00
{
2018-12-20 10:46:51 -05:00
lock ( Cache . ModuleRules )
{
Cache . ModuleRules . Add ( File . Location ) ;
}
2015-09-06 12:49:32 -04:00
bSearchSubFolders = false ;
}
2015-09-24 12:37:21 -04:00
else if ( File . HasExtension ( ".target.cs" ) )
2015-09-06 12:49:32 -04:00
{
2018-12-20 10:46:51 -05:00
lock ( Cache . TargetRules )
{
Cache . TargetRules . Add ( File . Location ) ;
}
2015-09-06 12:49:32 -04:00
}
2015-09-24 12:37:21 -04:00
else if ( File . HasExtension ( ".automation.csproj" ) )
2015-09-06 12:49:32 -04:00
{
2018-12-20 10:46:51 -05:00
lock ( Cache . AutomationModules )
{
Cache . AutomationModules . Add ( File . Location ) ;
}
2015-09-06 12:49:32 -04:00
bSearchSubFolders = false ;
}
}
// If we didn't find anything to stop the search, search all the subdirectories too
2015-09-24 12:37:21 -04:00
if ( bSearchSubFolders )
2015-09-06 12:49:32 -04:00
{
2018-12-20 10:46:51 -05:00
foreach ( DirectoryItem SubDirectory in Directory . EnumerateDirectories ( ) )
2015-09-06 12:49:32 -04:00
{
2018-12-20 10:46:51 -05:00
Queue . Enqueue ( ( ) = > FindAllRulesFilesRecursively ( SubDirectory , Cache , Queue ) ) ;
2015-09-06 12:49:32 -04:00
}
}
}
2019-04-24 08:31:23 -04:00
/// <summary>
/// Find all the module rules files under a given directory
/// </summary>
/// <param name="BaseDirectory">The directory to search under</param>
/// <param name="ModuleContext">The module context for each found rules instance</param>
/// <param name="ModuleFileToContext">Map of module files to their context</param>
private static void AddModuleRulesWithContext ( DirectoryReference BaseDirectory , ModuleRulesContext ModuleContext , Dictionary < FileReference , ModuleRulesContext > ModuleFileToContext )
{
IReadOnlyList < FileReference > RulesFiles = FindAllRulesFiles ( BaseDirectory , RulesFileType . Module ) ;
foreach ( FileReference RulesFile in RulesFiles )
{
ModuleFileToContext [ RulesFile ] = ModuleContext ;
}
}
/// <summary>
/// Find all the module rules files under a given directory
/// </summary>
/// <param name="BaseDirectory">The directory to search under</param>
/// <param name="SubDirectoryName">Name of the subdirectory to look under</param>
/// <param name="BaseModuleContext">The module context for each found rules instance</param>
/// <param name="DefaultUHTModuleType">The UHT module type</param>
/// <param name="ModuleFileToContext">Map of module files to their context</param>
private static void AddEngineModuleRulesWithContext ( DirectoryReference BaseDirectory , string SubDirectoryName , ModuleRulesContext BaseModuleContext , UHTModuleType ? DefaultUHTModuleType , Dictionary < FileReference , ModuleRulesContext > ModuleFileToContext )
{
DirectoryReference Directory = DirectoryReference . Combine ( BaseDirectory , SubDirectoryName ) ;
if ( DirectoryLookupCache . DirectoryExists ( Directory ) )
{
ModuleRulesContext ModuleContext = new ModuleRulesContext ( BaseModuleContext ) { DefaultUHTModuleType = DefaultUHTModuleType } ;
AddModuleRulesWithContext ( Directory , ModuleContext , ModuleFileToContext ) ;
}
}
2015-08-31 12:39:51 -04:00
/// <summary>
/// The cached rules assembly for engine modules and targets.
/// </summary>
2020-12-20 18:47:42 -04:00
private static RulesAssembly ? EngineRulesAssembly ;
2015-08-31 12:39:51 -04:00
2018-12-20 10:46:51 -05:00
/// <summary>
2015-05-08 15:19:00 -04:00
/// 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
2018-12-20 10:46:51 -05:00
/// </summary>
2015-09-03 08:47:24 -04:00
private static Dictionary < FileReference , RulesAssembly > LoadedAssemblyMap = new Dictionary < FileReference , RulesAssembly > ( ) ;
2014-03-14 14:13:41 -04:00
2015-08-31 12:39:51 -04:00
/// <summary>
/// Creates the engine rules assembly
/// </summary>
2018-08-14 18:32:34 -04:00
/// <param name="bUsePrecompiled">Whether to use a precompiled engine</param>
/// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
2015-08-31 12:39:51 -04:00
/// <returns>New rules assembly</returns>
2018-08-14 18:32:34 -04:00
public static RulesAssembly CreateEngineRulesAssembly ( bool bUsePrecompiled , bool bSkipCompile )
2015-08-31 12:39:51 -04:00
{
2015-09-24 12:37:21 -04:00
if ( EngineRulesAssembly = = null )
2015-08-31 12:39:51 -04:00
{
2021-03-02 13:44:57 -04:00
List < PluginInfo > EnginePlugins = new List < PluginInfo > ( ) ;
List < PluginInfo > MarketplacePlugins = new List < PluginInfo > ( ) ;
2019-05-03 08:03:23 -04:00
2021-06-09 12:54:42 -04:00
DirectoryReference MarketplaceDirectory = DirectoryReference . Combine ( UnrealBuild . EngineDirectory , "Plugins" , "Marketplace" ) ;
foreach ( PluginInfo PluginInfo in Plugins . ReadEnginePlugins ( UnrealBuild . EngineDirectory ) )
2021-03-02 13:44:57 -04:00
{
if ( PluginInfo . File . IsUnderDirectory ( MarketplaceDirectory ) )
{
MarketplacePlugins . Add ( PluginInfo ) ;
}
else
{
EnginePlugins . Add ( PluginInfo ) ;
}
}
2019-05-03 08:03:23 -04:00
2021-06-11 11:37:23 -04:00
EngineRulesAssembly = CreateEngineRulesAssemblyInternal ( UnrealBuildTool . GetExtensionDirs ( UnrealBuild . EngineDirectory ) , ProjectFileGenerator . EngineProjectFileNameBase , EnginePlugins , UnrealBuild . IsEngineInstalled ( ) | | bUsePrecompiled , bSkipCompile , null ) ;
2019-05-03 08:03:23 -04:00
2021-03-02 13:44:57 -04:00
if ( MarketplacePlugins . Count > 0 )
{
EngineRulesAssembly = CreateMarketplaceRulesAssembly ( MarketplacePlugins , bSkipCompile , EngineRulesAssembly ) ;
}
2015-08-31 12:39:51 -04:00
}
return EngineRulesAssembly ;
}
2017-05-12 19:19:12 -04:00
/// <summary>
/// Creates a rules assembly
/// </summary>
2019-05-03 08:03:23 -04:00
/// <param name="RootDirectories">The root directories to create rules for</param>
2017-05-12 19:19:12 -04:00
/// <param name="AssemblyPrefix">A prefix for the assembly file name</param>
2017-07-21 12:42:36 -04:00
/// <param name="Plugins">List of plugins to include in this assembly</param>
2018-08-14 18:32:34 -04:00
/// <param name="bReadOnly">Whether the assembly should be marked as installed</param>
/// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
2017-05-12 19:19:12 -04:00
/// <param name="Parent">The parent rules assembly</param>
/// <returns>New rules assembly</returns>
2021-03-02 13:44:57 -04:00
private static RulesAssembly CreateEngineRulesAssemblyInternal ( List < DirectoryReference > RootDirectories , string AssemblyPrefix , IReadOnlyList < PluginInfo > Plugins , bool bReadOnly , bool bSkipCompile , RulesAssembly ? Parent )
2017-05-12 19:19:12 -04:00
{
2019-05-03 08:03:23 -04:00
// Scope hierarchy
2021-03-02 13:44:57 -04:00
RulesScope Scope = new RulesScope ( "Engine" , null ) ;
RulesScope PluginsScope = new RulesScope ( "Engine Plugins" , Scope ) ;
RulesScope ProgramsScope = new RulesScope ( "Engine Programs" , PluginsScope ) ;
2017-05-12 19:19:12 -04:00
2018-01-20 11:19:29 -05:00
// Find the shared modules, excluding the programs directory. These are used to create an assembly with the bContainsEngineModules flag set to true.
2019-04-24 08:31:23 -04:00
Dictionary < FileReference , ModuleRulesContext > ModuleFileToContext = new Dictionary < FileReference , ModuleRulesContext > ( ) ;
2019-05-03 08:03:23 -04:00
ModuleRulesContext DefaultModuleContext = new ModuleRulesContext ( Scope , RootDirectories [ 0 ] ) ;
2017-05-12 19:19:12 -04:00
2019-05-03 08:03:23 -04:00
foreach ( DirectoryReference RootDirectory in RootDirectories )
2018-12-20 10:46:51 -05:00
{
2019-05-03 08:03:23 -04:00
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 ) ;
}
}
2020-03-13 23:45:06 -04:00
2019-05-03 08:03:23 -04:00
// 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 ] ) ;
2019-04-24 08:31:23 -04:00
FindModuleRulesForPlugins ( Plugins , PluginsModuleContext , ModuleFileToContext ) ;
2018-12-20 10:46:51 -05:00
}
2018-01-20 11:19:29 -05:00
// Create the assembly
2020-08-11 01:36:57 -04:00
DirectoryReference AssemblyDir = RootDirectories [ 0 ] ;
2020-02-25 16:49:43 -05:00
FileReference EngineAssemblyFileName = FileReference . Combine ( AssemblyDir , "Intermediate" , "Build" , "BuildRules" , AssemblyPrefix + "Rules" + FrameworkAssemblyExtension ) ;
2020-03-13 23:45:06 -04:00
RulesAssembly EngineAssembly = new RulesAssembly ( Scope , RootDirectories , Plugins , ModuleFileToContext , new List < FileReference > ( ) , EngineAssemblyFileName , bContainsEngineModules : true , DefaultBuildSettings : BuildSettingsVersion . Latest , bReadOnly : bReadOnly , bSkipCompile : bSkipCompile , Parent : Parent ) ;
2019-04-24 08:31:23 -04:00
2019-05-03 08:03:23 -04:00
List < FileReference > ProgramTargetFiles = new List < FileReference > ( ) ;
2019-04-24 08:31:23 -04:00
Dictionary < FileReference , ModuleRulesContext > ProgramModuleFiles = new Dictionary < FileReference , ModuleRulesContext > ( ) ;
2019-05-03 08:03:23 -04:00
foreach ( DirectoryReference RootDirectory in RootDirectories )
2018-12-20 10:46:51 -05:00
{
2019-05-03 08:03:23 -04:00
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 ) ) ;
}
2018-12-20 10:46:51 -05:00
}
2017-05-12 19:19:12 -04:00
// Create a path to the assembly that we'll either load or compile
2020-02-25 16:49:43 -05:00
FileReference ProgramAssemblyFileName = FileReference . Combine ( AssemblyDir , "Intermediate" , "Build" , "BuildRules" , AssemblyPrefix + "ProgramRules" + FrameworkAssemblyExtension ) ;
2020-03-13 23:45:06 -04:00
RulesAssembly ProgramAssembly = new RulesAssembly ( ProgramsScope , RootDirectories , new List < PluginInfo > ( ) . AsReadOnly ( ) , ProgramModuleFiles , ProgramTargetFiles , ProgramAssemblyFileName , bContainsEngineModules : false , DefaultBuildSettings : BuildSettingsVersion . Latest , bReadOnly : bReadOnly , bSkipCompile : bSkipCompile , Parent : EngineAssembly ) ;
2018-01-20 11:19:29 -05:00
// Return the combined assembly
return ProgramAssembly ;
2017-05-12 19:19:12 -04:00
}
2021-03-02 13:44:57 -04:00
/// <summary>
/// Creates a rules assembly
/// </summary>
/// <param name="Plugins">List of plugins to include in this assembly</param>
/// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
/// <param name="Parent">The parent rules assembly</param>
/// <returns>New rules assembly</returns>
private static RulesAssembly CreateMarketplaceRulesAssembly ( IReadOnlyList < PluginInfo > 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 < FileReference , ModuleRulesContext > ModuleFileToContext = new Dictionary < FileReference , ModuleRulesContext > ( ) ;
using ( Timeline . ScopeEvent ( "Finding marketplace plugin modules" ) )
{
2021-06-09 12:54:42 -04:00
ModuleRulesContext PluginsModuleContext = new ModuleRulesContext ( MarketplaceScope , UnrealBuild . EngineDirectory ) ;
2021-03-02 13:44:57 -04:00
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" ) ;
2021-06-09 12:54:42 -04:00
Result = new RulesAssembly ( MarketplaceScope , new List < DirectoryReference > { UnrealBuild . EngineDirectory } , Plugins , ModuleFileToContext , new List < FileReference > ( ) , AssemblyFileName , bContainsEngineModules : true , DefaultBuildSettings : BuildSettingsVersion . Latest , bReadOnly : false , bSkipCompile : bSkipCompile , Parent : Parent ) ;
2021-03-02 13:44:57 -04:00
}
return Result ;
}
2015-08-31 10:33:16 -04:00
/// <summary>
/// Creates a rules assembly with the given parameters.
/// </summary>
/// <param name="ProjectFileName">The project file to create rules for. Null for the engine.</param>
2018-08-14 18:32:34 -04:00
/// <param name="bUsePrecompiled">Whether to use a precompiled engine</param>
/// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
2017-01-30 16:52:08 -05:00
/// <returns>New rules assembly</returns>
2018-08-14 18:32:34 -04:00
public static RulesAssembly CreateProjectRulesAssembly ( FileReference ProjectFileName , bool bUsePrecompiled , bool bSkipCompile )
2014-03-14 14:13:41 -04:00
{
2015-08-31 10:33:16 -04:00
// Check if there's an existing assembly for this project
2020-12-20 18:47:42 -04:00
RulesAssembly ? ProjectRulesAssembly ;
2015-09-24 12:37:21 -04:00
if ( ! LoadedAssemblyMap . TryGetValue ( ProjectFileName , out ProjectRulesAssembly ) )
2014-03-14 14:13:41 -04:00
{
2017-08-31 12:08:38 -04:00
ProjectDescriptor Project = ProjectDescriptor . FromFile ( ProjectFileName ) ;
2017-05-12 19:19:12 -04:00
2017-06-14 16:17:46 -04:00
// Create the parent assembly
2020-09-20 15:18:41 -04:00
RulesAssembly Parent = CreateEngineRulesAssembly ( bUsePrecompiled , bSkipCompile ) ;
2014-03-14 14:13:41 -04:00
2019-05-15 16:08:22 -04:00
DirectoryReference MainProjectDirectory = ProjectFileName . Directory ;
2020-03-16 19:04:42 -04:00
//DirectoryReference MainProjectSourceDirectory = DirectoryReference.Combine(MainProjectDirectory, "Source");
2019-05-15 16:08:22 -04:00
2019-04-24 08:31:23 -04:00
// 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
2019-05-15 16:08:22 -04:00
ModuleRulesContext DefaultModuleContext = new ModuleRulesContext ( Scope , MainProjectDirectory ) ;
2019-04-24 08:31:23 -04:00
DefaultModuleContext . bCanBuildDebugGame = true ;
DefaultModuleContext . bCanHotReload = true ;
DefaultModuleContext . bClassifyAsGameModuleForUHT = true ;
2019-04-26 12:00:13 -04:00
DefaultModuleContext . bCanUseForSharedPCH = false ;
2019-05-15 16:08:22 -04:00
// gather modules from project and platforms
2019-04-24 08:31:23 -04:00
Dictionary < FileReference , ModuleRulesContext > ModuleFiles = new Dictionary < FileReference , ModuleRulesContext > ( ) ;
2019-05-15 16:08:22 -04:00
List < FileReference > TargetFiles = new List < FileReference > ( ) ;
2015-09-08 07:59:23 -04:00
2019-11-08 10:37:21 -05:00
// Find all the project directories
2020-04-08 15:07:46 -04:00
List < DirectoryReference > ProjectDirectories = UnrealBuildTool . GetExtensionDirs ( ProjectFileName . Directory ) ;
2019-11-07 21:01:48 -05:00
if ( Project . AdditionalRootDirectories ! = null )
{
2019-11-08 10:37:21 -05:00
ProjectDirectories . AddRange ( Project . AdditionalRootDirectories ) ;
2019-11-07 21:01:48 -05:00
}
2019-05-15 16:08:22 -04:00
// Find all the rules/plugins under the project source directories
2019-11-08 10:37:21 -05:00
foreach ( DirectoryReference ProjectDirectory in ProjectDirectories )
2017-07-21 12:42:36 -04:00
{
2019-05-15 16:08:22 -04:00
DirectoryReference ProjectSourceDirectory = DirectoryReference . Combine ( ProjectDirectory , "Source" ) ;
AddModuleRulesWithContext ( ProjectSourceDirectory , DefaultModuleContext , ModuleFiles ) ;
TargetFiles . AddRange ( FindAllRulesFiles ( ProjectSourceDirectory , RulesFileType . Target ) ) ;
2019-08-12 17:59:42 -04:00
}
2019-05-15 16:08:22 -04:00
2019-08-12 17:59:42 -04:00
// Find all the project plugins
List < PluginInfo > ProjectPlugins = new List < PluginInfo > ( ) ;
ProjectPlugins . AddRange ( Plugins . ReadProjectPlugins ( MainProjectDirectory ) ) ;
2019-05-15 16:08:22 -04:00
2019-08-12 17:59:42 -04:00
// Add the project's additional plugin directories plugins too
if ( Project . AdditionalPluginDirectories ! = null )
{
2019-11-08 10:37:21 -05:00
foreach ( DirectoryReference AdditionalPluginDirectory in Project . AdditionalPluginDirectories )
2017-07-21 12:42:36 -04:00
{
2019-11-08 10:37:21 -05:00
ProjectPlugins . AddRange ( Plugins . ReadAdditionalPlugins ( AdditionalPluginDirectory ) ) ;
2017-07-21 12:42:36 -04:00
}
}
// Find all the plugin module rules
2019-04-24 08:31:23 -04:00
FindModuleRulesForPlugins ( ProjectPlugins , DefaultModuleContext , ModuleFiles ) ;
2015-05-08 15:19:00 -04:00
// Add the games project's intermediate source folder
2019-05-15 16:08:22 -04:00
DirectoryReference ProjectIntermediateSourceDirectory = DirectoryReference . Combine ( MainProjectDirectory , "Intermediate" , "Source" ) ;
2017-01-30 16:52:08 -05:00
if ( DirectoryReference . Exists ( ProjectIntermediateSourceDirectory ) )
2015-05-08 15:19:00 -04:00
{
2019-04-24 08:31:23 -04:00
AddModuleRulesWithContext ( ProjectIntermediateSourceDirectory , DefaultModuleContext , ModuleFiles ) ;
2015-09-08 07:59:23 -04:00
TargetFiles . AddRange ( FindAllRulesFiles ( ProjectIntermediateSourceDirectory , RulesFileType . Target ) ) ;
2015-05-08 15:19:00 -04:00
}
2015-08-31 12:39:51 -04:00
2019-01-29 08:56:46 -05:00
// Compile the assembly. If there are no module or target files, just use the parent assembly.
2019-05-15 16:08:22 -04:00
FileReference AssemblyFileName = FileReference . Combine ( MainProjectDirectory , "Intermediate" , "Build" , "BuildRules" , ProjectFileName . GetFileNameWithoutExtension ( ) + "ModuleRules" + FrameworkAssemblyExtension ) ;
2019-01-29 08:56:46 -05:00
if ( ModuleFiles . Count = = 0 & & TargetFiles . Count = = 0 )
{
ProjectRulesAssembly = Parent ;
}
else
{
2020-03-13 23:45:06 -04:00
ProjectRulesAssembly = new RulesAssembly ( Scope , new List < DirectoryReference > { MainProjectDirectory } , ProjectPlugins , ModuleFiles , TargetFiles , AssemblyFileName , bContainsEngineModules : false , DefaultBuildSettings : null , bReadOnly : UnrealBuildTool . IsProjectInstalled ( ) , bSkipCompile : bSkipCompile , Parent : Parent ) ;
2019-01-29 08:56:46 -05:00
}
2015-09-03 08:47:24 -04:00
LoadedAssemblyMap . Add ( ProjectFileName , ProjectRulesAssembly ) ;
2015-05-08 15:19:00 -04:00
}
2015-08-31 12:39:51 -04:00
return ProjectRulesAssembly ;
}
2015-08-31 10:33:16 -04:00
2015-08-31 12:39:51 -04:00
/// <summary>
/// Creates a rules assembly with the given parameters.
/// </summary>
2017-01-30 16:52:08 -05:00
/// <param name="PluginFileName">The plugin file to create rules for</param>
2018-08-14 18:32:34 -04:00
/// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
2017-01-30 16:52:08 -05:00
/// <param name="Parent">The parent rules assembly</param>
2018-06-05 04:05:31 -04:00
/// <param name="bContainsEngineModules">Whether the plugin contains engine modules. Used to initialize the default value for ModuleRules.bTreatAsEngineModule.</param>
2017-01-30 16:52:08 -05:00
/// <returns>The new rules assembly</returns>
2018-08-14 18:32:34 -04:00
public static RulesAssembly CreatePluginRulesAssembly ( FileReference PluginFileName , bool bSkipCompile , RulesAssembly Parent , bool bContainsEngineModules )
2015-08-31 12:39:51 -04:00
{
// Check if there's an existing assembly for this project
2020-12-20 18:47:42 -04:00
RulesAssembly ? PluginRulesAssembly ;
2015-09-24 12:37:21 -04:00
if ( ! LoadedAssemblyMap . TryGetValue ( PluginFileName , out PluginRulesAssembly ) )
2015-08-31 12:39:51 -04:00
{
// Find all the rules source files
2019-04-24 08:31:23 -04:00
Dictionary < FileReference , ModuleRulesContext > ModuleFiles = new Dictionary < FileReference , ModuleRulesContext > ( ) ;
2015-09-08 07:59:23 -04:00
List < FileReference > TargetFiles = new List < FileReference > ( ) ;
// Create a list of plugins for this assembly. If it already exists in the parent assembly, just create an empty assembly.
List < PluginInfo > ForeignPlugins = new List < PluginInfo > ( ) ;
2020-12-20 18:47:42 -04:00
if ( ! Parent . EnumeratePlugins ( ) . Any ( x = > x . File = = PluginFileName ) )
2015-09-08 07:59:23 -04:00
{
2017-07-21 12:42:36 -04:00
ForeignPlugins . Add ( new PluginInfo ( PluginFileName , PluginType . External ) ) ;
2015-09-08 07:59:23 -04:00
}
2019-04-24 08:31:23 -04:00
// Create a new scope for the plugin. It should not reference anything else.
RulesScope Scope = new RulesScope ( "Plugin" , Parent . Scope ) ;
2015-09-08 07:59:23 -04:00
// Find all the modules
2019-04-24 08:31:23 -04:00
ModuleRulesContext PluginModuleContext = new ModuleRulesContext ( Scope , PluginFileName . Directory ) ;
2019-05-31 14:13:36 -04:00
PluginModuleContext . bClassifyAsGameModuleForUHT = ! bContainsEngineModules ;
2019-04-24 08:31:23 -04:00
FindModuleRulesForPlugins ( ForeignPlugins , PluginModuleContext , ModuleFiles ) ;
2015-05-08 15:19:00 -04:00
2015-08-31 12:39:51 -04:00
// Compile the assembly
2017-08-31 12:08:38 -04:00
FileReference AssemblyFileName = FileReference . Combine ( PluginFileName . Directory , "Intermediate" , "Build" , "BuildRules" , Path . GetFileNameWithoutExtension ( PluginFileName . FullName ) + "ModuleRules" + FrameworkAssemblyExtension ) ;
2020-03-13 23:45:06 -04:00
PluginRulesAssembly = new RulesAssembly ( Scope , new List < DirectoryReference > { PluginFileName . Directory } , ForeignPlugins , ModuleFiles , TargetFiles , AssemblyFileName , bContainsEngineModules , DefaultBuildSettings : null , bReadOnly : false , bSkipCompile : bSkipCompile , Parent : Parent ) ;
2015-09-03 08:47:24 -04:00
LoadedAssemblyMap . Add ( PluginFileName , PluginRulesAssembly ) ;
2015-08-31 12:39:51 -04:00
}
return PluginRulesAssembly ;
2014-03-14 14:13:41 -04:00
}
2018-08-14 18:32:34 -04:00
/// <summary>
/// Compile a rules assembly for the current target
/// </summary>
/// <param name="ProjectFile">The project file being compiled</param>
/// <param name="TargetName">The target being built</param>
/// <param name="bSkipRulesCompile">Whether to skip compiling any rules assemblies</param>
2020-09-20 15:18:41 -04:00
/// <param name="bUsePrecompiled">Whether to use a precompiled engine build</param>
2018-08-14 18:32:34 -04:00
/// <param name="ForeignPlugin">Foreign plugin to be compiled</param>
/// <returns>The compiled rules assembly</returns>
2020-12-20 18:47:42 -04:00
public static RulesAssembly CreateTargetRulesAssembly ( FileReference ? ProjectFile , string TargetName , bool bSkipRulesCompile , bool bUsePrecompiled , FileReference ? ForeignPlugin )
2018-08-14 18:32:34 -04:00
{
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 ;
}
2015-09-08 07:59:23 -04:00
/// <summary>
/// Finds all the module rules for plugins under the given directory.
/// </summary>
2017-01-30 16:52:08 -05:00
/// <param name="Plugins">The directory to search</param>
2019-04-24 08:31:23 -04:00
/// <param name="DefaultContext">The default context for any files that are enumerated</param>
/// <param name="ModuleFileToContext">Dictionary which is filled with mappings from the module file to its corresponding context</param>
private static void FindModuleRulesForPlugins ( IReadOnlyList < PluginInfo > Plugins , ModuleRulesContext DefaultContext , Dictionary < FileReference , ModuleRulesContext > ModuleFileToContext )
2015-09-08 07:59:23 -04:00
{
2018-12-20 10:46:51 -05:00
PrefetchRulesFiles ( Plugins . Select ( x = > DirectoryReference . Combine ( x . Directory , "Source" ) ) ) ;
2015-09-24 12:37:21 -04:00
foreach ( PluginInfo Plugin in Plugins )
2015-09-08 07:59:23 -04:00
{
2019-08-12 17:59:42 -04:00
List < FileReference > 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 ) ) ;
}
2015-09-24 12:37:21 -04:00
foreach ( FileReference ModuleFile in PluginModuleFiles )
2015-09-08 07:59:23 -04:00
{
2019-04-24 08:31:23 -04:00
ModuleRulesContext PluginContext = new ModuleRulesContext ( DefaultContext ) ;
PluginContext . DefaultOutputBaseDir = Plugin . Directory ;
PluginContext . Plugin = Plugin ;
ModuleFileToContext [ ModuleFile ] = PluginContext ;
2015-09-08 07:59:23 -04:00
}
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
2015-08-31 10:33:16 -04:00
/// Gets the filename that declares the given type.
2014-03-14 14:13:41 -04:00
/// </summary>
2015-08-31 10:33:16 -04:00
/// <param name="ExistingType">The type to search for.</param>
/// <returns>The filename that declared the given type, or null</returns>
2020-12-20 18:47:42 -04:00
public static string? GetFileNameFromType ( Type ExistingType )
2014-03-14 14:13:41 -04:00
{
2020-12-20 18:47:42 -04:00
FileReference ? FileName ;
2015-09-24 12:37:21 -04:00
if ( EngineRulesAssembly ! = null & & EngineRulesAssembly . TryGetFileNameFromType ( ExistingType , out FileName ) )
2015-08-31 12:59:09 -04:00
{
2015-09-03 08:47:24 -04:00
return FileName . FullName ;
2015-08-31 12:59:09 -04:00
}
2017-05-12 19:19:12 -04:00
2015-09-24 12:37:21 -04:00
foreach ( RulesAssembly RulesAssembly in LoadedAssemblyMap . Values )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( RulesAssembly . TryGetFileNameFromType ( ExistingType , out FileName ) )
2015-08-31 10:33:16 -04:00
{
2015-09-03 08:47:24 -04:00
return FileName . FullName ;
2015-08-31 10:33:16 -04:00
}
2014-03-14 14:13:41 -04:00
}
2015-08-31 10:33:16 -04:00
return null ;
2014-03-14 14:13:41 -04:00
}
2016-05-06 15:20:28 -04:00
}
2014-03-14 14:13:41 -04:00
}