// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using EpicGames.Core;
namespace UnrealBuildTool
{
///
/// A unit of code compilation and linking.
///
abstract class UEBuildModule
{
///
/// The rules for this module
///
public readonly ModuleRules Rules;
///
/// The directory for this module's object files
///
public readonly DirectoryReference IntermediateDirectory;
///
/// The name that uniquely identifies the module.
///
public string Name
{
get { return Rules.Name; }
}
///
/// Path to the module directory
///
public DirectoryReference ModuleDirectory
{
get { return Rules.Directory; }
}
///
/// Paths to all potential module source directories (with platform extension directories added in)
///
public DirectoryReference[] ModuleDirectories;
///
/// The name of the .Build.cs file this module was created from, if any
///
public FileReference RulesFile
{
get { return Rules.File; }
}
///
/// The binary the module will be linked into for the current target. Only set after UEBuildBinary.BindModules is called.
///
public UEBuildBinary Binary = null!;
///
/// The name of the _API define for this module
///
protected readonly string ModuleApiDefine;
///
/// Set of all the public definitions
///
public readonly HashSet PublicDefinitions;
///
/// Set of all public include paths
///
public readonly HashSet PublicIncludePaths;
///
/// Nested public include paths which used to be added automatically, but are now only added for modules with bNestedPublicIncludePaths set.
///
public readonly HashSet LegacyPublicIncludePaths = new HashSet();
///
/// Set of all private include paths
///
public readonly HashSet PrivateIncludePaths;
///
/// Set of all system include paths
///
public readonly HashSet PublicSystemIncludePaths;
///
/// Set of all additional libraries
///
public readonly HashSet PublicLibraries = new HashSet();
///
/// Set of all system libraries
///
public readonly HashSet PublicSystemLibraries = new HashSet();
///
/// Set of all public system library paths
///
public readonly HashSet PublicSystemLibraryPaths;
///
/// Set of additional frameworks
///
public readonly HashSet PublicFrameworks;
///
///
///
public readonly HashSet PublicWeakFrameworks;
///
///
///
protected readonly HashSet PublicAdditionalFrameworks;
///
///
///
protected readonly HashSet PublicAdditionalBundleResources;
///
/// Names of modules with header files that this module's public interface needs access to.
///
public List? PublicIncludePathModules;
///
/// Names of modules that this module's public interface depends on.
///
public List? PublicDependencyModules;
///
/// Names of DLLs that this module should delay load
///
public HashSet PublicDelayLoadDLLs;
///
/// Names of modules with header files that this module's private implementation needs access to.
///
public List? PrivateIncludePathModules;
///
/// Names of modules that this module's private implementation depends on.
///
public List? PrivateDependencyModules;
///
/// Extra modules this module may require at run time
///
public List? DynamicallyLoadedModules;
///
/// Set of all whitelisted restricted folder references
///
private readonly HashSet WhitelistRestrictedFolders;
///
/// Set of aliased restricted folder references
///
public readonly Dictionary AliasRestrictedFolders;
///
/// Constructor
///
/// Rules for this module
/// Intermediate directory for this module
public UEBuildModule(ModuleRules Rules, DirectoryReference IntermediateDirectory)
{
this.Rules = Rules;
this.IntermediateDirectory = IntermediateDirectory;
ModuleApiDefine = Name.ToUpperInvariant() + "_API";
HashSet PublicPreBuildLibraries = HashSetFromOptionalEnumerableStringParameter(Rules.PublicPreBuildLibraries);
PublicDefinitions = HashSetFromOptionalEnumerableStringParameter(Rules.PublicDefinitions);
PublicIncludePaths = CreateDirectoryHashSet(Rules.PublicIncludePaths);
PublicSystemIncludePaths = CreateDirectoryHashSet(Rules.PublicSystemIncludePaths);
PublicSystemLibraryPaths = CreateDirectoryHashSet(Rules.PublicSystemLibraryPaths);
HashSet PublicAdditionalLibraries = HashSetFromOptionalEnumerableStringParameter(Rules.PublicAdditionalLibraries.Union(PublicPreBuildLibraries));
PublicSystemLibraries = HashSetFromOptionalEnumerableStringParameter(Rules.PublicSystemLibraries);
PublicFrameworks = HashSetFromOptionalEnumerableStringParameter(Rules.PublicFrameworks);
PublicWeakFrameworks = HashSetFromOptionalEnumerableStringParameter(Rules.PublicWeakFrameworks);
foreach (string LibraryName in PublicAdditionalLibraries)
{
FileItem Library = FileItem.GetItemByPath(LibraryName);
if (Library.Exists)
{
// if the library path is fully qualified we just add it, this is the preferred method of adding a library
FileReference Location = Library.Location;
PublicLibraries.Add(Location);
}
else if (PublicPreBuildLibraries.Contains(LibraryName))
{
Log.TraceLog("Library '{0}' was not resolvable to a file when used in Module '{1}'. Be sure to add either a TargetRules.PreBuildSteps entry or a TargetRules.PreBuildTargets entry to assure it is built for your target.", LibraryName, Name);
PublicSystemLibraries.Add(LibraryName);
}
else
{
// the library path does not seem to be resolvable as is, lets warn about it as dependency checking will not work for it
Log.TraceWarning("Library '{0}' was not resolvable to a file when used in Module '{1}', assuming it is a filename and will search library paths for it. This is slow and dependency checking will not work for it. Please update reference to be fully qualified alternatively use PublicSystemLibraryPaths if you do intended to use this slow path to suppress this warning. ", LibraryName, Name);
PublicSystemLibraries.Add(LibraryName);
}
}
PublicAdditionalFrameworks = new HashSet();
if(Rules.PublicAdditionalFrameworks != null)
{
foreach(ModuleRules.Framework FrameworkRules in Rules.PublicAdditionalFrameworks)
{
UEBuildFramework Framework;
if (FrameworkRules.IsZipFile())
{
// If FrameworkPath ends in .zip, it needs to be extracted
Framework = new UEBuildFramework(FrameworkRules.Name, FileReference.Combine(ModuleDirectory, FrameworkRules.Path), DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "UnzippedFrameworks", FrameworkRules.Name, Path.GetFileNameWithoutExtension(FrameworkRules.Path)), FrameworkRules.CopyBundledAssets, FrameworkRules.bCopyFramework);
}
else
{
// Framework on disk
Framework = new UEBuildFramework(FrameworkRules.Name, null, DirectoryReference.Combine(ModuleDirectory, FrameworkRules.Path), FrameworkRules.CopyBundledAssets, FrameworkRules.bCopyFramework);
}
PublicAdditionalFrameworks.Add(Framework);
}
}
PublicAdditionalBundleResources = Rules.AdditionalBundleResources == null ? new HashSet() : new HashSet(Rules.AdditionalBundleResources.Select(x => new UEBuildBundleResource(x)));
PublicDelayLoadDLLs = HashSetFromOptionalEnumerableStringParameter(Rules.PublicDelayLoadDLLs);
if(Rules.bUsePrecompiled)
{
PrivateIncludePaths = new HashSet();
}
else
{
PrivateIncludePaths = CreateDirectoryHashSet(Rules.PrivateIncludePaths);
}
WhitelistRestrictedFolders = new HashSet(Rules.WhitelistRestrictedFolders.Select(x => DirectoryReference.Combine(ModuleDirectory, x)));
AliasRestrictedFolders = new Dictionary(Rules.AliasRestrictedFolders);
// get the module directories from the module
ModuleDirectories = Rules.GetAllModuleDirectories();
}
///
/// Determines if a file is part of the given module
///
/// Path to the file
/// True if the file is part of this module
public virtual bool ContainsFile(FileReference Location)
{
return ModuleDirectories.Any(x => Location.IsUnderDirectory(x));
}
///
/// Returns a list of this module's dependencies.
///
/// An enumerable containing the dependencies of the module.
public HashSet GetDependencies(bool bWithIncludePathModules, bool bWithDynamicallyLoadedModules)
{
HashSet Modules = new HashSet();
Modules.UnionWith(PublicDependencyModules!);
Modules.UnionWith(PrivateDependencyModules!);
if(bWithIncludePathModules)
{
Modules.UnionWith(PublicIncludePathModules!);
Modules.UnionWith(PrivateIncludePathModules!);
}
if(bWithDynamicallyLoadedModules)
{
Modules.UnionWith(DynamicallyLoadedModules!);
}
return Modules;
}
///
/// Returns a list of this module's frameworks.
///
/// A List containing the frameworks this module requires.
public List GetPublicFrameworks()
{
return new List(PublicFrameworks);
}
///
/// Returns a list of this module's immediate dependencies.
///
/// An enumerable containing the dependencies of the module.
public IEnumerable GetDirectDependencyModules()
{
return PublicDependencyModules.Concat(PrivateDependencyModules).Concat(DynamicallyLoadedModules);
}
///
/// Converts an optional string list parameter to a well-defined hash set.
///
protected HashSet CreateDirectoryHashSet(IEnumerable InEnumerableStrings)
{
HashSet Directories = new HashSet();
if(InEnumerableStrings != null)
{
foreach(string InputString in InEnumerableStrings)
{
DirectoryReference Dir = new DirectoryReference(ExpandPathVariables(InputString, null, null));
if(DirectoryLookupCache.DirectoryExists(Dir))
{
Directories.Add(Dir);
}
else
{
Log.WriteLineOnce(LogEventType.Warning, LogFormatOptions.NoSeverityPrefix, "{0}: warning: Referenced directory '{1}' does not exist.", RulesFile, Dir);
}
}
}
return Directories;
}
///
/// Converts an optional string list parameter to a well-defined hash set.
///
protected HashSet HashSetFromOptionalEnumerableStringParameter(IEnumerable InEnumerableStrings)
{
return InEnumerableStrings == null ? new HashSet() : new HashSet(InEnumerableStrings.Select(x => ExpandPathVariables(x, null, null)));
}
///
/// Determines whether this module has a circular dependency on the given module
///
public bool HasCircularDependencyOn(string ModuleName)
{
return Rules.CircularlyReferencedDependentModules.Contains(ModuleName);
}
///
/// Enumerates additional build products which may be produced by this module. Some platforms (eg. Mac, Linux) can link directly against .so/.dylibs, but they
/// are also copied to the output folder by the toolchain.
///
/// List to which libraries required by this module are added
/// List of bundle resources required by this module
public void GatherAdditionalResources(List Libraries, List BundleResources)
{
Libraries.AddRange(PublicLibraries.Select(x => x.FullName));
Libraries.AddRange(PublicSystemLibraries);
BundleResources.AddRange(PublicAdditionalBundleResources);
}
///
/// Determines the distribution level of a module based on its directory and includes.
///
/// The set of additional paths to check, if available
/// Map of the restricted folder types to the first found instance
public Dictionary FindRestrictedFolderReferences(List RootDirectories)
{
Dictionary References = new Dictionary();
if (!Rules.bLegalToDistributeObjectCode)
{
// Find all the directories that this module references
HashSet ReferencedDirs = new HashSet();
GetReferencedDirectories(ReferencedDirs);
// Remove all the whitelisted folders
ReferencedDirs.ExceptWith(WhitelistRestrictedFolders);
ReferencedDirs.ExceptWith(PublicDependencyModules.SelectMany(x => x.WhitelistRestrictedFolders));
ReferencedDirs.ExceptWith(PrivateDependencyModules.SelectMany(x => x.WhitelistRestrictedFolders));
// Add flags for each of them
foreach(DirectoryReference ReferencedDir in ReferencedDirs)
{
// Find the base directory containing this reference
DirectoryReference BaseDir = RootDirectories.FirstOrDefault(x => ReferencedDir.IsUnderDirectory(x));
// @todo platplug does this need to check platform extension engine directories? what are ReferencedDir's here?
if (BaseDir == null)
{
continue;
}
// Add references to each of the restricted folders
List Folders = RestrictedFolders.FindRestrictedFolders(BaseDir, ReferencedDir);
foreach(RestrictedFolder Folder in Folders)
{
if(!References.ContainsKey(Folder))
{
References.Add(Folder, ReferencedDir);
}
}
}
}
return References;
}
///
/// Finds all the directories that this folder references when building
///
/// Set of directories to add to
protected virtual void GetReferencedDirectories(HashSet Directories)
{
Directories.Add(ModuleDirectory);
foreach(DirectoryReference PublicIncludePath in PublicIncludePaths)
{
Directories.Add(PublicIncludePath);
}
foreach(DirectoryReference PrivateIncludePath in PrivateIncludePaths)
{
Directories.Add(PrivateIncludePath);
}
foreach(DirectoryReference PublicSystemIncludePath in PublicSystemIncludePaths)
{
Directories.Add(PublicSystemIncludePath);
}
foreach (DirectoryReference PublicSystemLibraryPath in PublicSystemLibraryPaths)
{
Directories.Add(PublicSystemLibraryPath);
}
}
///
/// Find all the modules which affect the private compile environment.
///
///
protected void FindModulesInPrivateCompileEnvironment(Dictionary ModuleToIncludePathsOnlyFlag)
{
// Add in all the modules that are only in the private compile environment
foreach (UEBuildModule PrivateDependencyModule in PrivateDependencyModules!)
{
PrivateDependencyModule.FindModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
foreach (UEBuildModule PrivateIncludePathModule in PrivateIncludePathModules!)
{
PrivateIncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
// Add the modules in the public compile environment
FindModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
///
/// Find all the modules which affect the public compile environment.
///
///
protected void FindModulesInPublicCompileEnvironment(Dictionary ModuleToIncludePathsOnlyFlag)
{
//
bool bModuleIncludePathsOnly;
if (ModuleToIncludePathsOnlyFlag.TryGetValue(this, out bModuleIncludePathsOnly) && !bModuleIncludePathsOnly)
{
return;
}
ModuleToIncludePathsOnlyFlag[this] = false;
foreach (UEBuildModule DependencyModule in PublicDependencyModules!)
{
DependencyModule.FindModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
// Now add an include paths from modules with header files that we need access to, but won't necessarily be importing
foreach (UEBuildModule IncludePathModule in PublicIncludePathModules!)
{
IncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
}
///
/// Find all the modules which affect the public compile environment. Searches through
///
///
protected void FindIncludePathModulesInPublicCompileEnvironment(Dictionary ModuleToIncludePathsOnlyFlag)
{
if (!ModuleToIncludePathsOnlyFlag.ContainsKey(this))
{
// Add this module to the list
ModuleToIncludePathsOnlyFlag.Add(this, true);
// Include any of its public include path modules in the compile environment too
foreach (UEBuildModule IncludePathModule in PublicIncludePathModules!)
{
IncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
}
}
}
private void AddIncludePaths(HashSet IncludePaths, HashSet IncludePathsToAdd)
{
// Need to check whether directories exist to avoid bloating compiler command line with generated code directories
foreach(DirectoryReference IncludePathToAdd in IncludePathsToAdd)
{
IncludePaths.Add(IncludePathToAdd);
}
}
///
/// Sets up the environment for compiling any module that includes the public interface of this module.
///
public virtual void AddModuleToCompileEnvironment(
UEBuildBinary? SourceBinary,
HashSet IncludePaths,
HashSet SystemIncludePaths,
HashSet ModuleInterfacePaths,
List Definitions,
List AdditionalFrameworks,
List AdditionalPrerequisites,
bool bLegacyPublicIncludePaths
)
{
// Add the module's parent directory to the include path, so we can root #includes from generated source files to it
IncludePaths.Add(ModuleDirectory.ParentDirectory);
// Add this module's public include paths and definitions.
AddIncludePaths(IncludePaths, PublicIncludePaths);
if(bLegacyPublicIncludePaths)
{
AddIncludePaths(IncludePaths, LegacyPublicIncludePaths);
}
SystemIncludePaths.UnionWith(PublicSystemIncludePaths);
Definitions.AddRange(PublicDefinitions);
// Add the import or export declaration for the module
if(Rules.Type == ModuleRules.ModuleType.CPlusPlus)
{
if(Rules.Target.LinkType == TargetLinkType.Monolithic)
{
if (Rules.Target.bShouldCompileAsDLL && (Rules.Target.bHasExports || Rules.ModuleSymbolVisibility == ModuleRules.SymbolVisibility.VisibileForDll))
{
Definitions.Add(ModuleApiDefine + "=DLLEXPORT");
}
else
{
Definitions.Add(ModuleApiDefine + "=");
}
}
else if(Binary == null || SourceBinary != Binary)
{
Definitions.Add(ModuleApiDefine + "=DLLIMPORT");
}
else if(!Binary.bAllowExports)
{
Definitions.Add(ModuleApiDefine + "=");
}
else
{
Definitions.Add(ModuleApiDefine + "=DLLEXPORT");
}
}
// Add the additional frameworks so that the compiler can know about their #include paths
AdditionalFrameworks.AddRange(PublicAdditionalFrameworks);
// Add any generated type library headers
if (Rules.TypeLibraries.Count > 0)
{
IncludePaths.Add(IntermediateDirectory);
foreach (ModuleRules.TypeLibrary TypeLibrary in Rules.TypeLibraries)
{
AdditionalPrerequisites.Add(FileItem.GetItemByFileReference(FileReference.Combine(IntermediateDirectory, TypeLibrary.Header)));
}
}
}
///
/// Sets up the environment for compiling this module.
///
protected virtual void SetupPrivateCompileEnvironment(
HashSet IncludePaths,
HashSet SystemIncludePaths,
HashSet ModuleInterfacePaths,
List Definitions,
List AdditionalFrameworks,
List AdditionalPrerequisites,
bool bWithLegacyPublicIncludePaths
)
{
if (!Rules.bTreatAsEngineModule)
{
Definitions.Add("DEPRECATED_FORGAME=DEPRECATED");
Definitions.Add("UE_DEPRECATED_FORGAME=UE_DEPRECATED");
}
// Add this module's private include paths and definitions.
IncludePaths.UnionWith(PrivateIncludePaths);
// Find all the modules that are part of the public compile environment for this module.
Dictionary ModuleToIncludePathsOnlyFlag = new Dictionary();
FindModulesInPrivateCompileEnvironment(ModuleToIncludePathsOnlyFlag);
// Now set up the compile environment for the modules in the original order that we encountered them
foreach (UEBuildModule Module in ModuleToIncludePathsOnlyFlag.Keys)
{
Module.AddModuleToCompileEnvironment(Binary, IncludePaths, SystemIncludePaths, ModuleInterfacePaths, Definitions, AdditionalFrameworks, AdditionalPrerequisites, bWithLegacyPublicIncludePaths);
}
}
///
/// Expand path variables within the context of this module
///
/// Path to expand variables within
/// Directory containing the binary that links this module. May be mull.
/// Directory containing the output executable. May be null.
/// The path with variables expanded
public string ExpandPathVariables(string Path, DirectoryReference? BinaryOutputDir, DirectoryReference? TargetOutputDir)
{
if(Path.StartsWith("$(", StringComparison.Ordinal))
{
int StartIdx = 2;
for(int EndIdx = StartIdx; EndIdx < Path.Length; EndIdx++)
{
if(Path[EndIdx] == ')')
{
if(MatchVariableName(Path, StartIdx, EndIdx, "EngineDir"))
{
Path = UnrealBuildTool.EngineDirectory + Path.Substring(EndIdx + 1);
}
else if(MatchVariableName(Path, StartIdx, EndIdx, "ProjectDir"))
{
if(Rules.Target.ProjectFile == null)
{
Path = UnrealBuildTool.EngineDirectory + Path.Substring(EndIdx + 1);
}
else
{
Path = Rules.Target.ProjectFile.Directory + Path.Substring(EndIdx + 1);
}
}
else if(MatchVariableName(Path, StartIdx, EndIdx, "ModuleDir"))
{
Path = Rules.ModuleDirectory + Path.Substring(EndIdx + 1);
}
else if(MatchVariableName(Path, StartIdx, EndIdx, "PluginDir"))
{
Path = Rules.PluginDirectory + Path.Substring(EndIdx + 1);
}
else if(BinaryOutputDir != null && MatchVariableName(Path, StartIdx, EndIdx, "BinaryOutputDir"))
{
Path = BinaryOutputDir.FullName + Path.Substring(EndIdx + 1);
}
else if(TargetOutputDir != null && MatchVariableName(Path, StartIdx, EndIdx, "TargetOutputDir"))
{
Path = TargetOutputDir.FullName + Path.Substring(EndIdx + 1);
}
else
{
string Name = Path.Substring(StartIdx, EndIdx - StartIdx);
string? Value = Environment.GetEnvironmentVariable(Name);
if(String.IsNullOrEmpty(Value))
{
throw new BuildException("Environment variable '{0}' is not defined (referenced by {1})", Name, Rules.File);
}
Path = Value + Path.Substring(EndIdx + 1);
}
break;
}
}
}
return Path;
}
///
/// Match a variable name within a path
///
/// The path variable
/// Start index of the substring to match
/// End index of the substring to match
/// Variable name to compare against
/// True if the variable name matches
private bool MatchVariableName(string Path, int StartIdx, int EndIdx, string Name)
{
return Name.Length == EndIdx - StartIdx && String.Compare(Path, StartIdx, Name, 0, EndIdx - StartIdx) == 0;
}
///
/// Expand path variables within the context of this module
///
/// Path to expand variables within
/// Directory containing the binary that links this module. May be mull.
/// Directory containing the output executable. May be null.
/// The path with variables expanded
private IEnumerable ExpandPathVariables(IEnumerable Paths, DirectoryReference? BinaryDir, DirectoryReference? ExeDir)
{
foreach(string Path in Paths)
{
yield return ExpandPathVariables(Path, BinaryDir, ExeDir);
}
}
///
/// Sets up the environment for linking any module that includes the public interface of this module.
///
protected virtual void SetupPublicLinkEnvironment(
UEBuildBinary? SourceBinary,
List Libraries,
List SystemLibraryPaths,
List SystemLibraries,
List RuntimeLibraryPaths,
List Frameworks,
List WeakFrameworks,
List AdditionalFrameworks,
List AdditionalBundleResources,
List DelayLoadDLLs,
List BinaryDependencies,
HashSet VisitedModules,
DirectoryReference ExeDir
)
{
// There may be circular dependencies in compile dependencies, so we need to avoid reentrance.
if (VisitedModules.Add(this))
{
// Add this module's binary to the binary dependencies.
if (Binary != null
&& Binary != SourceBinary
&& !BinaryDependencies.Contains(Binary))
{
BinaryDependencies.Add(Binary);
}
// If this module belongs to a static library that we are not currently building, recursively add the link environment settings for all of its dependencies too.
// Keep doing this until we reach a module that is not part of a static library (or external module, since they have no associated binary).
// Static libraries do not contain the symbols for their dependencies, so we need to recursively gather them to be linked into other binary types.
bool bIsBuildingAStaticLibrary = (SourceBinary != null && SourceBinary.Type == UEBuildBinaryType.StaticLibrary);
bool bIsModuleBinaryAStaticLibrary = (Binary != null && Binary.Type == UEBuildBinaryType.StaticLibrary);
if (!bIsBuildingAStaticLibrary && bIsModuleBinaryAStaticLibrary)
{
// Gather all dependencies and recursively call SetupPublicLinkEnvironmnet
List AllDependencyModules = new List();
AllDependencyModules.AddRange(PrivateDependencyModules!);
AllDependencyModules.AddRange(PublicDependencyModules!);
foreach (UEBuildModule DependencyModule in AllDependencyModules)
{
bool bIsExternalModule = (DependencyModule as UEBuildModuleExternal != null);
bool bIsInStaticLibrary = (DependencyModule.Binary != null && DependencyModule.Binary.Type == UEBuildBinaryType.StaticLibrary);
if (bIsExternalModule || bIsInStaticLibrary)
{
DependencyModule.SetupPublicLinkEnvironment(SourceBinary, Libraries, SystemLibraryPaths, SystemLibraries, RuntimeLibraryPaths, Frameworks, WeakFrameworks,
AdditionalFrameworks, AdditionalBundleResources, DelayLoadDLLs, BinaryDependencies, VisitedModules, ExeDir);
}
}
}
// Add this module's public include library paths and additional libraries.
Libraries.AddRange(PublicLibraries);
SystemLibraryPaths.AddRange(PublicSystemLibraryPaths);
SystemLibraries.AddRange(PublicSystemLibraries);
RuntimeLibraryPaths.AddRange(ExpandPathVariables(Rules.PublicRuntimeLibraryPaths, SourceBinary?.OutputDir, ExeDir));
Frameworks.AddRange(PublicFrameworks);
WeakFrameworks.AddRange(PublicWeakFrameworks);
AdditionalBundleResources.AddRange(PublicAdditionalBundleResources);
AdditionalFrameworks.AddRange(PublicAdditionalFrameworks);
DelayLoadDLLs.AddRange(PublicDelayLoadDLLs);
}
}
///
/// Sets up the environment for linking this module.
///
public virtual void SetupPrivateLinkEnvironment(
UEBuildBinary SourceBinary,
LinkEnvironment LinkEnvironment,
List BinaryDependencies,
HashSet VisitedModules,
DirectoryReference ExeDir
)
{
// Add the private rpaths
LinkEnvironment.RuntimeLibraryPaths.AddRange(ExpandPathVariables(Rules.PrivateRuntimeLibraryPaths, SourceBinary.OutputDir, ExeDir));
// Allow the module's public dependencies to add library paths and additional libraries to the link environment.
SetupPublicLinkEnvironment(SourceBinary, LinkEnvironment.Libraries, LinkEnvironment.SystemLibraryPaths, LinkEnvironment.SystemLibraries, LinkEnvironment.RuntimeLibraryPaths, LinkEnvironment.Frameworks, LinkEnvironment.WeakFrameworks,
LinkEnvironment.AdditionalFrameworks, LinkEnvironment.AdditionalBundleResources, LinkEnvironment.DelayLoadDLLs, BinaryDependencies, VisitedModules, ExeDir);
// Also allow the module's public and private dependencies to modify the link environment.
List AllDependencyModules = new List();
AllDependencyModules.AddRange(PrivateDependencyModules!);
AllDependencyModules.AddRange(PublicDependencyModules!);
foreach (UEBuildModule DependencyModule in AllDependencyModules)
{
DependencyModule.SetupPublicLinkEnvironment(SourceBinary, LinkEnvironment.Libraries, LinkEnvironment.SystemLibraryPaths, LinkEnvironment.SystemLibraries, LinkEnvironment.RuntimeLibraryPaths, LinkEnvironment.Frameworks, LinkEnvironment.WeakFrameworks,
LinkEnvironment.AdditionalFrameworks, LinkEnvironment.AdditionalBundleResources, LinkEnvironment.DelayLoadDLLs, BinaryDependencies, VisitedModules, ExeDir);
}
// Add all the additional properties
LinkEnvironment.AdditionalProperties.AddRange(Rules.AdditionalPropertiesForReceipt.Inner);
// this is a link-time property that needs to be accumulated (if any modules contributing to this module is ignoring, all are ignoring)
LinkEnvironment.bIgnoreUnresolvedSymbols |= Rules.bIgnoreUnresolvedSymbols;
}
///
/// Compiles the module, and returns a list of files output by the compiler.
///
public virtual List Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, List SpecificFilesToCompile, ISourceFileWorkingSet WorkingSet, IActionGraphBuilder Graph)
{
// Generate type libraries for Windows
foreach(ModuleRules.TypeLibrary TypeLibrary in Rules.TypeLibraries)
{
FileReference OutputFile = FileReference.Combine(IntermediateDirectory, TypeLibrary.Header);
ToolChain.GenerateTypeLibraryHeader(CompileEnvironment, TypeLibrary, OutputFile, Graph);
}
return new List();
}
// Object interface.
public override string ToString()
{
return Name;
}
///
/// Finds the modules referenced by this module which have not yet been bound to a binary
///
/// List of unbound modules
public List GetUnboundReferences()
{
List Modules = new List();
Modules.AddRange(PrivateDependencyModules.Where(x => x.Binary == null));
Modules.AddRange(PublicDependencyModules.Where(x => x.Binary == null));
return Modules;
}
///
/// Gets all of the modules referenced by this module
///
/// Hash of all referenced modules with their addition index.
/// Hashset used to ignore modules which are already added to the list
/// True if dynamically loaded modules (and all of their dependent modules) should be included.
/// True if circular dependencies should be processed
/// True to return only this module's direct dependencies
public virtual void GetAllDependencyModules(List ReferencedModules, HashSet IgnoreReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies)
{
}
public delegate UEBuildModule CreateModuleDelegate(string Name, string ReferenceChain);
///
/// Public entry point to recursively create a module and all its dependencies
///
///
///
public void RecursivelyCreateModules(CreateModuleDelegate CreateModule, string ReferenceChain)
{
List ReferenceStack = new List();
RecursivelyCreateModules(CreateModule, ReferenceChain, ReferenceStack);
}
///
/// Creates all the modules required for this target
///
/// Delegate to create a module with a given name
/// Chain of references before reaching this module
/// Stack of module dependencies that led to this module
protected void RecursivelyCreateModules(CreateModuleDelegate CreateModule, string ReferenceChain, List ReferenceStack)
{
// Name of this reference
string ThisRefName = (RulesFile == null) ? Name : RulesFile.GetFileName();
// Set the reference chain for anything referenced by this module
string NextReferenceChain = String.Format("{0} -> {1}", ReferenceChain, ThisRefName);
// We need to check for cycles if this module has already been created and its name is in the stack.
bool CheckForCycles = PrivateIncludePathModules != null && ReferenceStack.Contains(this);
// Add us to the reference stack - note do this before checking for cycles as we allow them if an element in the stack
// declares the next element at leading to a dependency cycle.
ReferenceStack.Add(this);
if (CheckForCycles)
{
// We are FeatureModuleA and we know we're already in the list so there must be a circular dependency like this:
// Target -> FeatureModuleA -> FeatureModuleB -> BaseModule -> FeatureModuleA
// In this case it's up to BaseModule to confess that it's going to introduce a
// cycle by referencing FeatureModuleA so let's check it did that.
// (Note: it's *bad* that it is doing this, but it's even worse not to flag these
// cycles when they're introduced and have a way of tracking them!)
string? GuiltyModule = null;
string? VictimModule = null;
for (int i = 0; i < ReferenceStack.Count() - 1; i++)
{
UEBuildModule ReferringModule = ReferenceStack.ElementAt(i);
UEBuildModule TargetModule = ReferenceStack.ElementAt(i + 1);
if (ReferringModule.HasCircularDependencyOn(TargetModule.Name))
{
GuiltyModule = ReferringModule.Name;
VictimModule = TargetModule.Name;
break;
}
}
// No module has confessed its guilt, so this is an error.
if (string.IsNullOrEmpty(GuiltyModule))
{
string CycleChain = string.Join(" -> ", ReferenceStack);
Log.TraceError("Circular dependency on {0} detected.\n" +
"\tFull Route: {1}\n" +
"\tCycled Route: is {2}.\n" +
"Break this loop by moving dependencies into a separate module or using Private/PublicIncludePathModuleNames to reference declarations\n",
ThisRefName, NextReferenceChain, CycleChain);
}
else
{
Log.TraceVerbose("Found circular reference to {0}, but {1} declares a cycle on {2} which breaks the chain", ThisRefName, GuiltyModule, VictimModule);
}
}
// Recursively create all the public include path modules. These modules may not be added to the target (and we don't process their referenced
// dependencies), but they need to be created to set up their include paths.
RecursivelyCreateIncludePathModulesByName(Rules.PublicIncludePathModuleNames, ref PublicIncludePathModules, CreateModule, NextReferenceChain);
// Create all the referenced modules. This path can be recursive, so we check against PrivateIncludePathModules to ensure we don't recurse through the
// same module twice (it produces better errors if something fails).
if(PrivateIncludePathModules == null)
{
// Create the private include path modules
RecursivelyCreateIncludePathModulesByName(Rules.PrivateIncludePathModuleNames, ref PrivateIncludePathModules, CreateModule, NextReferenceChain);
// Create all the dependency modules - pass through the reference stack so we can check for cycles
RecursivelyCreateModulesByName(Rules.PublicDependencyModuleNames, ref PublicDependencyModules, CreateModule, NextReferenceChain, ReferenceStack);
RecursivelyCreateModulesByName(Rules.PrivateDependencyModuleNames, ref PrivateDependencyModules, CreateModule, NextReferenceChain, ReferenceStack);
// Dynamic loads aren't considered a reference chain so start with an empty stack
RecursivelyCreateModulesByName(Rules.DynamicallyLoadedModuleNames, ref DynamicallyLoadedModules, CreateModule, NextReferenceChain, new List());
}
// pop us off the current stack
ReferenceStack.RemoveAt(ReferenceStack.Count - 1);
}
private static void RecursivelyCreateModulesByName(List ModuleNames, ref List? Modules, CreateModuleDelegate CreateModule, string ReferenceChain, List ReferenceStack)
{
// Check whether the module list is already set. We set this immediately (via the ref) to avoid infinite recursion.
if (Modules == null)
{
Modules = new List();
foreach (string ModuleName in ModuleNames)
{
UEBuildModule Module = CreateModule(ModuleName, ReferenceChain);
if (!Modules.Contains(Module))
{
Module.RecursivelyCreateModules(CreateModule, ReferenceChain, ReferenceStack);
Modules.Add(Module);
}
}
}
}
private static void RecursivelyCreateIncludePathModulesByName(List ModuleNames, ref List? Modules, CreateModuleDelegate CreateModule, string ReferenceChain)
{
// Check whether the module list is already set. We set this immediately (via the ref) to avoid infinite recursion.
if (Modules == null)
{
Modules = new List();
foreach (string ModuleName in ModuleNames)
{
UEBuildModule Module = CreateModule(ModuleName, ReferenceChain);
RecursivelyCreateIncludePathModulesByName(Module.Rules.PublicIncludePathModuleNames, ref Module.PublicIncludePathModules, CreateModule, ReferenceChain);
Modules.Add(Module);
}
}
}
///
/// Returns valueless API defines (like MODULE_API)
///
public IEnumerable GetEmptyApiMacros()
{
if (Rules.Type == ModuleRules.ModuleType.CPlusPlus)
{
return new[] {ModuleApiDefine + "="};
}
return new string[0];
}
///
/// Write information about this binary to a JSON file
///
/// The output directory for the binary containing this module
/// The output directory for the target executable
/// Writer for this binary's data
public virtual void ExportJson(DirectoryReference? BinaryOutputDir, DirectoryReference? TargetOutputDir, JsonWriter Writer)
{
Writer.WriteValue("Name", Name);
Writer.WriteValue("Directory", ModuleDirectory.FullName);
Writer.WriteValue("Rules", RulesFile.FullName);
Writer.WriteValue("PCHUsage", Rules.PCHUsage.ToString());
if (Rules.PrivatePCHHeaderFile != null)
{
Writer.WriteValue("PrivatePCH", FileReference.Combine(ModuleDirectory, Rules.PrivatePCHHeaderFile).FullName);
}
if (Rules.SharedPCHHeaderFile != null)
{
Writer.WriteValue("SharedPCH", FileReference.Combine(ModuleDirectory, Rules.SharedPCHHeaderFile).FullName);
}
ExportJsonModuleArray(Writer, "PublicDependencyModules", PublicDependencyModules);
ExportJsonModuleArray(Writer, "PublicIncludePathModules", PublicIncludePathModules);
ExportJsonModuleArray(Writer, "PrivateDependencyModules", PrivateDependencyModules);
ExportJsonModuleArray(Writer, "PrivateIncludePathModules", PrivateIncludePathModules);
ExportJsonModuleArray(Writer, "DynamicallyLoadedModules", DynamicallyLoadedModules);
ExportJsonStringArray(Writer, "PublicSystemIncludePaths", PublicSystemIncludePaths.Select(x => x.FullName));
ExportJsonStringArray(Writer, "PublicIncludePaths", PublicIncludePaths.Select(x => x.FullName));
ExportJsonStringArray(Writer, "PrivateIncludePaths", PrivateIncludePaths.Select(x => x.FullName));
ExportJsonStringArray(Writer, "PublicLibraries", PublicLibraries.Select(x => x.FullName));
ExportJsonStringArray(Writer, "PublicSystemLibraries", PublicSystemLibraries);
ExportJsonStringArray(Writer, "PublicSystemLibraryPaths", PublicSystemLibraryPaths.Select(x => x.FullName));
ExportJsonStringArray(Writer, "PublicFrameworks", PublicFrameworks);
ExportJsonStringArray(Writer, "PublicWeakFrameworks", PublicWeakFrameworks);
ExportJsonStringArray(Writer, "PublicDelayLoadDLLs", PublicDelayLoadDLLs);
ExportJsonStringArray(Writer, "PublicDefinitions", PublicDefinitions);
Writer.WriteArrayStart("CircularlyReferencedModules");
foreach(string ModuleName in Rules.CircularlyReferencedDependentModules)
{
Writer.WriteValue(ModuleName);
}
Writer.WriteArrayEnd();
// Don't add runtime dependencies for modules that aren't being linked in. They may reference BinaryOutputDir, which is invalid.
if (Binary != null)
{
Writer.WriteArrayStart("RuntimeDependencies");
foreach (ModuleRules.RuntimeDependency RuntimeDependency in Rules.RuntimeDependencies.Inner)
{
Writer.WriteObjectStart();
Writer.WriteValue("Path", ExpandPathVariables(RuntimeDependency.Path, BinaryOutputDir, TargetOutputDir));
if (RuntimeDependency.SourcePath != null)
{
Writer.WriteValue("SourcePath", ExpandPathVariables(RuntimeDependency.SourcePath, BinaryOutputDir, TargetOutputDir));
}
Writer.WriteValue("Type", RuntimeDependency.Type.ToString());
Writer.WriteObjectEnd();
}
Writer.WriteArrayEnd();
}
}
///
/// Write an array of module names to a JSON writer
///
/// Writer for the array data
/// Name of the array property
/// Sequence of modules to write. May be null.
void ExportJsonModuleArray(JsonWriter Writer, string ArrayName, IEnumerable? Modules)
{
Writer.WriteArrayStart(ArrayName);
if (Modules != null)
{
foreach (UEBuildModule Module in Modules)
{
Writer.WriteValue(Module.Name);
}
}
Writer.WriteArrayEnd();
}
///
/// Write an array of strings to a JSON writer
///
/// Writer for the array data
/// Name of the array property
/// Sequence of strings to write. May be null.
void ExportJsonStringArray(JsonWriter Writer, string ArrayName, IEnumerable? Strings)
{
Writer.WriteArrayStart(ArrayName);
if (Strings != null)
{
foreach(string String in Strings)
{
Writer.WriteValue(String);
}
}
Writer.WriteArrayEnd();
}
};
}