/// <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;
			}

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

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

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

			return Module;
		}
Exemple #2
0
		public static UEBuildModuleType GetModuleTypeFromDescriptor(ModuleDescriptor Module)
		{
			switch (Module.Type)
			{
				case ModuleHostType.Developer:
					return UEBuildModuleType.Developer;
				case ModuleHostType.Editor:
				case ModuleHostType.EditorNoCommandlet:
					return UEBuildModuleType.Editor;
				case ModuleHostType.Program:
					return UEBuildModuleType.Program;
				case ModuleHostType.Runtime:
				case ModuleHostType.RuntimeNoCommandlet:
					return UEBuildModuleType.Runtime;
				default:
					throw new BuildException("Unhandled module type {0}", Module.Type.ToString());
			}
		}
Exemple #3
0
        /// <summary>
        /// Saves the descriptor to disk
        /// </summary>
        /// <param name="FileName">The filename to write to</param>
        public void Save(string FileName)
        {
            using (JsonWriter Writer = new JsonWriter(FileName))
            {
                Writer.WriteObjectStart();

                Writer.WriteValue("FileVersion", (int)ProjectDescriptorVersion.Latest);
                Writer.WriteValue("Version", Version);
                Writer.WriteValue("VersionName", VersionName);
                Writer.WriteValue("FriendlyName", FriendlyName);
                Writer.WriteValue("Description", Description);
                Writer.WriteValue("Category", Category);
                Writer.WriteValue("CreatedBy", CreatedBy);
                Writer.WriteValue("CreatedByURL", CreatedByURL);
                Writer.WriteValue("DocsURL", DocsURL);
                Writer.WriteValue("MarketplaceURL", MarketplaceURL);
                Writer.WriteValue("SupportURL", SupportURL);
                Writer.WriteValue("EnabledByDefault", bEnabledByDefault);
                Writer.WriteValue("CanContainContent", bCanContainContent);
                Writer.WriteValue("IsBetaVersion", bIsBetaVersion);
                Writer.WriteValue("Installed", bInstalled);
                Writer.WriteValue("RequiresBuildPlatform", bRequiresBuildPlatform);

                ModuleDescriptor.WriteArray(Writer, "Modules", Modules);

                if (PreBuildSteps != null)
                {
                    PreBuildSteps.Write(Writer, "PreBuildSteps");
                }

                if (PostBuildSteps != null)
                {
                    PostBuildSteps.Write(Writer, "PostBuildSteps");
                }

                Writer.WriteObjectEnd();
            }
        }
		/// <summary>
		/// Write an array of module descriptors
		/// </summary>
		/// <param name="Writer">The Json writer to output to</param>
		/// <param name="Name">Name of the array</param>
		/// <param name="Modules">Array of modules</param>
		public static void WriteArray(JsonWriter Writer, string Name, ModuleDescriptor[] Modules)
		{
			if (Modules.Length > 0)
			{
				Writer.WriteArrayStart(Name);
				foreach (ModuleDescriptor Module in Modules)
				{
					Module.Write(Writer);
				}
				Writer.WriteArrayEnd();
			}
		}
Exemple #5
0
        /// <summary>
        /// Writes the plugin descriptor to an existing Json writer
        /// </summary>
        /// <param name="Writer">The writer to receive plugin data</param>
        public void Write(JsonWriter Writer)
        {
            Writer.WriteValue("FileVersion", (int)ProjectDescriptorVersion.Latest);
            Writer.WriteValue("Version", Version);
            Writer.WriteValue("VersionName", VersionName);
            Writer.WriteValue("FriendlyName", FriendlyName);
            Writer.WriteValue("Description", Description);
            Writer.WriteValue("Category", Category);
            Writer.WriteValue("CreatedBy", CreatedBy);
            Writer.WriteValue("CreatedByURL", CreatedByURL);
            Writer.WriteValue("DocsURL", DocsURL);
            Writer.WriteValue("MarketplaceURL", MarketplaceURL);
            Writer.WriteValue("SupportURL", SupportURL);
            if (!String.IsNullOrEmpty(EngineVersion))
            {
                Writer.WriteValue("EngineVersion", EngineVersion);
            }
            if (bEnabledByDefault.HasValue)
            {
                Writer.WriteValue("EnabledByDefault", bEnabledByDefault.Value);
            }
            Writer.WriteValue("CanContainContent", bCanContainContent);
            if (bIsBetaVersion)
            {
                Writer.WriteValue("IsBetaVersion", bIsBetaVersion);
            }
            if (bInstalled)
            {
                Writer.WriteValue("Installed", bInstalled);
            }

            if (bRequiresBuildPlatform)
            {
                Writer.WriteValue("RequiresBuildPlatform", bRequiresBuildPlatform);
            }

            if (SupportedTargetPlatforms != null && SupportedTargetPlatforms.Length > 0)
            {
                Writer.WriteEnumArrayField <UnrealTargetPlatform>("SupportedTargetPlatforms", SupportedTargetPlatforms);
            }

            if (SupportedPrograms != null && SupportedPrograms.Length > 0)
            {
                Writer.WriteStringArrayField("SupportedPrograms", SupportedPrograms);
            }

            ModuleDescriptor.WriteArray(Writer, "Modules", Modules);

            LocalizationTargetDescriptor.WriteArray(Writer, "LocalizationTargets", LocalizationTargets);

            if (PreBuildSteps != null)
            {
                PreBuildSteps.Write(Writer, "PreBuildSteps");
            }

            if (PostBuildSteps != null)
            {
                PostBuildSteps.Write(Writer, "PostBuildSteps");
            }

            PluginReferenceDescriptor.WriteArray(Writer, "Plugins", Plugins);
        }
Exemple #6
0
        /// <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.TryGetEnumArrayField <UnrealTargetPlatform>("SupportedTargetPlatforms", out SupportedTargetPlatforms);
            RawObject.TryGetStringArrayField("SupportedPrograms", out SupportedPrograms);

            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("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));
            }
        }
Exemple #7
0
        /// <summary>
        /// Creates a plugin descriptor from a file on disk
        /// </summary>
        /// <param name="FileName">The filename to read</param>
        /// <param name="bPluginTypeEnabledByDefault">Whether this plugin should be enabled by default based on its location</param>
        /// <returns>New plugin descriptor</returns>
        public static PluginDescriptor FromFile(FileReference FileName, bool bPluginTypeEnabledByDefault)
        {
            JsonObject RawObject = JsonObject.Read(FileName.FullName);

            try
            {
                PluginDescriptor Descriptor = new PluginDescriptor();

                // Read the version
                if (!RawObject.TryGetIntegerField("FileVersion", out Descriptor.FileVersion))
                {
                    if (!RawObject.TryGetIntegerField("PluginFileVersion", out Descriptor.FileVersion))
                    {
                        throw new BuildException("Plugin descriptor file '{0}' does not contain a valid FileVersion entry", FileName);
                    }
                }

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

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

                if (!RawObject.TryGetStringField("Category", out Descriptor.Category))
                {
                    // Category used to be called CategoryPath in .uplugin files
                    RawObject.TryGetStringField("CategoryPath", out Descriptor.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 (Descriptor.Category != null && Descriptor.Category.Length >= 2 && Descriptor.Category.StartsWith("\"") && Descriptor.Category.EndsWith("\""))
                {
                    Descriptor.Category = Descriptor.Category.Substring(1, Descriptor.Category.Length - 2);
                }

                RawObject.TryGetStringField("CreatedBy", out Descriptor.CreatedBy);
                RawObject.TryGetStringField("CreatedByURL", out Descriptor.CreatedByURL);
                RawObject.TryGetStringField("DocsURL", out Descriptor.DocsURL);
                RawObject.TryGetStringField("MarketplaceURL", out Descriptor.MarketplaceURL);
                RawObject.TryGetStringField("SupportURL", out Descriptor.SupportURL);
                RawObject.TryGetIntegerField("CompatibleChangelist", out Descriptor.CompatibleChangelist);

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

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

                if (!RawObject.TryGetBoolField("EnabledByDefault", out Descriptor.bEnabledByDefault))
                {
                    Descriptor.bEnabledByDefault = bPluginTypeEnabledByDefault;
                }

                RawObject.TryGetBoolField("CanContainContent", out Descriptor.bCanContainContent);
                RawObject.TryGetBoolField("IsBetaVersion", out Descriptor.bIsBetaVersion);
                RawObject.TryGetBoolField("IsMod", out Descriptor.bIsMod);
                RawObject.TryGetBoolField("Installed", out Descriptor.bInstalled);
                RawObject.TryGetBoolField("CanBeUsedWithUnrealHeaderTool", out Descriptor.bCanBeUsedWithUnrealHeaderTool);
                RawObject.TryGetBoolField("RequiresBuildPlatform", out Descriptor.bRequiresBuildPlatform);

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

                return(Descriptor);
            }
            catch (JsonParseException ParseException)
            {
                throw new JsonParseException("{0} (in {1})", ParseException.Message, FileName);
            }
        }
        /// <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;
            }

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

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

            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);
        }
Exemple #9
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?!?
        }
Exemple #10
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="RawObject">Raw JSON object to parse</param>
        /// <param name="BaseDir">Base directory for resolving relative paths</param>
        public ProjectDescriptor(JsonObject RawObject, DirectoryReference BaseDir)
        {
            // Read the version
            if (!RawObject.TryGetIntegerField("FileVersion", out FileVersion))
            {
                if (!RawObject.TryGetIntegerField("ProjectFileVersion", out FileVersion))
                {
                    throw new BuildException("Project 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("Project descriptor appears to be in a newer version ({0}) of the file format that we can load (max version: {1}).", FileVersion, (int)ProjectDescriptorVersion.Latest);
            }

            // Read simple fields
            RawObject.TryGetStringField("EngineAssociation", out EngineAssociation);
            RawObject.TryGetStringField("Category", out Category);
            RawObject.TryGetStringField("Description", out Description);
            RawObject.TryGetBoolField("Enterprise", out IsEnterpriseProject);
            RawObject.TryGetBoolField("DisableEnginePluginsByDefault", out DisableEnginePluginsByDefault);

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

            // Read the plugins
            JsonObject[] PluginsArray;
            if (RawObject.TryGetObjectArrayField("Plugins", out PluginsArray))
            {
                Plugins = Array.ConvertAll(PluginsArray, x => PluginReferenceDescriptor.FromJsonObject(x));
            }

            // Read the additional root directories
            string[] RootDirectoryStrings;
            if (RawObject.TryGetStringArrayField("AdditionalRootDirectories", out RootDirectoryStrings))
            {
                AdditionalRootDirectories.AddRange(RootDirectoryStrings.Select(x => DirectoryReference.Combine(BaseDir, x)));
            }

            // Read the additional plugin directories
            string[] PluginDirectoryStrings;
            if (RawObject.TryGetStringArrayField("AdditionalPluginDirectories", out PluginDirectoryStrings))
            {
                AdditionalPluginDirectories.AddRange(PluginDirectoryStrings.Select(x => DirectoryReference.Combine(BaseDir, x)));
            }

            // Read the target platforms
            RawObject.TryGetStringArrayField("TargetPlatforms", out TargetPlatforms);

            // Get the sample name hash
            RawObject.TryGetUnsignedIntegerField("EpicSampleNameHash", out EpicSampleNameHash);

            // Read the pre and post-build steps
            CustomBuildSteps.TryRead(RawObject, "PreBuildSteps", out PreBuildSteps);
            CustomBuildSteps.TryRead(RawObject, "PostBuildSteps", out PostBuildSteps);
        }
        /// <summary>
        /// Creates a plugin descriptor from a file on disk
        /// </summary>
        /// <param name="FileName">The filename to read</param>
        /// <returns>New plugin descriptor</returns>
        public static ProjectDescriptor FromFile(string FileName)
        {
            JsonObject RawObject = JsonObject.Read(FileName);

            try
            {
                ProjectDescriptor Descriptor = new ProjectDescriptor();

                // Read the version
                if (!RawObject.TryGetIntegerField("FileVersion", out Descriptor.FileVersion))
                {
                    if (!RawObject.TryGetIntegerField("ProjectFileVersion", out Descriptor.FileVersion))
                    {
                        throw new BuildException("Project descriptor '{0}' does not contain a valid FileVersion entry", FileName);
                    }
                }

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

                // Read simple fields
                RawObject.TryGetStringField("EngineAssociation", out Descriptor.EngineAssociation);
                RawObject.TryGetStringField("Category", out Descriptor.Category);
                RawObject.TryGetStringField("Description", out Descriptor.Description);

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

                // Read the plugins
                JsonObject[] PluginsArray;
                if (RawObject.TryGetObjectArrayField("Plugins", out PluginsArray))
                {
                    Descriptor.Plugins = Array.ConvertAll(PluginsArray, x => PluginReferenceDescriptor.FromJsonObject(x));
                }

                string[] Dirs;
                Descriptor.AdditionalPluginDirectories = new List <DirectoryReference>();
                // Read the additional plugin directories
                if (RawObject.TryGetStringArrayField("AdditionalPluginDirectories", out Dirs))
                {
                    for (int Index = 0; Index < Dirs.Length; Index++)
                    {
                        if (Path.IsPathRooted(Dirs[Index]))
                        {
                            // Absolute path so create in place
                            Descriptor.AdditionalPluginDirectories.Add(new DirectoryReference(Dirs[Index]));
                            Log.TraceVerbose("Project ({0}) : Added additional absolute plugin directory ({1})", FileName, Dirs[Index]);
                        }
                        else
                        {
                            // This path is relative to the project path so build that out
                            string RelativePath = Path.Combine(Path.GetDirectoryName(FileName), Dirs[Index]);
                            Descriptor.AdditionalPluginDirectories.Add(new DirectoryReference(RelativePath));
                            Log.TraceVerbose("Project ({0}) : Added additional relative plugin directory ({1})", FileName, Dirs[Index]);
                        }
                    }
                }

                // Read the target platforms
                RawObject.TryGetStringArrayField("TargetPlatforms", out Descriptor.TargetPlatforms);

                // Get the sample name hash
                RawObject.TryGetUnsignedIntegerField("EpicSampleNameHash", out Descriptor.EpicSampleNameHash);

                // Read the pre and post-build steps
                CustomBuildSteps.TryRead(RawObject, "PreBuildSteps", out Descriptor.PreBuildSteps);
                CustomBuildSteps.TryRead(RawObject, "PostBuildSteps", out Descriptor.PostBuildSteps);

                return(Descriptor);
            }
            catch (JsonParseException ParseException)
            {
                throw new JsonParseException("{0} (in {1})", ParseException.Message, FileName);
            }
        }
        /// <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);
        }
        /// <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;
                    }
                }
            }
            else
            {
                throw new BuildException("Platform extension plugin {0} was named improperly. It must be in the form <ParentPlugin>_<Platform>.uplugin", Filename);
            }

            // did we find a parent plugin?
            if (Parent == null)
            {
                throw new BuildException("Unable to find parent plugin {0} for platform extension plugin {1}. Make sure {0}.uplugin exists.", Tokens[0], 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);

            // this should cause an error if it's invalid platform name
            //UnrealTargetPlatform Platform = UnrealTargetPlatform.Parse(PlatformName);

            // merge the supported platforms
            if (Child.Descriptor.SupportedTargetPlatforms != null)
            {
                if (Parent.Descriptor.SupportedTargetPlatforms == null)
                {
                    Parent.Descriptor.SupportedTargetPlatforms = Child.Descriptor.SupportedTargetPlatforms;
                }
                else
                {
                    Parent.Descriptor.SupportedTargetPlatforms = Parent.Descriptor.SupportedTargetPlatforms.Union(Child.Descriptor.SupportedTargetPlatforms).ToList();
                }
            }

            // make sure we are whitelisted for any modules we list
            if (Child.Descriptor.Modules != null)
            {
                if (Parent.Descriptor.Modules == null)
                {
                    Parent.Descriptor.Modules = Child.Descriptor.Modules;
                }
                else
                {
                    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 (ChildModule.WhitelistPlatforms != null)
                            {
                                if (ParentModule.WhitelistPlatforms == null)
                                {
                                    ParentModule.WhitelistPlatforms = ChildModule.WhitelistPlatforms;
                                }
                                else
                                {
                                    ParentModule.WhitelistPlatforms = ParentModule.WhitelistPlatforms.Union(ChildModule.WhitelistPlatforms).ToList();
                                }
                            }
                            if (ChildModule.BlacklistPlatforms != null)
                            {
                                if (ParentModule.BlacklistPlatforms == null)
                                {
                                    ParentModule.BlacklistPlatforms = ChildModule.BlacklistPlatforms;
                                }
                                else
                                {
                                    ParentModule.BlacklistPlatforms = ParentModule.BlacklistPlatforms.Union(ChildModule.BlacklistPlatforms).ToList();
                                }
                            }
                        }
                        else
                        {
                            Parent.Descriptor.Modules.Add(ChildModule);
                        }
                    }
                }
            }

            // make sure we are whitelisted for any plugins we list
            if (Child.Descriptor.Plugins != null)
            {
                if (Parent.Descriptor.Plugins == null)
                {
                    Parent.Descriptor.Plugins = Child.Descriptor.Plugins;
                }
                else
                {
                    foreach (PluginReferenceDescriptor ChildPluginReference in Child.Descriptor.Plugins)
                    {
                        PluginReferenceDescriptor ParentPluginReference = Parent.Descriptor.Plugins.FirstOrDefault(x => x.Name.Equals(ChildPluginReference.Name));
                        if (ParentPluginReference != null)
                        {
                            // we only need to whitelist the platform if the parent had a whitelist (otherwise, we could mistakenly remove all other platforms)
                            if (ParentPluginReference.WhitelistPlatforms != null)
                            {
                                if (ChildPluginReference.WhitelistPlatforms != null)
                                {
                                    ParentPluginReference.WhitelistPlatforms = ParentPluginReference.WhitelistPlatforms.Union(ChildPluginReference.WhitelistPlatforms).ToList();
                                }
                            }

                            // if we want to blacklist a platform, add it even if the parent didn't have a blacklist. this won't cause problems with other platforms
                            if (ChildPluginReference.BlacklistPlatforms != null)
                            {
                                if (ParentPluginReference.BlacklistPlatforms == null)
                                {
                                    ParentPluginReference.BlacklistPlatforms = ChildPluginReference.BlacklistPlatforms;
                                }
                                else
                                {
                                    ParentPluginReference.BlacklistPlatforms = ParentPluginReference.BlacklistPlatforms.Union(ChildPluginReference.BlacklistPlatforms).ToList();
                                }
                            }
                        }
                        else
                        {
                            Parent.Descriptor.Plugins.Add(ChildPluginReference);
                        }
                    }
                }
            }
            // @todo platplug: what else do we want to support merging?!?
        }