Files
Ben Marsh 0cc6e3dca6 Copying //UE4/Dev-Build to Dev-Main (//UE4/Dev-Main)
#rb none
#rnx

[CL 6631504 by Ben Marsh in Main branch]
2019-05-24 11:51:54 -04:00

709 lines
27 KiB
C#

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Tools.DotNETCommon;
namespace UnrealBuildTool
{
/// <summary>
/// Stores information about a compiled rules assembly and the types it contains
/// </summary>
public class RulesAssembly
{
/// <summary>
/// Outers scope for items created by this assembly. Used for chaining assemblies together.
/// </summary>
internal readonly RulesScope Scope;
/// <summary>
/// The compiled assembly
/// </summary>
private Assembly CompiledAssembly;
/// <summary>
/// The base directory for this assembly
/// </summary>
private DirectoryReference BaseDir;
/// <summary>
/// All the plugins included in this assembly
/// </summary>
private IReadOnlyList<PluginInfo> Plugins;
/// <summary>
/// Maps module names to their actual xxx.Module.cs file on disk
/// </summary>
private Dictionary<string, FileReference> ModuleNameToModuleFile = new Dictionary<string, FileReference>(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Maps target names to their actual xxx.Target.cs file on disk
/// </summary>
private Dictionary<string, FileReference> TargetNameToTargetFile = new Dictionary<string, FileReference>(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Mapping from module file to its context.
/// </summary>
private Dictionary<FileReference, ModuleRulesContext> ModuleFileToContext;
/// <summary>
/// Cache for whether a module has source code
/// </summary>
private Dictionary<FileReference, bool> ModuleHasSource = new Dictionary<FileReference, bool>();
/// <summary>
/// Whether this assembly contains engine modules. Used to set default values for bTreatAsEngineModule.
/// </summary>
private bool bContainsEngineModules;
/// <summary>
/// Whether to use backwards compatible default settings for module and target rules. This is enabled by default for game projects to support a simpler migration path, but
/// is disabled for engine modules.
/// </summary>
private bool bUseBackwardsCompatibleDefaults;
/// <summary>
/// Whether the modules and targets in this assembly are read-only
/// </summary>
private bool bReadOnly;
/// <summary>
/// The parent rules assembly that this assembly inherits. Game assemblies inherit the engine assembly, and the engine assembly inherits nothing.
/// </summary>
private RulesAssembly Parent;
/// <summary>
/// Constructor. Compiles a rules assembly from the given source files.
/// </summary>
/// <param name="Scope">The scope of items created by this assembly</param>
/// <param name="BaseDir">The base directory for this assembly</param>
/// <param name="Plugins">All the plugins included in this assembly</param>
/// <param name="ModuleFileToContext">List of module files to compile</param>
/// <param name="TargetFiles">List of target files to compile</param>
/// <param name="AssemblyFileName">The output path for the compiled assembly</param>
/// <param name="bContainsEngineModules">Whether this assembly contains engine modules. Used to initialize the default value for ModuleRules.bTreatAsEngineModule.</param>
/// <param name="bUseBackwardsCompatibleDefaults">Whether modules in this assembly should use backwards-compatible defaults.</param>
/// <param name="bReadOnly">Whether the modules and targets in this assembly are installed, and should be created with the bUsePrecompiled flag set</param>
/// <param name="bSkipCompile">Whether to skip compiling this assembly</param>
/// <param name="Parent">The parent rules assembly</param>
internal RulesAssembly(RulesScope Scope, DirectoryReference BaseDir, IReadOnlyList<PluginInfo> Plugins, Dictionary<FileReference, ModuleRulesContext> ModuleFileToContext, List<FileReference> TargetFiles, FileReference AssemblyFileName, bool bContainsEngineModules, bool bUseBackwardsCompatibleDefaults, bool bReadOnly, bool bSkipCompile, RulesAssembly Parent)
{
this.Scope = Scope;
this.BaseDir = BaseDir;
this.Plugins = Plugins;
this.ModuleFileToContext = ModuleFileToContext;
this.bContainsEngineModules = bContainsEngineModules;
this.bUseBackwardsCompatibleDefaults = bUseBackwardsCompatibleDefaults;
this.bReadOnly = bReadOnly;
this.Parent = Parent;
// Find all the source files
HashSet<FileReference> AssemblySourceFiles = new HashSet<FileReference>();
AssemblySourceFiles.UnionWith(ModuleFileToContext.Keys);
AssemblySourceFiles.UnionWith(TargetFiles);
// Compile the assembly
if (AssemblySourceFiles.Count > 0)
{
List<string> PreprocessorDefines = GetPreprocessorDefinitions();
CompiledAssembly = DynamicCompilation.CompileAndLoadAssembly(AssemblyFileName, AssemblySourceFiles, PreprocessorDefines: PreprocessorDefines, DoNotCompile: bSkipCompile);
}
// Setup the module map
foreach (FileReference ModuleFile in ModuleFileToContext.Keys)
{
string ModuleName = ModuleFile.GetFileNameWithoutAnyExtensions();
if (!ModuleNameToModuleFile.ContainsKey(ModuleName))
{
ModuleNameToModuleFile.Add(ModuleName, ModuleFile);
}
}
// Setup the target map
foreach (FileReference TargetFile in TargetFiles)
{
string TargetName = TargetFile.GetFileNameWithoutAnyExtensions();
if (!TargetNameToTargetFile.ContainsKey(TargetName))
{
TargetNameToTargetFile.Add(TargetName, TargetFile);
}
}
// Write any deprecation warnings for methods overriden from a base with the [ObsoleteOverride] attribute. Unlike the [Obsolete] attribute, this ensures the message
// is given because the method is implemented, not because it's called.
if (CompiledAssembly != null)
{
foreach (Type CompiledType in CompiledAssembly.GetTypes())
{
foreach (MethodInfo Method in CompiledType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
ObsoleteOverrideAttribute Attribute = Method.GetCustomAttribute<ObsoleteOverrideAttribute>(true);
if (Attribute != null)
{
FileReference Location;
if (!TryGetFileNameFromType(CompiledType, out Location))
{
Location = new FileReference(CompiledAssembly.Location);
}
Log.TraceWarning("{0}: warning: {1}", Location, Attribute.Message);
}
}
if(CompiledType.BaseType == typeof(ModuleRules))
{
ConstructorInfo Constructor = CompiledType.GetConstructor(new Type[] { typeof(TargetInfo) });
if(Constructor != null)
{
FileReference Location;
if (!TryGetFileNameFromType(CompiledType, out Location))
{
Location = new FileReference(CompiledAssembly.Location);
}
Log.TraceWarning("{0}: warning: Module constructors should take a ReadOnlyTargetRules argument (rather than a TargetInfo argument) and pass it to the base class constructor from 4.15 onwards. Please update the method signature.", Location);
}
}
}
}
}
/// <summary>
/// Determines if the given path is read-only
/// </summary>
/// <param name="Location">The location to check</param>
/// <returns>True if the path is read-only, false otherwise</returns>
public bool IsReadOnly(FileSystemReference Location)
{
if(Location.IsUnderDirectory(BaseDir))
{
return bReadOnly;
}
else if(Parent != null)
{
return Parent.IsReadOnly(Location);
}
else
{
return false;
}
}
/// <summary>
/// Finds all the preprocessor definitions that need to be set for the current engine.
/// </summary>
/// <returns>List of preprocessor definitions that should be set</returns>
public static List<string> GetPreprocessorDefinitions()
{
List<string> PreprocessorDefines = new List<string>();
PreprocessorDefines.Add("WITH_FORWARDED_MODULE_RULES_CTOR");
PreprocessorDefines.Add("WITH_FORWARDED_TARGET_RULES_CTOR");
// Define macros for the UE4 version, starting with 4.17
BuildVersion Version;
if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version))
{
for(int MinorVersion = 17; MinorVersion <= Version.MinorVersion; MinorVersion++)
{
PreprocessorDefines.Add(String.Format("UE_4_{0}_OR_LATER", MinorVersion));
}
}
return PreprocessorDefines;
}
/// <summary>
/// Fills a list with all the module names in this assembly (or its parent)
/// </summary>
/// <param name="ModuleNames">List to receive the module names</param>
public void GetAllModuleNames(List<string> ModuleNames)
{
if (Parent != null)
{
Parent.GetAllModuleNames(ModuleNames);
}
ModuleNames.AddRange(CompiledAssembly.GetTypes().Where(x => x.IsClass && x.IsSubclassOf(typeof(ModuleRules)) && ModuleNameToModuleFile.ContainsKey(x.Name)).Select(x => x.Name));
}
/// <summary>
/// Tries to get the filename that declared the given type
/// </summary>
/// <param name="ExistingType"></param>
/// <param name="File"></param>
/// <returns>True if the type was found, false otherwise</returns>
public bool TryGetFileNameFromType(Type ExistingType, out FileReference File)
{
if (ExistingType.Assembly == CompiledAssembly)
{
string Name = ExistingType.Name;
if (ModuleNameToModuleFile.TryGetValue(Name, out File))
{
return true;
}
string NameWithoutTarget = Name;
if (NameWithoutTarget.EndsWith("Target"))
{
NameWithoutTarget = NameWithoutTarget.Substring(0, NameWithoutTarget.Length - 6);
}
if (TargetNameToTargetFile.TryGetValue(NameWithoutTarget, out File))
{
return true;
}
}
else
{
if (Parent != null && Parent.TryGetFileNameFromType(ExistingType, out File))
{
return true;
}
}
File = null;
return false;
}
/// <summary>
/// Gets the source file containing rules for the given module
/// </summary>
/// <param name="ModuleName">The name of the module</param>
/// <returns>The filename containing rules for this module, or an empty string if not found</returns>
public FileReference GetModuleFileName(string ModuleName)
{
FileReference ModuleFile;
if (ModuleNameToModuleFile.TryGetValue(ModuleName, out ModuleFile))
{
return ModuleFile;
}
else
{
return (Parent == null) ? null : Parent.GetModuleFileName(ModuleName);
}
}
/// <summary>
/// Gets the type defining rules for the given module
/// </summary>
/// <param name="ModuleName">The name of the module</param>
/// <returns>The rules type for this module, or null if not found</returns>
public Type GetModuleRulesType(string ModuleName)
{
if (ModuleNameToModuleFile.ContainsKey(ModuleName))
{
return GetModuleRulesTypeInternal(ModuleName);
}
else
{
return (Parent == null) ? null : Parent.GetModuleRulesType(ModuleName);
}
}
/// <summary>
/// Gets the type defining rules for the given module within this assembly
/// </summary>
/// <param name="ModuleName">The name of the module</param>
/// <returns>The rules type for this module, or null if not found</returns>
private Type GetModuleRulesTypeInternal(string ModuleName)
{
// The build module must define a type named 'Rules' that derives from our 'ModuleRules' type.
Type RulesObjectType = CompiledAssembly.GetType(ModuleName);
if (RulesObjectType == null)
{
// Temporary hack to avoid System namespace collisions
// @todo projectfiles: Make rules assemblies require namespaces.
RulesObjectType = CompiledAssembly.GetType("UnrealBuildTool.Rules." + ModuleName);
}
return RulesObjectType;
}
/// <summary>
/// Gets the source file containing rules for the given target
/// </summary>
/// <param name="TargetName">The name of the target</param>
/// <returns>The filename containing rules for this target, or an empty string if not found</returns>
public FileReference GetTargetFileName(string TargetName)
{
FileReference TargetFile;
if (TargetNameToTargetFile.TryGetValue(TargetName, out TargetFile))
{
return TargetFile;
}
else
{
return (Parent == null) ? null : Parent.GetTargetFileName(TargetName);
}
}
/// <summary>
/// Creates an instance of a module rules descriptor object for the specified module name
/// </summary>
/// <param name="ModuleName">Name of the module</param>
/// <param name="Target">Information about the target associated with this module</param>
/// <param name="ReferenceChain">Chain of references leading to this module</param>
/// <returns>Compiled module rule info</returns>
public ModuleRules CreateModuleRules(string ModuleName, ReadOnlyTargetRules Target, string ReferenceChain)
{
// Currently, we expect the user's rules object type name to be the same as the module name
string ModuleTypeName = ModuleName;
// Make sure the base module file is known to us
FileReference ModuleFileName;
if (!ModuleNameToModuleFile.TryGetValue(ModuleTypeName, out ModuleFileName))
{
if (Parent == null)
{
throw new BuildException("Could not find definition for module '{0}', (referenced via {1})", ModuleTypeName, ReferenceChain);
}
else
{
return Parent.CreateModuleRules(ModuleName, Target, ReferenceChain);
}
}
// get the standard Rules object class from the assembly
Type BaseRulesObjectType = GetModuleRulesTypeInternal(ModuleTypeName);
// look around for platform/group modules that we will use instead of the basic module
Type PlatformRulesObjectType = GetModuleRulesTypeInternal(ModuleTypeName + "_" + Target.Platform.ToString());
if (PlatformRulesObjectType == null)
{
foreach (UnrealPlatformGroup Group in UEBuildPlatform.GetPlatformGroups(Target.Platform))
{
// look to see if the group has an override
Type GroupRulesObjectType = GetModuleRulesTypeInternal(ModuleName + "_" + Group.ToString());
// we expect only one platform group to be found in the extensions
if (GroupRulesObjectType != null && PlatformRulesObjectType != null)
{
throw new BuildException("Found multiple platform group overrides ({0} and {1}) for module {2} without a platform specific override. Create a platform override with the class hierarchy as needed.",
GroupRulesObjectType.Name, PlatformRulesObjectType.Name, ModuleName);
}
PlatformRulesObjectType = GroupRulesObjectType;
}
}
// Figure out the best rules object to use
Type RulesObjectType = PlatformRulesObjectType != null ? PlatformRulesObjectType : BaseRulesObjectType;
if (RulesObjectType == null)
{
throw new BuildException("Expecting to find a type to be declared in a module rules named '{0}' in {1}. This type must derive from the 'ModuleRules' type defined by Unreal Build Tool.", ModuleTypeName, CompiledAssembly.FullName);
}
// Create an instance of the module's rules object
try
{
// Create an uninitialized ModuleRules object and set some defaults.
ModuleRules RulesObject = (ModuleRules)FormatterServices.GetUninitializedObject(RulesObjectType);
// even if we created a platform-extension version of the module rules, we are pretending to be
// the base type, so that no one else needs to manage this
RulesObject.Name = ModuleName;
RulesObject.File = ModuleFileName;
RulesObject.Directory = ModuleFileName.Directory;
RulesObject.Context = ModuleFileToContext[RulesObject.File];
RulesObject.Plugin = RulesObject.Context.Plugin;
RulesObject.bTreatAsEngineModule = bContainsEngineModules;
RulesObject.bUseBackwardsCompatibleDefaults = bUseBackwardsCompatibleDefaults && Target.bUseBackwardsCompatibleDefaults;
RulesObject.bPrecompile = (RulesObject.bTreatAsEngineModule || ModuleName.Equals("UE4Game", StringComparison.OrdinalIgnoreCase)) && Target.bPrecompile;
RulesObject.bUsePrecompiled = bReadOnly;
// go up the type hierarchy (if there is a hierarchy), looking for any extra directories for the module
if (RulesObjectType != BaseRulesObjectType && RulesObjectType != typeof(ModuleRules))
{
Type SubType = RulesObjectType;
RulesObject.DirectoriesForModuleSubClasses = new Dictionary<Type, DirectoryReference>();
while (SubType != BaseRulesObjectType)
{
FileReference SubTypeFileName;
if (TryGetFileNameFromType(SubType, out SubTypeFileName))
{
RulesObject.DirectoriesForModuleSubClasses.Add(SubType, SubTypeFileName.Directory);
}
SubType = SubType.BaseType;
}
}
// Call the constructor
ConstructorInfo Constructor = RulesObjectType.GetConstructor(new Type[] { typeof(ReadOnlyTargetRules) });
if(Constructor == null)
{
throw new BuildException("No valid constructor found for {0}.", ModuleName);
}
Constructor.Invoke(RulesObject, new object[] { Target });
return RulesObject;
}
catch (Exception Ex)
{
Exception MessageEx = (Ex is TargetInvocationException && Ex.InnerException != null)? Ex.InnerException : Ex;
throw new BuildException(Ex, "Unable to instantiate module '{0}': {1}\n(referenced via {2})", ModuleName, MessageEx.ToString(), ReferenceChain);
}
}
/// <summary>
/// Construct an instance of the given target rules
/// </summary>
/// <param name="TypeName">Type name of the target rules</param>
/// <param name="TargetInfo">Target configuration information to pass to the constructor</param>
/// <param name="Arguments">Command line arguments for this target</param>
/// <returns>Instance of the corresponding TargetRules</returns>
protected TargetRules CreateTargetRulesInstance(string TypeName, TargetInfo TargetInfo, CommandLineArguments Arguments)
{
// The build module must define a type named '<TargetName>Target' that derives from our 'TargetRules' type.
Type RulesType = CompiledAssembly.GetType(TypeName);
if (RulesType == null)
{
throw new BuildException("Expecting to find a type to be declared in a target rules named '{0}'. This type must derive from the 'TargetRules' type defined by Unreal Build Tool.", TypeName);
}
// Create an instance of the module's rules object, and set some defaults before calling the constructor.
TargetRules Rules = (TargetRules)FormatterServices.GetUninitializedObject(RulesType);
Rules.bUseBackwardsCompatibleDefaults = bUseBackwardsCompatibleDefaults;
// Find the constructor
ConstructorInfo Constructor = RulesType.GetConstructor(new Type[] { typeof(TargetInfo) });
if(Constructor == null)
{
throw new BuildException("No constructor found on {0} which takes an argument of type TargetInfo.", RulesType.Name);
}
// Invoke the regular constructor
try
{
Constructor.Invoke(Rules, new object[] { TargetInfo });
}
catch (Exception Ex)
{
throw new BuildException(Ex, "Unable to instantiate instance of '{0}' object type from compiled assembly '{1}'. Unreal Build Tool creates an instance of your module's 'Rules' object in order to find out about your module's requirements. The CLR exception details may provide more information: {2}", TypeName, Path.GetFileNameWithoutExtension(CompiledAssembly.Location), Ex.ToString());
}
// Return the target file name to the caller
Rules.File = TargetNameToTargetFile[TargetInfo.Name];
// Set the default overriddes for the configured target type
Rules.SetOverridesForTargetType();
// Parse any additional command-line arguments. These override default settings specified in config files or the .target.cs files.
if(Arguments != null)
{
foreach(object ConfigurableObject in Rules.GetConfigurableObjects())
{
Arguments.ApplyTo(ConfigurableObject);
}
}
// Set the final value for the link type in the target rules
if(Rules.LinkType == TargetLinkType.Default)
{
throw new BuildException("TargetRules.LinkType should be inferred from TargetType");
}
// Set the default value for whether to use the shared build environment
if(Rules.BuildEnvironment == TargetBuildEnvironment.Unique && UnrealBuildTool.IsEngineInstalled())
{
throw new BuildException("Targets with a unique build environment cannot be built an installed engine.");
}
// Automatically include CoreUObject
if (Rules.bCompileAgainstEngine)
{
Rules.bCompileAgainstCoreUObject = true;
}
// Must have editor only data if building the editor.
if (Rules.bBuildEditor)
{
Rules.bBuildWithEditorOnlyData = true;
}
// Apply the override to force debug info to be enabled
if (Rules.bForceDebugInfo)
{
Rules.bDisableDebugInfo = false;
Rules.bOmitPCDebugInfoInDevelopment = false;
}
// Setup the malloc profiler
if (Rules.bUseMallocProfiler)
{
Rules.bOmitFramePointers = false;
Rules.GlobalDefinitions.Add("USE_MALLOC_PROFILER=1");
}
// Set a macro if we allow using generated inis
if (!Rules.bAllowGeneratedIniWhenCooked)
{
Rules.GlobalDefinitions.Add("DISABLE_GENERATED_INI_WHEN_COOKED=1");
}
if (!Rules.bAllowNonUFSIniWhenCooked)
{
Rules.GlobalDefinitions.Add("DISABLE_NONUFS_INI_WHEN_COOKED=1");
}
if (Rules.bDisableUnverifiedCertificates)
{
Rules.GlobalDefinitions.Add("DISABLE_UNVERIFIED_CERTIFICATE_LOADING=1");
}
// Allow the platform to finalize the settings
UEBuildPlatform Platform = UEBuildPlatform.GetBuildPlatform(Rules.Platform);
Platform.ValidateTarget(Rules);
// Some platforms may *require* monolithic compilation...
if (Rules.LinkType != TargetLinkType.Monolithic && UEBuildPlatform.PlatformRequiresMonolithicBuilds(Rules.Platform, Rules.Configuration))
{
throw new BuildException(String.Format("{0}: {1} does not support modular builds", Rules.Name, Rules.Platform));
}
return Rules;
}
/// <summary>
/// Creates a target rules object for the specified target name.
/// </summary>
/// <param name="TargetName">Name of the target</param>
/// <param name="Platform">Platform being compiled</param>
/// <param name="Configuration">Configuration being compiled</param>
/// <param name="Architecture">Architecture being built</param>
/// <param name="ProjectFile">Path to the project file for this target</param>
/// <param name="Arguments">Command line arguments for this target</param>
/// <returns>The build target rules for the specified target</returns>
public TargetRules CreateTargetRules(string TargetName, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string Architecture, FileReference ProjectFile, CommandLineArguments Arguments)
{
bool bFoundTargetName = TargetNameToTargetFile.ContainsKey(TargetName);
if (bFoundTargetName == false)
{
if (Parent == null)
{
// throw new BuildException("Couldn't find target rules file for target '{0}' in rules assembly '{1}'.", TargetName, RulesAssembly.FullName);
string ExceptionMessage = "Couldn't find target rules file for target '";
ExceptionMessage += TargetName;
ExceptionMessage += "' in rules assembly '";
ExceptionMessage += CompiledAssembly.FullName;
ExceptionMessage += "'." + Environment.NewLine;
ExceptionMessage += "Location: " + CompiledAssembly.Location + Environment.NewLine;
ExceptionMessage += "Target rules found:" + Environment.NewLine;
foreach (KeyValuePair<string, FileReference> entry in TargetNameToTargetFile)
{
ExceptionMessage += "\t" + entry.Key + " - " + entry.Value + Environment.NewLine;
}
throw new BuildException(ExceptionMessage);
}
else
{
return Parent.CreateTargetRules(TargetName, Platform, Configuration, Architecture, ProjectFile, Arguments);
}
}
// Currently, we expect the user's rules object type name to be the same as the module name + 'Target'
string TargetTypeName = TargetName + "Target";
// The build module must define a type named '<TargetName>Target' that derives from our 'TargetRules' type.
return CreateTargetRulesInstance(TargetTypeName, new TargetInfo(TargetName, Platform, Configuration, Architecture, ProjectFile), Arguments);
}
/// <summary>
/// Determines a target name based on the type of target we're trying to build
/// </summary>
/// <param name="Type">The type of target to look for</param>
/// <param name="Platform">The platform being built</param>
/// <param name="Configuration">The configuration being built</param>
/// <param name="Architecture">The architecture being built</param>
/// <param name="ProjectFile">Project file for the target being built</param>
/// <returns>Name of the target for the given type</returns>
public string GetTargetNameByType(TargetType Type, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string Architecture, FileReference ProjectFile)
{
// Create all the targets in this assembly
List<string> Matches = new List<string>();
foreach(KeyValuePair<string, FileReference> TargetPair in TargetNameToTargetFile)
{
TargetRules Rules = CreateTargetRulesInstance(TargetPair.Key + "Target", new TargetInfo(TargetPair.Key, Platform, Configuration, Architecture, ProjectFile), null);
if(Rules.Type == Type)
{
Matches.Add(TargetPair.Key);
}
}
// If we got a result, return it. If there were multiple results, fail.
if(Matches.Count == 0)
{
if(Parent == null)
{
throw new BuildException("Unable to find target of type '{0}' for project '{1}'", Type, ProjectFile);
}
else
{
return Parent.GetTargetNameByType(Type, Platform, Configuration, Architecture, ProjectFile);
}
}
else
{
if(Matches.Count == 1)
{
return Matches[0];
}
else
{
throw new BuildException("Found multiple targets with TargetType={0}: {1}", Type, String.Join(", ", Matches));
}
}
}
/// <summary>
/// Enumerates all the plugins that are available
/// </summary>
/// <returns></returns>
public IEnumerable<PluginInfo> EnumeratePlugins()
{
return global::UnrealBuildTool.Plugins.FilterPlugins(EnumeratePluginsInternal());
}
/// <summary>
/// Enumerates all the plugins that are available
/// </summary>
/// <returns></returns>
protected IEnumerable<PluginInfo> EnumeratePluginsInternal()
{
if (Parent == null)
{
return Plugins;
}
else
{
return Plugins.Concat(Parent.EnumeratePluginsInternal());
}
}
/// <summary>
/// Tries to find the PluginInfo associated with a given module file
/// </summary>
/// <param name="ModuleFile">The module to search for</param>
/// <param name="Plugin">The matching plugin info, or null.</param>
/// <returns>True if the module belongs to a plugin</returns>
public bool TryGetPluginForModule(FileReference ModuleFile, out PluginInfo Plugin)
{
ModuleRulesContext Context;
if (ModuleFileToContext.TryGetValue(ModuleFile, out Context))
{
Plugin = Context.Plugin;
return Plugin != null;
}
if(Parent == null)
{
Plugin = null;
return false;
}
return Parent.TryGetPluginForModule(ModuleFile, out Plugin);
}
}
}