// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using Tools.DotNETCommon; namespace UnrealBuildTool { /// /// The version format for .uplugin files. This rarely changes now; plugin descriptors should maintain backwards compatibility automatically. /// public enum PluginDescriptorVersion { /// /// Invalid /// Invalid = 0, /// /// Initial version /// Initial = 1, /// /// Adding SampleNameHash /// NameHash = 2, /// /// Unifying plugin/project files (since abandoned, but backwards compatibility maintained) /// ProjectPluginUnification = 3, /// /// This needs to be the last line, so we can calculate the value of Latest below /// LatestPlusOne, /// /// The latest plugin descriptor version /// Latest = LatestPlusOne - 1 } /// /// In-memory representation of a .uplugin file /// public class PluginDescriptor { /// /// Descriptor version number /// public int FileVersion; /// /// Version number for the plugin. The version number must increase with every version of the plugin, so that the system /// can determine whether one version of a plugin is newer than another, or to enforce other requirements. This version /// number is not displayed in front-facing UI. Use the VersionName for that. /// public int Version; /// /// Name of the version for this plugin. This is the front-facing part of the version number. It doesn't need to match /// the version number numerically, but should be updated when the version number is increased accordingly. /// public string VersionName; /// /// Friendly name of the plugin /// public string FriendlyName; /// /// Description of the plugin /// public string Description; /// /// The name of the category this plugin /// public string Category; /// /// The company or individual who created this plugin. This is an optional field that may be displayed in the user interface. /// public string CreatedBy; /// /// Hyperlink URL string for the company or individual who created this plugin. This is optional. /// public string CreatedByURL; /// /// Documentation URL string. /// public string DocsURL; /// /// Marketplace URL for this plugin. This URL will be embedded into projects that enable this plugin, so we can redirect to the marketplace if a user doesn't have it installed. /// public string MarketplaceURL; /// /// Support URL/email for this plugin. /// public string SupportURL; /// /// Sets the version of the engine that this plugin is compatible with. /// public string EngineVersion; /// /// List of platforms supported by this plugin. This list will be copied to any plugin reference from a project file, to allow filtering entire plugins from staged builds. /// public UnrealTargetPlatform[] SupportedTargetPlatforms; /// /// List of programs supported by this plugin. /// public string[] SupportedPrograms; /// /// List of all modules associated with this plugin /// public ModuleDescriptor[] Modules; /// /// List of all localization targets associated with this plugin /// public LocalizationTargetDescriptor[] LocalizationTargets; /// /// Whether this plugin should be enabled by default for all projects /// public Nullable bEnabledByDefault; /// /// Can this plugin contain content? /// public bool bCanContainContent; /// /// Marks the plugin as beta in the UI /// public bool bIsBetaVersion; /// /// Set for plugins which are installed /// public bool bInstalled; /// /// For plugins that are under a platform folder (eg. /PS4/), determines whether compiling the plugin requires the build platform and/or SDK to be available /// public bool bRequiresBuildPlatform; /// /// Set of pre-build steps to execute, keyed by host platform name. /// public CustomBuildSteps PreBuildSteps; /// /// Set of post-build steps to execute, keyed by host platform name. /// public CustomBuildSteps PostBuildSteps; /// /// Additional plugins that this plugin depends on /// public PluginReferenceDescriptor[] Plugins; /// /// Private constructor. This object should not be created directly; read it from disk using FromFile() instead. /// private PluginDescriptor() { FileVersion = (int)PluginDescriptorVersion.Latest; } /// /// Reads a plugin descriptor from a json object /// /// The object to read from /// New plugin descriptor 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("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)); } } /// /// Creates a plugin descriptor from a file on disk /// /// The filename to read /// New plugin descriptor public static PluginDescriptor FromFile(FileReference FileName) { JsonObject RawObject = JsonObject.Read(FileName); try { return new PluginDescriptor(RawObject); } catch (JsonParseException ParseException) { throw new JsonParseException("{0} (in {1})", ParseException.Message, FileName); } } /// /// Saves the descriptor to disk /// /// The filename to write to public void Save(string FileName) { using (JsonWriter Writer = new JsonWriter(FileName)) { Writer.WriteObjectStart(); Write(Writer); Writer.WriteObjectEnd(); } } /// /// Writes the plugin descriptor to an existing Json writer /// /// The writer to receive plugin data 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("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); } /// /// Determines if this reference enables the plugin for a given platform /// /// The platform to check /// True if the plugin should be enabled public bool SupportsTargetPlatform(UnrealTargetPlatform Platform) { return SupportedTargetPlatforms == null || SupportedTargetPlatforms.Length == 0 || SupportedTargetPlatforms.Contains(Platform); } } }