Example #1
0
 /// <summary>
 /// Initialize the attribute with a list of platforms
 /// </summary>
 /// <param name="Platforms">Variable-length array of platform arguments</param>
 public SupportedPlatformsAttribute(params string[] Platforms)
 {
     try
     {
         this.Platforms = Array.ConvertAll(Platforms, x => UnrealTargetPlatform.Parse(x));
     }
     catch (BuildException Ex)
     {
         Tools.DotNETCommon.ExceptionUtils.AddContext(Ex, "while parsing a SupportedPlatforms attribute");
         throw;
     }
 }
        /// <summary>
        /// Construct a TargetInfo from an archive on disk
        /// </summary>
        /// <param name="Reader">Archive to read from</param>
        public TargetInfo(BinaryArchiveReader Reader)
        {
            this.Name     = Reader.ReadString();
            this.Platform = UnrealTargetPlatform.Parse(Reader.ReadString());
            string ConfigurationStr = Reader.ReadString();

            this.Architecture = Reader.ReadString();
            this.ProjectFile  = Reader.ReadFileReference();
            string[] ArgumentStrs = Reader.ReadArray(() => Reader.ReadString());

            if (!UnrealTargetConfiguration.TryParse(ConfigurationStr, out Configuration))
            {
                throw new BuildException(string.Format("The configration name {0} is not a valid configration name. Valid names are ({1})", Name,
                                                       string.Join(",", Enum.GetValues(typeof(UnrealTargetConfiguration)).Cast <UnrealTargetConfiguration>().Select(x => x.ToString()))));
            }

            Arguments = new CommandLineArguments(ArgumentStrs);
        }
Example #3
0
        /// <summary>
        /// Read a receipt from disk.
        /// </summary>
        /// <param name="Location">Filename to read from</param>
        /// <param name="EngineDir">Engine directory for expanded variables</param>
        public static TargetReceipt Read(FileReference Location, DirectoryReference EngineDir)
        {
            JsonObject RawObject = JsonObject.Read(Location);

            // Read the initial fields
            string                    TargetName    = RawObject.GetStringField("TargetName");
            TargetType                TargetType    = RawObject.GetEnumField <TargetType>("TargetType");
            UnrealTargetPlatform      Platform      = UnrealTargetPlatform.Parse(RawObject.GetStringField("Platform"));
            UnrealTargetConfiguration Configuration = RawObject.GetEnumField <UnrealTargetConfiguration>("Configuration");

            // Try to read the build version
            BuildVersion Version;

            if (!BuildVersion.TryParse(RawObject.GetObjectField("Version"), out Version))
            {
                throw new JsonParseException("Invalid 'Version' field");
            }

            // Read the project path
            FileReference ProjectFile;

            string RelativeProjectFile;

            if (RawObject.TryGetStringField("Project", out RelativeProjectFile))
            {
                ProjectFile = FileReference.Combine(Location.Directory, RelativeProjectFile);
            }
            else
            {
                ProjectFile = null;
            }

            // Create the receipt
            TargetReceipt Receipt = new TargetReceipt(ProjectFile, TargetName, TargetType, Platform, Configuration, Version);

            // Get the project directory
            DirectoryReference ProjectDir = Receipt.ProjectDir;

            // Read the launch executable
            string Launch;

            if (RawObject.TryGetStringField("Launch", out Launch))
            {
                Receipt.Launch = ExpandPathVariables(Launch, EngineDir, ProjectDir);
            }

            // Read the build products
            JsonObject[] BuildProductObjects;
            if (RawObject.TryGetObjectArrayField("BuildProducts", out BuildProductObjects))
            {
                foreach (JsonObject BuildProductObject in BuildProductObjects)
                {
                    string           Path;
                    BuildProductType Type;
                    if (BuildProductObject.TryGetStringField("Path", out Path) && BuildProductObject.TryGetEnumField("Type", out Type))
                    {
                        FileReference File = ExpandPathVariables(Path, EngineDir, ProjectDir);

                        string Module;
                        BuildProductObject.TryGetStringField("Module", out Module);

                        Receipt.AddBuildProduct(File, Type);
                    }
                }
            }

            // Read the runtime dependencies
            JsonObject[] RuntimeDependencyObjects;
            if (RawObject.TryGetObjectArrayField("RuntimeDependencies", out RuntimeDependencyObjects))
            {
                foreach (JsonObject RuntimeDependencyObject in RuntimeDependencyObjects)
                {
                    string Path;
                    if (RuntimeDependencyObject.TryGetStringField("Path", out Path))
                    {
                        FileReference File = ExpandPathVariables(Path, EngineDir, ProjectDir);

                        StagedFileType Type;
                        if (!RuntimeDependencyObject.TryGetEnumField("Type", out Type))
                        {
                            // Previous format included an optional IgnoreIfMissing flag, which was only used for debug files. We can explicitly reference them as DebugNonUFS files now.
                            bool bIgnoreIfMissing;
                            if (RuntimeDependencyObject.TryGetBoolField("IgnoreIfMissing", out bIgnoreIfMissing))
                            {
                                bIgnoreIfMissing = false;
                            }
                            Type = bIgnoreIfMissing? StagedFileType.DebugNonUFS : StagedFileType.NonUFS;
                        }

                        Receipt.RuntimeDependencies.Add(File, Type);
                    }
                }
            }

            // Read the additional properties
            JsonObject[] AdditionalPropertyObjects;
            if (RawObject.TryGetObjectArrayField("AdditionalProperties", out AdditionalPropertyObjects))
            {
                foreach (JsonObject AdditionalPropertyObject in AdditionalPropertyObjects)
                {
                    string Name;
                    if (AdditionalPropertyObject.TryGetStringField("Name", out Name))
                    {
                        string Value;
                        if (AdditionalPropertyObject.TryGetStringField("Value", out Value))
                        {
                            Receipt.AdditionalProperties.Add(new ReceiptProperty(Name, Value));
                        }
                    }
                }
            }

            return(Receipt);
        }
        /// <summary>
        /// Reads a plugin descriptor from a json object
        /// </summary>
        /// <param name="RawObject">The object to read from</param>
        /// <returns>New plugin descriptor</returns>
        public PluginDescriptor(JsonObject RawObject)
        {
            // Read the version
            if (!RawObject.TryGetIntegerField("FileVersion", out FileVersion))
            {
                if (!RawObject.TryGetIntegerField("PluginFileVersion", out FileVersion))
                {
                    throw new BuildException("Plugin descriptor does not contain a valid FileVersion entry");
                }
            }

            // Check it's not newer than the latest version we can parse
            if (FileVersion > (int)PluginDescriptorVersion.Latest)
            {
                throw new BuildException("Plugin descriptor appears to be in a newer version ({0}) of the file format that we can load (max version: {1}).", FileVersion, (int)PluginDescriptorVersion.Latest);
            }

            // Read the other fields
            RawObject.TryGetIntegerField("Version", out Version);
            RawObject.TryGetStringField("VersionName", out VersionName);
            RawObject.TryGetStringField("FriendlyName", out FriendlyName);
            RawObject.TryGetStringField("Description", out Description);

            if (!RawObject.TryGetStringField("Category", out Category))
            {
                // Category used to be called CategoryPath in .uplugin files
                RawObject.TryGetStringField("CategoryPath", out Category);
            }

            // Due to a difference in command line parsing between Windows and Mac, we shipped a few Mac samples containing
            // a category name with escaped quotes. Remove them here to make sure we can list them in the right category.
            if (Category != null && Category.Length >= 2 && Category.StartsWith("\"") && Category.EndsWith("\""))
            {
                Category = Category.Substring(1, Category.Length - 2);
            }

            RawObject.TryGetStringField("CreatedBy", out CreatedBy);
            RawObject.TryGetStringField("CreatedByURL", out CreatedByURL);
            RawObject.TryGetStringField("DocsURL", out DocsURL);
            RawObject.TryGetStringField("MarketplaceURL", out MarketplaceURL);
            RawObject.TryGetStringField("SupportURL", out SupportURL);
            RawObject.TryGetStringField("EngineVersion", out EngineVersion);
            RawObject.TryGetStringArrayField("SupportedPrograms", out SupportedPrograms);
            RawObject.TryGetBoolField("bIsPluginExtension", out bIsPluginExtension);

            try
            {
                string[] SupportedTargetPlatformNames;
                if (RawObject.TryGetStringArrayField("SupportedTargetPlatforms", out SupportedTargetPlatformNames))
                {
                    SupportedTargetPlatforms = Array.ConvertAll(SupportedTargetPlatformNames, x => UnrealTargetPlatform.Parse(x));
                }
            }
            catch (BuildException Ex)
            {
                ExceptionUtils.AddContext(Ex, "while parsing SupportedTargetPlatforms in plugin with FriendlyName '{0}'", FriendlyName);
                throw;
            }


            JsonObject[] ModulesArray;
            if (RawObject.TryGetObjectArrayField("Modules", out ModulesArray))
            {
                Modules = Array.ConvertAll(ModulesArray, x => ModuleDescriptor.FromJsonObject(x));
            }

            JsonObject[] LocalizationTargetsArray;
            if (RawObject.TryGetObjectArrayField("LocalizationTargets", out LocalizationTargetsArray))
            {
                LocalizationTargets = Array.ConvertAll(LocalizationTargetsArray, x => LocalizationTargetDescriptor.FromJsonObject(x));
            }

            bool bEnabledByDefaultValue;

            if (RawObject.TryGetBoolField("EnabledByDefault", out bEnabledByDefaultValue))
            {
                bEnabledByDefault = bEnabledByDefaultValue;
            }

            RawObject.TryGetBoolField("CanContainContent", out bCanContainContent);
            RawObject.TryGetBoolField("IsBetaVersion", out bIsBetaVersion);
            RawObject.TryGetBoolField("IsExperimentalVersion", out bIsExperimentalVersion);
            RawObject.TryGetBoolField("Installed", out bInstalled);

            bool bCanBeUsedWithUnrealHeaderTool;

            if (RawObject.TryGetBoolField("CanBeUsedWithUnrealHeaderTool", out bCanBeUsedWithUnrealHeaderTool) && bCanBeUsedWithUnrealHeaderTool)
            {
                Array.Resize(ref SupportedPrograms, (SupportedPrograms == null)? 1 : SupportedPrograms.Length + 1);
                SupportedPrograms[SupportedPrograms.Length - 1] = "UnrealHeaderTool";
            }

            RawObject.TryGetBoolField("RequiresBuildPlatform", out bRequiresBuildPlatform);

            CustomBuildSteps.TryRead(RawObject, "PreBuildSteps", out PreBuildSteps);
            CustomBuildSteps.TryRead(RawObject, "PostBuildSteps", out PostBuildSteps);

            JsonObject[] PluginsArray;
            if (RawObject.TryGetObjectArrayField("Plugins", out PluginsArray))
            {
                Plugins = Array.ConvertAll(PluginsArray, x => PluginReferenceDescriptor.FromJsonObject(x));
            }
        }
        /// <summary>
        /// Parse a list of target descriptors from the command line
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <param name="bUsePrecompiled">Whether to use a precompiled engine distribution</param>
        /// <param name="bSkipRulesCompile">Whether to skip compiling rules assemblies</param>
        /// <param name="TargetDescriptors">List of target descriptors</param>
        public static void ParseSingleCommandLine(CommandLineArguments Arguments, bool bUsePrecompiled, bool bSkipRulesCompile, List <TargetDescriptor> TargetDescriptors)
        {
            List <UnrealTargetPlatform>      Platforms      = new List <UnrealTargetPlatform>();
            List <UnrealTargetConfiguration> Configurations = new List <UnrealTargetConfiguration>();
            List <string> TargetNames = new List <string>();
            FileReference ProjectFile = Arguments.GetFileReferenceOrDefault("-Project=", null);

            // Settings for creating/using static libraries for the engine
            for (int ArgumentIndex = 0; ArgumentIndex < Arguments.Count; ArgumentIndex++)
            {
                string Argument = Arguments[ArgumentIndex];
                if (Argument.Length > 0 && Argument[0] != '-')
                {
                    // Mark this argument as used. We'll interpret it as one thing or another.
                    Arguments.MarkAsUsed(ArgumentIndex);

                    // Check if it's a project file argument
                    if (Argument.EndsWith(".uproject", StringComparison.OrdinalIgnoreCase))
                    {
                        FileReference NewProjectFile = new FileReference(Argument);
                        if (ProjectFile != null && ProjectFile != NewProjectFile)
                        {
                            throw new BuildException("Multiple project files specified on command line (first {0}, then {1})", ProjectFile, NewProjectFile);
                        }
                        ProjectFile = new FileReference(Argument);
                        continue;
                    }

                    // Split it into separate arguments
                    string[] InlineArguments = Argument.Split('+');

                    // Try to parse them as platforms
                    UnrealTargetPlatform ParsedPlatform;
                    if (UnrealTargetPlatform.TryParse(InlineArguments[0], out ParsedPlatform))
                    {
                        Platforms.Add(ParsedPlatform);
                        for (int InlineArgumentIdx = 1; InlineArgumentIdx < InlineArguments.Length; InlineArgumentIdx++)
                        {
                            Platforms.Add(UnrealTargetPlatform.Parse(InlineArguments[InlineArgumentIdx]));
                        }
                        continue;
                    }

                    // Try to parse them as configurations
                    UnrealTargetConfiguration ParsedConfiguration;
                    if (Enum.TryParse(InlineArguments[0], true, out ParsedConfiguration))
                    {
                        Configurations.Add(ParsedConfiguration);
                        for (int InlineArgumentIdx = 1; InlineArgumentIdx < InlineArguments.Length; InlineArgumentIdx++)
                        {
                            string InlineArgument = InlineArguments[InlineArgumentIdx];
                            if (!Enum.TryParse(InlineArgument, true, out ParsedConfiguration))
                            {
                                throw new BuildException("Invalid configuration '{0}'", InlineArgument);
                            }
                            Configurations.Add(ParsedConfiguration);
                        }
                        continue;
                    }

                    // Otherwise assume they are target names
                    TargetNames.AddRange(InlineArguments);
                }
            }

            if (Platforms.Count == 0)
            {
                throw new BuildException("No platforms specified for target");
            }
            if (Configurations.Count == 0)
            {
                throw new BuildException("No configurations specified for target");
            }

            // Make sure the project file exists, and make sure we're using the correct case.
            if (ProjectFile != null)
            {
                FileInfo ProjectFileInfo = FileUtils.FindCorrectCase(ProjectFile.ToFileInfo());
                if (!ProjectFileInfo.Exists)
                {
                    throw new BuildException("Unable to find project '{0}'.", ProjectFile);
                }
                ProjectFile = new FileReference(ProjectFileInfo);
            }

            // Expand all the platforms, architectures and configurations
            foreach (UnrealTargetPlatform Platform in Platforms)
            {
                // Make sure the platform is valid
                if (!InstalledPlatformInfo.IsValid(null, Platform, null, EProjectType.Code, InstalledPlatformState.Downloaded))
                {
                    if (!InstalledPlatformInfo.IsValid(null, Platform, null, EProjectType.Code, InstalledPlatformState.Supported))
                    {
                        throw new BuildException("The {0} platform is not supported from this engine distribution.", Platform);
                    }
                    else
                    {
                        throw new BuildException("Missing files required to build {0} targets. Enable {0} as an optional download component in the Epic Games Launcher.", Platform);
                    }
                }

                // Parse the architecture parameter, or get the default for the platform
                List <string> Architectures = new List <string>(Arguments.GetValues("-Architecture=", '+'));
                if (Architectures.Count == 0)
                {
                    Architectures.Add(UEBuildPlatform.GetBuildPlatform(Platform).GetDefaultArchitecture(ProjectFile));
                }

                foreach (string Architecture in Architectures)
                {
                    foreach (UnrealTargetConfiguration Configuration in Configurations)
                    {
                        // Create all the target descriptors for targets specified by type
                        foreach (string TargetTypeString in Arguments.GetValues("-TargetType="))
                        {
                            TargetType TargetType;
                            if (!Enum.TryParse(TargetTypeString, out TargetType))
                            {
                                throw new BuildException("Invalid target type '{0}'", TargetTypeString);
                            }

                            if (ProjectFile == null)
                            {
                                throw new BuildException("-TargetType=... requires a project file to be specified");
                            }
                            else
                            {
                                TargetNames.Add(RulesCompiler.CreateProjectRulesAssembly(ProjectFile, bUsePrecompiled, bSkipRulesCompile).GetTargetNameByType(TargetType, Platform, Configuration, Architecture, ProjectFile));
                            }
                        }

                        // Make sure we could parse something
                        if (TargetNames.Count == 0)
                        {
                            throw new BuildException("No target name was specified on the command-line.");
                        }

                        // Create all the target descriptors
                        foreach (string TargetName in TargetNames)
                        {
                            // If a project file was not specified see if we can find one
                            if (ProjectFile == null && NativeProjects.TryGetProjectForTarget(TargetName, out ProjectFile))
                            {
                                Log.TraceVerbose("Found project file for {0} - {1}", TargetName, ProjectFile);
                            }

                            // Create the target descriptor
                            TargetDescriptors.Add(new TargetDescriptor(ProjectFile, TargetName, Platform, Configuration, Architecture, Arguments));
                        }
                    }
                }
            }
        }
Example #6
0
        private static void ParsePlatformConfiguration(string PlatformConfiguration)
        {
            // Trim whitespace at the beginning.
            PlatformConfiguration = PlatformConfiguration.Trim();
            // Remove brackets.
            PlatformConfiguration = PlatformConfiguration.TrimStart('(');
            PlatformConfiguration = PlatformConfiguration.TrimEnd(')');

            bool bCanCreateEntry = true;

            string ConfigurationName;
            UnrealTargetConfiguration Configuration = UnrealTargetConfiguration.Unknown;

            if (ParseSubValue(PlatformConfiguration, "Configuration=", out ConfigurationName))
            {
                Enum.TryParse(ConfigurationName, out Configuration);
            }
            if (Configuration == UnrealTargetConfiguration.Unknown)
            {
                Log.TraceWarning("Unable to read configuration from {0}", PlatformConfiguration);
                bCanCreateEntry = false;
            }

            string PlatformName;

            if (ParseSubValue(PlatformConfiguration, "PlatformName=", out PlatformName))
            {
                if (!UnrealTargetPlatform.IsValidName(PlatformName))
                {
                    Log.TraceWarning("Unable to read platform from {0}", PlatformConfiguration);
                    bCanCreateEntry = false;
                }
            }

            string     PlatformTypeName;
            TargetType PlatformType = TargetType.Game;

            if (ParseSubValue(PlatformConfiguration, "PlatformType=", out PlatformTypeName))
            {
                if (!Enum.TryParse(PlatformTypeName, out PlatformType))
                {
                    Log.TraceWarning("Unable to read Platform Type from {0}, defaulting to Game", PlatformConfiguration);
                    PlatformType = TargetType.Game;
                }
            }
            if (PlatformType == TargetType.Program)
            {
                Log.TraceWarning("Program is not a valid PlatformType for an Installed Platform, defaulting to Game");
                PlatformType = TargetType.Game;
            }

            string Architecture;

            ParseSubValue(PlatformConfiguration, "Architecture=", out Architecture);

            string RequiredFile;

            if (ParseSubValue(PlatformConfiguration, "RequiredFile=", out RequiredFile))
            {
                RequiredFile = FileReference.Combine(UnrealBuildTool.RootDirectory, RequiredFile).ToString();
            }

            string       ProjectTypeName;
            EProjectType ProjectType = EProjectType.Any;

            if (ParseSubValue(PlatformConfiguration, "ProjectType=", out ProjectTypeName))
            {
                Enum.TryParse(ProjectTypeName, out ProjectType);
            }
            if (ProjectType == EProjectType.Unknown)
            {
                Log.TraceWarning("Unable to read project type from {0}", PlatformConfiguration);
                bCanCreateEntry = false;
            }

            string CanBeDisplayedString;
            bool   bCanBeDisplayed = false;

            if (ParseSubValue(PlatformConfiguration, "bCanBeDisplayed=", out CanBeDisplayedString))
            {
                bCanBeDisplayed = Convert.ToBoolean(CanBeDisplayedString);
            }

            if (bCanCreateEntry)
            {
                InstalledPlatformConfigurations.Add(new InstalledPlatformConfiguration(Configuration, UnrealTargetPlatform.Parse(PlatformName), PlatformType, Architecture, RequiredFile, ProjectType, bCanBeDisplayed));
            }
        }
Example #7
0
        /// <summary>
        /// Construct a PluginReferenceDescriptor from a Json object
        /// </summary>
        /// <param name="RawObject">The Json object containing a plugin reference descriptor</param>
        /// <returns>New PluginReferenceDescriptor object</returns>
        public static PluginReferenceDescriptor FromJsonObject(JsonObject RawObject)
        {
            string[] WhitelistPlatformNames       = null;
            string[] BlacklistPlatformNames       = null;
            string[] SupportedTargetPlatformNames = null;

            PluginReferenceDescriptor Descriptor = new PluginReferenceDescriptor(RawObject.GetStringField("Name"), null, RawObject.GetBoolField("Enabled"));

            RawObject.TryGetBoolField("Optional", out Descriptor.bOptional);
            RawObject.TryGetStringField("Description", out Descriptor.Description);
            RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL);

            // Only parse platform information if enabled
            if (Descriptor.bEnabled)
            {
                RawObject.TryGetStringArrayField("WhitelistPlatforms", out WhitelistPlatformNames);
                RawObject.TryGetStringArrayField("BlacklistPlatforms", out BlacklistPlatformNames);
                RawObject.TryGetEnumArrayField <UnrealTargetConfiguration>("WhitelistTargetConfigurations", out Descriptor.WhitelistTargetConfigurations);
                RawObject.TryGetEnumArrayField <UnrealTargetConfiguration>("BlacklistTargetConfigurations", out Descriptor.BlacklistTargetConfigurations);
                RawObject.TryGetEnumArrayField <TargetType>("WhitelistTargets", out Descriptor.WhitelistTargets);
                RawObject.TryGetEnumArrayField <TargetType>("BlacklistTargets", out Descriptor.BlacklistTargets);
                RawObject.TryGetStringArrayField("SupportedTargetPlatforms", out SupportedTargetPlatformNames);
            }

            try
            {
                // convert string array to UnrealTargetPlatform arrays
                if (WhitelistPlatformNames != null)
                {
                    Descriptor.WhitelistPlatforms = WhitelistPlatformNames.Select(x => UnrealTargetPlatform.Parse(x)).ToList();
                }
                if (BlacklistPlatformNames != null)
                {
                    Descriptor.BlacklistPlatforms = BlacklistPlatformNames.Select(x => UnrealTargetPlatform.Parse(x)).ToList();
                }
                if (SupportedTargetPlatformNames != null)
                {
                    Descriptor.SupportedTargetPlatforms = SupportedTargetPlatformNames.Select(x => UnrealTargetPlatform.Parse(x)).ToList();
                }
            }
            catch (BuildException Ex)
            {
                ExceptionUtils.AddContext(Ex, "while parsing PluginReferenceDescriptor {0}", Descriptor.Name);
                throw;
            }


            return(Descriptor);
        }
Example #8
0
        /// <summary>
        ///  Attempt to merge a child plugin up into a parent plugin (via file naming scheme). Very little merging happens
        ///  but it does allow for platform extensions to extend a plugin with module files
        /// </summary>
        /// <param name="Child">Child plugin that needs to merge to a main, parent plugin</param>
        /// <param name="Filename">Child plugin's filename, used to determine the parent's name</param>
        private static void TryMergeWithParent(PluginInfo Child, FileReference Filename)
        {
            // find the parent
            PluginInfo Parent = null;

            string[] Tokens = Filename.GetFileNameWithoutAnyExtensions().Split("_".ToCharArray());
            if (Tokens.Length == 2)
            {
                string ParentPluginName = Tokens[0];
                foreach (KeyValuePair <DirectoryReference, List <PluginInfo> > Pair in PluginInfoCache)
                {
                    Parent = Pair.Value.FirstOrDefault(x => x.Name.Equals(ParentPluginName, StringComparison.InvariantCultureIgnoreCase));
                    if (Parent != null)
                    {
                        break;
                    }
                }
            }

            // did we find a parent plugin?
            if (Parent == null)
            {
                throw new BuildException("Child plugin {0} was not named properly. It should be in the form <ParentPlugin>_<Platform>.uplugin", Filename);
            }

            // validate child plugin file name
            string PlatformName = Tokens[1];

            if (!IsValidChildPluginSuffix(PlatformName))
            {
                Log.TraceWarning("Ignoring child plugin: {0} - Unknown suffix \"{1}\". Expected valid platform or group", Child.File.GetFileName(), PlatformName);
                return;
            }

            // add our uplugin file to the existing plugin to be used to search for modules later
            Parent.ChildFiles.Add(Child.File);

            // merge the supported platforms
            Parent.Descriptor.MergeSupportedTargetPlatforms(Child.Descriptor.SupportedTargetPlatforms);

            // make sure we are whitelisted for any modules we list, if the parent had a whitelist
            if (Child.Descriptor.Modules != null)
            {
                // this should cause an error if it's invalid platform name
                UnrealTargetPlatform Platform = UnrealTargetPlatform.Parse(PlatformName);

                foreach (ModuleDescriptor ChildModule in Child.Descriptor.Modules)
                {
                    ModuleDescriptor ParentModule = Parent.Descriptor.Modules.FirstOrDefault(x => x.Name.Equals(ChildModule.Name) && x.Type == ChildModule.Type);
                    if (ParentModule != null)
                    {
                        // merge white/blacklists (if the parent had a list, and child didn't specify a list, just add the child platform to the parent list - for white and black!)
                        if (ParentModule.WhitelistPlatforms != null && ParentModule.WhitelistPlatforms.Length > 0)
                        {
                            List <UnrealTargetPlatform> Whitelist = ParentModule.WhitelistPlatforms.ToList();
                            if (ChildModule.WhitelistPlatforms != null && ChildModule.WhitelistPlatforms.Length > 0)
                            {
                                Whitelist.AddRange(ChildModule.WhitelistPlatforms);
                            }
                            else
                            {
                                Whitelist.Add(Platform);
                            }
                            ParentModule.WhitelistPlatforms = Whitelist.ToArray();
                        }
                        if (ParentModule.BlacklistPlatforms != null && ParentModule.BlacklistPlatforms.Length > 0)
                        {
                            if (ChildModule.BlacklistPlatforms != null && ChildModule.BlacklistPlatforms.Length > 0)
                            {
                                List <UnrealTargetPlatform> Blacklist = ParentModule.BlacklistPlatforms.ToList();
                                Blacklist.AddRange(ChildModule.BlacklistPlatforms);
                                ParentModule.BlacklistPlatforms = Blacklist.ToArray();
                            }
                        }
                    }
                }
            }
            // @todo platplug: what else do we want to support merging?!?
        }
        /// <summary>
        /// Constructs a ModuleDescriptor from a Json object
        /// </summary>
        /// <param name="InObject"></param>
        /// <returns>The new module descriptor</returns>
        public static ModuleDescriptor FromJsonObject(JsonObject InObject)
        {
            ModuleDescriptor Module = new ModuleDescriptor(InObject.GetStringField("Name"), InObject.GetEnumField <ModuleHostType>("Type"));

            ModuleLoadingPhase LoadingPhase;

            if (InObject.TryGetEnumField <ModuleLoadingPhase>("LoadingPhase", out LoadingPhase))
            {
                Module.LoadingPhase = LoadingPhase;
            }

            try
            {
                string[] WhitelistPlatforms;
                if (InObject.TryGetStringArrayField("WhitelistPlatforms", out WhitelistPlatforms))
                {
                    Module.WhitelistPlatforms = Array.ConvertAll(WhitelistPlatforms, x => UnrealTargetPlatform.Parse(x));
                }

                string[] BlacklistPlatforms;
                if (InObject.TryGetStringArrayField("BlacklistPlatforms", out BlacklistPlatforms))
                {
                    Module.BlacklistPlatforms = Array.ConvertAll(BlacklistPlatforms, x => UnrealTargetPlatform.Parse(x));
                }
            }
            catch (BuildException Ex)
            {
                ExceptionUtils.AddContext(Ex, "while parsing module descriptor '{0}'", Module.Name);
                throw;
            }

            TargetType[] WhitelistTargets;
            if (InObject.TryGetEnumArrayField <TargetType>("WhitelistTargets", out WhitelistTargets))
            {
                Module.WhitelistTargets = WhitelistTargets;
            }

            TargetType[] BlacklistTargets;
            if (InObject.TryGetEnumArrayField <TargetType>("BlacklistTargets", out BlacklistTargets))
            {
                Module.BlacklistTargets = BlacklistTargets;
            }

            UnrealTargetConfiguration[] WhitelistTargetConfigurations;
            if (InObject.TryGetEnumArrayField <UnrealTargetConfiguration>("WhitelistTargetConfigurations", out WhitelistTargetConfigurations))
            {
                Module.WhitelistTargetConfigurations = WhitelistTargetConfigurations;
            }

            UnrealTargetConfiguration[] BlacklistTargetConfigurations;
            if (InObject.TryGetEnumArrayField <UnrealTargetConfiguration>("BlacklistTargetConfigurations", out BlacklistTargetConfigurations))
            {
                Module.BlacklistTargetConfigurations = BlacklistTargetConfigurations;
            }

            string[] WhitelistPrograms;
            if (InObject.TryGetStringArrayField("WhitelistPrograms", out WhitelistPrograms))
            {
                Module.WhitelistPrograms = WhitelistPrograms;
            }

            string[] BlacklistPrograms;
            if (InObject.TryGetStringArrayField("BlacklistPrograms", out BlacklistPrograms))
            {
                Module.BlacklistPrograms = BlacklistPrograms;
            }

            string[] AdditionalDependencies;
            if (InObject.TryGetStringArrayField("AdditionalDependencies", out AdditionalDependencies))
            {
                Module.AdditionalDependencies = AdditionalDependencies;
            }

            return(Module);
        }