public string GetMLSDKVersion(ConfigHierarchy EngineIni) { string MLSDKPath; string Major = "0"; string Minor = "0"; if (EngineIni.GetString("/Script/LuminPlatformEditor.MagicLeapSDKSettings", "MLSDKPath", out MLSDKPath) && !string.IsNullOrWhiteSpace(MLSDKPath)) { Dictionary <string, string> PathEntry; ConfigHierarchy.TryParse(MLSDKPath, out PathEntry); MLSDKPath = PathEntry.Values.First(); } if (string.IsNullOrWhiteSpace(MLSDKPath)) { MLSDKPath = Environment.GetEnvironmentVariable("MLSDK"); } if (!string.IsNullOrEmpty(MLSDKPath)) { if (Directory.Exists(MLSDKPath)) { String VersionFile = string.Format("{0}/include/ml_version.h", MLSDKPath).Replace('/', Path.DirectorySeparatorChar); if (File.Exists(VersionFile)) { string FileText = File.ReadAllText(VersionFile); string Pattern = @"(MLSDK_VERSION_MAJOR) (?'MAJOR'\d+).*(MLSDK_VERSION_MINOR) (?'MINOR'\d+).*(MLSDK_VERSION_REVISION) (?'REV'\d+)"; Regex VersionRegex = new Regex(Pattern, RegexOptions.Singleline); MatchCollection Matches = VersionRegex.Matches(FileText); if (Matches.Count > 0 && !string.IsNullOrEmpty(Matches[0].Groups["MAJOR"].Value) && !string.IsNullOrEmpty(Matches[0].Groups["MINOR"].Value)) { Major = Matches[0].Groups["MAJOR"].Value; Minor = Matches[0].Groups["MINOR"].Value; } } } } return(string.Format("{0}.{1}", Major, Minor)); }
protected string GetConfigColor(string PlatformConfigKey, string DefaultValue) { var ConfigValue = GetConfigString(PlatformConfigKey, null, null); if (ConfigValue == null) { return(DefaultValue); } Dictionary <string, string> Pairs; int R, G, B; if (ConfigHierarchy.TryParse(ConfigValue, out Pairs) && int.TryParse(Pairs["R"], out R) && int.TryParse(Pairs["G"], out G) && int.TryParse(Pairs["B"], out B)) { return("#" + R.ToString("X2") + G.ToString("X2") + B.ToString("X2")); } Log.TraceWarning("Failed to parse color config value. Using default."); return(DefaultValue); }
public string GenerateManifest(string ProjectName, bool bForDistribution, string Architecture) { ConfigHierarchy GameIni = GetConfigCacheIni(ConfigHierarchyType.Game); string ProjectVersion = string.Empty; GameIni.GetString("/Script/EngineSettings.GeneralProjectSettings", "ProjectVersion", out ProjectVersion); if (string.IsNullOrEmpty(ProjectVersion)) { ProjectVersion = "1.0.0.0"; } ConfigHierarchy EngineIni = GetConfigCacheIni(ConfigHierarchyType.Engine); Int32 VersionCode; EngineIni.GetInt32("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "VersionCode", out VersionCode); string SDKVersion = GetMLSDKVersion(EngineIni); string PackageName = GetPackageName(ProjectName); string ApplicationDisplayName = GetApplicationDisplayName(ProjectName); string MinimumAPILevel = GetMinimumAPILevelRequired(); string TargetExecutableName = "bin/" + ProjectName; PackageManifest.version_name = ProjectVersion; PackageManifest.package = PackageName; PackageManifest.version_code = Convert.ToUInt64(VersionCode); PackageManifest.application = new manifestApplication { sdk_version = SDKVersion, min_api_level = MinimumAPILevel, visible_name = ApplicationDisplayName }; List <string> AppPrivileges; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "AppPrivileges", out AppPrivileges); List <string> ExtraComponentElements; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraComponentElements", out ExtraComponentElements); // We start with 1 since there will always be a 'root' <component> int Size = 1; Size += (AppPrivileges != null) ? AppPrivileges.Count : 0; Size += (ExtraComponentElements != null) ? ExtraComponentElements.Count : 0; // Increment Size here if more elements are required in the <application> node. // Index used for sibling elements (app privileges, root component and any extra components) int CurrentIndex = 0; PackageManifest.application.Items = new object[Size]; // Remove all invalid strings from the list of strings AppPrivileges.RemoveAll(item => item == "Invalid"); // Privileges get added first for (int Index = 0; Index < AppPrivileges.Count(); ++Index) { string TrimmedPrivilege = AppPrivileges[Index].Trim(' '); if (TrimmedPrivilege != "") { PackageManifest.application.Items[CurrentIndex] = new manifestApplicationUsesprivilege { name = TrimmedPrivilege, }; CurrentIndex++; } } // Then our root component, this is important as `mldb launch` will use the first component in the manifest PackageManifest.application.Items[CurrentIndex] = new manifestApplicationComponent(); manifestApplicationComponent RootComponent = (manifestApplicationComponent)PackageManifest.application.Items[CurrentIndex]; RootComponent.name = ".fullscreen"; RootComponent.visible_name = ApplicationDisplayName; RootComponent.binary_name = TargetExecutableName; RootComponent.type = GetApplicationType(); // Sub-elements under root <component> List <string> ExtraComponentSubElements; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraComponentSubElements", out ExtraComponentSubElements); List <string> LocalizedAppNames; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "LocalizedAppNames", out LocalizedAppNames); // We start with 1 since there will always be an icon element int NumElementsInRootComponent = 1; NumElementsInRootComponent += (ExtraComponentSubElements != null) ? ExtraComponentSubElements.Count : 0; // If localized app names have been specified, add count of 1 for the <locale> tag. NumElementsInRootComponent += (LocalizedAppNames != null) ? 1 : 0; // Increment NumElementsInRootComponent here if more elements are required in the <component> node. RootComponent.Items = new object[NumElementsInRootComponent]; // Root component icon Dictionary <string, manifestApplicationComponentIconTranslation> LocalizedIconsDict = new Dictionary <string, manifestApplicationComponentIconTranslation>(); if (Directory.Exists(IconDirectory + "/Model")) { string[] IconModelSubDirectories = Directory.GetDirectories(IconDirectory + "/Model"); foreach (var IconModelSubDirectory in IconModelSubDirectories) { manifestApplicationComponentIconTranslation LocalizedIcon = new manifestApplicationComponentIconTranslation(); LocalizedIcon.language = Path.GetFileName(IconModelSubDirectory); LocalizedIcon.model_folder = GetIconModelStagingPath() + "/" + LocalizedIcon.language; LocalizedIconsDict.Add(LocalizedIcon.language, LocalizedIcon); } } if (Directory.Exists(IconDirectory + "/Portal")) { string[] IconPortalSubDirectories = Directory.GetDirectories(IconDirectory + "/Portal"); foreach (var IconPortalSubDirectory in IconPortalSubDirectories) { manifestApplicationComponentIconTranslation LocalizedIcon; string language = Path.GetFileName(IconPortalSubDirectory); if (!LocalizedIconsDict.TryGetValue(language, out LocalizedIcon)) { LocalizedIcon = new manifestApplicationComponentIconTranslation(); LocalizedIcon.language = language; LocalizedIconsDict.Add(LocalizedIcon.language, LocalizedIcon); } LocalizedIcon.portal_folder = GetIconPortalStagingPath() + "/" + LocalizedIcon.language; } } manifestApplicationComponentIconTranslation[] LocalizedIcons = new manifestApplicationComponentIconTranslation[LocalizedIconsDict.Count]; int LocalizedIconIndex = 0; foreach (KeyValuePair <string, manifestApplicationComponentIconTranslation> LocalizedIconKVP in LocalizedIconsDict) { LocalizedIcons[LocalizedIconIndex++] = LocalizedIconKVP.Value; } RootComponent.Items[0] = new manifestApplicationComponentIcon(); ((manifestApplicationComponentIcon)RootComponent.Items[0]).locale = LocalizedIcons; ((manifestApplicationComponentIcon)RootComponent.Items[0]).model_folder = GetIconModelStagingPath(); ((manifestApplicationComponentIcon)RootComponent.Items[0]).portal_folder = GetIconPortalStagingPath(); int RootComponentIndex = 1; if (ExtraComponentSubElements != null) { for (int Index = 0; Index < ExtraComponentSubElements.Count(); ++Index) { Dictionary <string, string> NodeContent; if (ConfigHierarchy.TryParse(ExtraComponentSubElements[Index], out NodeContent)) { RootComponent.Items[RootComponentIndex] = GetComponentSubElement(NodeContent["ElementType"], NodeContent["Value"]); RootComponentIndex++; } } } // Localized app names if (LocalizedAppNames != null && LocalizedAppNames.Count > 0) { manifestApplicationComponentLocale LocaleTag = new manifestApplicationComponentLocale(); LocaleTag.Items = new manifestApplicationComponentLocaleTranslation[LocalizedAppNames.Count]; RootComponent.Items[RootComponentIndex] = LocaleTag; RootComponentIndex++; for (int i = 0; i < LocalizedAppNames.Count; ++i) { Dictionary <string, string> NodeContent; if (ConfigHierarchy.TryParse(LocalizedAppNames[i], out NodeContent)) { LocaleTag.Items[i] = new manifestApplicationComponentLocaleTranslation { language = NodeContent["LanguageCode"], visible_name = NodeContent["AppName"] }; } } } // Finally, add additional components CurrentIndex++; if (ExtraComponentElements != null) { for (int Index = 0; Index < ExtraComponentElements.Count(); ++Index) { Dictionary <string, string> ComponentElement; if (ConfigHierarchy.TryParse(ExtraComponentElements[Index], out ComponentElement)) { PackageManifest.application.Items[CurrentIndex] = GetComponentElement(ComponentElement); CurrentIndex++; } } } // Wrap up serialization XmlSerializer PackageManifestSerializer = new XmlSerializer(PackageManifest.GetType()); XmlSerializerNamespaces MLNamespace = new XmlSerializerNamespaces(); MLNamespace.Add("ml", "magicleap"); StringWriter Writer = new StringWriter(); PackageManifestSerializer.Serialize(Writer, PackageManifest, MLNamespace); // allow plugins to modify final manifest HERE XDocument XDoc; try { XDoc = XDocument.Parse(Writer.ToString()); } catch (Exception e) { throw new BuildException("LuminManifest.xml is invalid {0}\n{1}", e, Writer.ToString()); } UPL.ProcessPluginNode(Architecture, "luminManifestUpdates", "", ref XDoc); return(XDoc.ToString()); }
private object GetComponentElement(Dictionary <string, string> ComponentElement) { manifestApplicationComponent OutComponent = new manifestApplicationComponent { name = ComponentElement["Name"], visible_name = ComponentElement["VisibleName"], }; // App developer has the responsibility to package the executable in the bin folder, // perhaps by using UPL. We simply generate the manifest correctly. string BinaryName = ComponentElement["ExecutableName"]; if (BinaryName.IndexOf("bin/") != 0) { // Prepend bin folder string to executable name if it not there already. BinaryName = string.Format("bin/{0}", BinaryName); } OutComponent.binary_name = BinaryName; switch (ComponentElement["ComponentType"]) { case "Universe": OutComponent.type = manifestApplicationComponentType.Universe; break; case "Fullscreen": OutComponent.type = manifestApplicationComponentType.Fullscreen; break; case "SearchProvider": OutComponent.type = manifestApplicationComponentType.SearchProvider; break; case "MusicService": OutComponent.type = manifestApplicationComponentType.MusicService; break; case "Console": default: OutComponent.type = manifestApplicationComponentType.Console; break; case "SystemUI": OutComponent.type = manifestApplicationComponentType.SystemUI; break; } if (ComponentElement.ContainsKey("ExtraComponentSubElements")) { // Unfortunately there are no config object array parsing functions in UBT string SubElementsString = ComponentElement["ExtraComponentSubElements"]; string ConfObjArrayPattern = "\\([a-zA-Z0-9]+=[a-zA-Z0-9]+,[a-zA-Z0-9]+=\"?[a-zA-Z0-9]+\"?\\)"; Regex ConfigObjArrayRegex = new Regex(ConfObjArrayPattern); MatchCollection ConfigObjMatches = ConfigObjArrayRegex.Matches(SubElementsString); if (ConfigObjMatches.Count != 0) { OutComponent.Items = new object[ConfigObjMatches.Count]; for (int Index = 0; Index < ConfigObjMatches.Count; ++Index) { Match Match = ConfigObjMatches[Index]; Dictionary <string, string> SubElement; if (ConfigHierarchy.TryParse(Match.Value, out SubElement)) { OutComponent.Items[Index] = GetComponentSubElement(SubElement["ElementType"], SubElement["Value"]); } } } } return(OutComponent); }
/// <summary> /// Attempts to parse the given text into an object which matches a specific field type /// </summary> /// <param name="Text">The text to parse</param> /// <param name="FieldType">The type of field to parse</param> /// <param name="Value">If successful, a value of type 'FieldType'</param> /// <returns>True if the value could be parsed, false otherwise</returns> public static bool TryParseValue(string Text, Type FieldType, out object Value) { if (FieldType == typeof(string)) { Value = Text; return(true); } else if (FieldType == typeof(bool)) { bool BoolValue; if (ConfigHierarchy.TryParse(Text, out BoolValue)) { Value = BoolValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(int)) { int IntValue; if (ConfigHierarchy.TryParse(Text, out IntValue)) { Value = IntValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(float)) { float FloatValue; if (ConfigHierarchy.TryParse(Text, out FloatValue)) { Value = FloatValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(double)) { double DoubleValue; if (ConfigHierarchy.TryParse(Text, out DoubleValue)) { Value = DoubleValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(Guid)) { Guid GuidValue; if (ConfigHierarchy.TryParse(Text, out GuidValue)) { Value = GuidValue; return(true); } else { Value = null; return(false); } } else if (FieldType.IsEnum) { try { Value = Enum.Parse(FieldType, Text); return(true); } catch { Value = null; return(false); } } else if (FieldType.GetGenericTypeDefinition() == typeof(Nullable <>)) { return(TryParseValue(Text, FieldType.GetGenericArguments()[0], out Value)); } else { throw new Exception("Unsupported type for [ConfigFile] attribute"); } }
/// <summary> /// Construct an info object from a config file /// </summary> /// <param name="Config"></param> public bool InitFromConfig(ConfigFile Config) { // we must have the key section ConfigFileSection Section = null; if (Config.TryGetSection("DataDrivenPlatformInfo", out Section) == false) { return(false); } ConfigHierarchySection ParsedSection = new ConfigHierarchySection(new List <ConfigFileSection>() { Section }); // get string values if (ParsedSection.TryGetValue("IniParent", out IniParent) == false) { IniParent = ""; } // slightly nasty bool parsing for bool values string Temp; if (ParsedSection.TryGetValue("bIsConfidential", out Temp) == false || ConfigHierarchy.TryParse(Temp, out bIsConfidential) == false) { bIsConfidential = false; } return(true); }
/// <summary> /// Return all data driven infos found /// </summary> /// <returns></returns> public static Dictionary <string, ConfigDataDrivenPlatformInfo> GetAllPlatformInfos() { // need to init? if (PlatformInfos == null) { PlatformInfos = new Dictionary <string, ConfigDataDrivenPlatformInfo>(); Dictionary <string, string> IniParents = new Dictionary <string, string>(); foreach (DirectoryReference EngineConfigDir in UnrealBuildTool.GetAllEngineDirectories("Config")) { // look through all config dirs looking for the data driven ini file foreach (string FilePath in Directory.EnumerateFiles(EngineConfigDir.FullName, "DataDrivenPlatformInfo.ini", SearchOption.AllDirectories)) { FileReference FileRef = new FileReference(FilePath); // get the platform name from the path string IniPlatformName; if (FileRef.IsUnderDirectory(DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Config"))) { // Foo/Engine/Config/<Platform>/DataDrivenPlatformInfo.ini IniPlatformName = Path.GetFileName(Path.GetDirectoryName(FilePath)); } else { // Foo/Engine/Platforms/<Platform>/Config/DataDrivenPlatformInfo.ini IniPlatformName = Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(FilePath))); } // load the DataDrivenPlatformInfo from the path ConfigFile Config = new ConfigFile(FileRef); ConfigDataDrivenPlatformInfo NewInfo = new ConfigDataDrivenPlatformInfo(); // we must have the key section ConfigFileSection Section = null; if (Config.TryGetSection("DataDrivenPlatformInfo", out Section)) { ConfigHierarchySection ParsedSection = new ConfigHierarchySection(new List <ConfigFileSection>() { Section }); // get string values string IniParent; if (ParsedSection.TryGetValue("IniParent", out IniParent)) { IniParents[IniPlatformName] = IniParent; } // slightly nasty bool parsing for bool values string Temp; if (ParsedSection.TryGetValue("bIsConfidential", out Temp) == false || ConfigHierarchy.TryParse(Temp, out NewInfo.bIsConfidential) == false) { NewInfo.bIsConfidential = false; } // get a list of additional restricted folders IReadOnlyList <string> AdditionalRestrictedFolders; if (ParsedSection.TryGetValues("AdditionalRestrictedFolders", out AdditionalRestrictedFolders) && AdditionalRestrictedFolders.Count > 0) { NewInfo.AdditionalRestrictedFolders = AdditionalRestrictedFolders.Select(x => x.Trim()).Where(x => x.Length > 0).ToArray(); } // create cache it PlatformInfos[IniPlatformName] = NewInfo; } } } // now that all are read in, calculate the ini parent chain, starting with parent-most foreach (KeyValuePair <string, ConfigDataDrivenPlatformInfo> Pair in PlatformInfos) { string CurrentPlatform; // walk up the chain and build up the ini chain List <string> Chain = new List <string>(); if (IniParents.TryGetValue(Pair.Key, out CurrentPlatform)) { while (!string.IsNullOrEmpty(CurrentPlatform)) { // insert at 0 to reverse the order Chain.Insert(0, CurrentPlatform); if (IniParents.TryGetValue(CurrentPlatform, out CurrentPlatform) == false) { break; } } } // bake it into the info if (Chain.Count > 0) { Pair.Value.IniParentChain = Chain.ToArray(); } } } return(PlatformInfos); }
/// <summary> /// Attempts to parse the given text into an object which matches a specific field type /// </summary> /// <param name="Text">The text to parse</param> /// <param name="FieldType">The type of field to parse</param> /// <param name="Value">If successful, a value of type 'FieldType'</param> /// <returns>True if the value could be parsed, false otherwise</returns> public static bool TryParseValue(string Text, Type FieldType, out object Value) { if (FieldType == typeof(string)) { Value = Text; return(true); } else if (FieldType == typeof(bool)) { bool BoolValue; if (ConfigHierarchy.TryParse(Text, out BoolValue)) { Value = BoolValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(int)) { int IntValue; if (ConfigHierarchy.TryParse(Text, out IntValue)) { Value = IntValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(float)) { float FloatValue; if (ConfigHierarchy.TryParse(Text, out FloatValue)) { Value = FloatValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(double)) { double DoubleValue; if (ConfigHierarchy.TryParse(Text, out DoubleValue)) { Value = DoubleValue; return(true); } else { Value = null; return(false); } } else if (FieldType == typeof(Guid)) { Guid GuidValue; if (ConfigHierarchy.TryParse(Text, out GuidValue)) { Value = GuidValue; return(true); } else { Value = null; return(false); } } else { throw new Exception("Unsupported type for [ConfigFile] attribute"); } }
public string GenerateManifest(string ProjectName, bool bForDistribution, string Architecture) { ConfigHierarchy GameIni = GetConfigCacheIni(ConfigHierarchyType.Game); string ProjectVersion = string.Empty; GameIni.GetString("/Script/EngineSettings.GeneralProjectSettings", "ProjectVersion", out ProjectVersion); if (string.IsNullOrEmpty(ProjectVersion)) { ProjectVersion = "1.0.0.0"; } ConfigHierarchy EngineIni = GetConfigCacheIni(ConfigHierarchyType.Engine); Int32 VersionCode; EngineIni.GetInt32("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "VersionCode", out VersionCode); string SDKVersion = GetMLSDKVersion(EngineIni); string PackageName = GetPackageName(ProjectName); string ApplicationDisplayName = GetApplicationDisplayName(ProjectName); string MinimumAPILevel = GetMinimumAPILevelRequired(); string TargetExecutableName = "bin/" + ProjectName; PackageManifest.version_name = ProjectVersion; PackageManifest.package = PackageName; PackageManifest.version_code = Convert.ToUInt64(VersionCode); PackageManifest.application = new manifestApplication { sdk_version = SDKVersion, min_api_level = MinimumAPILevel, visible_name = ApplicationDisplayName }; List <string> AppPrivileges; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "AppPrivileges", out AppPrivileges); List <string> ExtraComponentElements; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraComponentElements", out ExtraComponentElements); // We always add an additional item as that will be our 'root' <component> int Size = (ExtraComponentElements == null ? AppPrivileges.Count() : AppPrivileges.Count() + ExtraComponentElements.Count()) + 1; // Index used for sibling elements (app privileges, root component and any extra components) int CurrentIndex = 0; PackageManifest.application.Items = new object[Size]; // Remove all invalid strings from the list of strings AppPrivileges.RemoveAll(item => item == "Invalid"); // Privileges get added first for (int Index = 0; Index < AppPrivileges.Count(); ++Index) { string TrimmedPrivilege = AppPrivileges[Index].Trim(' '); if (TrimmedPrivilege != "") { PackageManifest.application.Items[CurrentIndex] = new manifestApplicationUsesprivilege { name = TrimmedPrivilege, }; CurrentIndex++; } } // Then our root component, this is important as `mldb launch` will use the first component in the manifest PackageManifest.application.Items[CurrentIndex] = new manifestApplicationComponent(); manifestApplicationComponent RootComponent = (manifestApplicationComponent)PackageManifest.application.Items[CurrentIndex]; RootComponent.name = ".fullscreen"; RootComponent.visible_name = ApplicationDisplayName; RootComponent.binary_name = TargetExecutableName; RootComponent.type = GetApplicationType(); // Sub-elements under root <component> List <string> ExtraComponentSubElements; EngineIni.GetArray("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "ExtraComponentSubElements", out ExtraComponentSubElements); RootComponent.Items = (ExtraComponentSubElements == null ? new object[1] : new object[ExtraComponentSubElements.Count() + 1]); // Root component icon RootComponent.Items[0] = new manifestApplicationComponentIcon(); ((manifestApplicationComponentIcon)RootComponent.Items[0]).model_folder = GetIconModelStagingPath(); ((manifestApplicationComponentIcon)RootComponent.Items[0]).portal_folder = GetIconPortalStagingPath(); if (ExtraComponentSubElements != null) { for (int Index = 0; Index < ExtraComponentSubElements.Count(); ++Index) { Dictionary <string, string> NodeContent; if (ConfigHierarchy.TryParse(ExtraComponentSubElements[Index], out NodeContent)) { RootComponent.Items[Index + 1] = GetComponentSubElement(NodeContent["ElementType"], NodeContent["Value"]); } } } // Finally, add additional components CurrentIndex++; if (ExtraComponentElements != null) { for (int Index = 0; Index < ExtraComponentElements.Count(); ++Index) { Dictionary <string, string> ComponentElement; if (ConfigHierarchy.TryParse(ExtraComponentElements[Index], out ComponentElement)) { PackageManifest.application.Items[CurrentIndex] = GetComponentElement(ComponentElement); CurrentIndex++; } } } // Wrap up serialization XmlSerializer PackageManifestSerializer = new XmlSerializer(PackageManifest.GetType()); XmlSerializerNamespaces MLNamespace = new XmlSerializerNamespaces(); MLNamespace.Add("ml", "magicleap"); StringWriter Writer = new StringWriter(); PackageManifestSerializer.Serialize(Writer, PackageManifest, MLNamespace); // allow plugins to modify final manifest HERE XDocument XDoc; try { XDoc = XDocument.Parse(Writer.ToString()); } catch (Exception e) { throw new BuildException("LuminManifest.xml is invalid {0}\n{1}", e, Writer.ToString()); } UPL.ProcessPluginNode(Architecture, "luminManifestUpdates", "", ref XDoc); return(XDoc.ToString()); }
public List <string> CreateManifest(string InManifestName, string InOutputPath, string InIntermediatePath, FileReference InProjectFile, string InProjectDirectory, List <UnrealTargetConfiguration> InTargetConfigs, List <string> InExecutables) { // Verify we can find the SDK. string SDKDirectory = GetSDKDirectory(); if (string.IsNullOrEmpty(SDKDirectory)) { return(null); } // Check parameter values are valid. if (InTargetConfigs.Count != InExecutables.Count) { Log.TraceError("The number of target configurations ({0}) and executables ({1}) passed to manifest generation do not match.", InTargetConfigs.Count, InExecutables.Count); return(null); } if (InTargetConfigs.Count < 1) { Log.TraceError("The number of target configurations is zero, so we cannot generate a manifest."); return(null); } if (!CreateCheckDirectory(InOutputPath)) { Log.TraceError("Failed to create output directory \"{0}\".", InOutputPath); return(null); } if (!CreateCheckDirectory(InIntermediatePath)) { Log.TraceError("Failed to create intermediate directory \"{0}\".", InIntermediatePath); return(null); } OutputPath = InOutputPath; IntermediatePath = InIntermediatePath; ProjectFile = InProjectFile; ProjectPath = InProjectDirectory; UpdatedFilePaths = new List <string>(); // Load up INI settings. We'll use engine settings to retrieve the manifest configuration, but these may reference // values in either game or engine settings, so we'll keep both. GameIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Game, DirectoryReference.FromFile(InProjectFile), ConfigPlatform); EngineIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(InProjectFile), ConfigPlatform); // Load and verify/clean culture list { List <string> CulturesToStageWithDuplicates; GameIni.GetArray("/Script/UnrealEd.ProjectPackagingSettings", "CulturesToStage", out CulturesToStageWithDuplicates); GameIni.GetString("/Script/UnrealEd.ProjectPackagingSettings", "DefaultCulture", out DefaultCulture); if (CulturesToStageWithDuplicates == null || CulturesToStageWithDuplicates.Count < 1) { Log.TraceError("At least one culture must be selected to stage."); return(null); } CulturesToStage = CulturesToStageWithDuplicates.Distinct().ToList(); } if (DefaultCulture == null || DefaultCulture.Length < 1) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("A default culture must be selected to stage. Using {0}.", DefaultCulture); } if (!CulturesToStage.Contains(DefaultCulture)) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("The default culture must be one of the staged cultures. Using {0}.", DefaultCulture); } List <string> PerCultureValues; if (EngineIni.GetArray(IniSection_PlatformTargetSettings, "PerCultureResources", out PerCultureValues)) { foreach (string CultureCombinedValues in PerCultureValues) { Dictionary <string, string> SeparatedCultureValues; if (!ConfigHierarchy.TryParse(CultureCombinedValues, out SeparatedCultureValues)) { Log.TraceWarning("Invalid per-culture resource value: {0}", CultureCombinedValues); continue; } string StageId = SeparatedCultureValues["StageId"]; int CultureIndex = CulturesToStage.FindIndex(x => x == StageId); if (CultureIndex >= 0) { CulturesToStage[CultureIndex] = SeparatedCultureValues["CultureId"]; if (DefaultCulture == StageId) { DefaultCulture = SeparatedCultureValues["CultureId"]; } } } } // Only warn if shipping, we can run without translated cultures they're just needed for cert else if (InTargetConfigs.Contains(UnrealTargetConfiguration.Shipping)) { Log.TraceInformation("Staged culture mappings not setup in the editor. See Per Culture Resources in the {0} Target Settings.", Platform.ToString()); } // Clean out the resources intermediate path so that we know there are no stale binary files. string IntermediateResourceDirectory = Path.Combine(IntermediatePath, BuildResourceSubPath); RecursivelyForceDeleteDirectory(IntermediateResourceDirectory); if (!CreateCheckDirectory(IntermediateResourceDirectory)) { Log.TraceError("Could not create directory {0}.", IntermediateResourceDirectory); return(null); } // Construct a single resource writer for the default (no-culture) values string DefaultResourceIntermediatePath = Path.Combine(IntermediateResourceDirectory, "resources.resw"); DefaultResourceWriter = new UEResXWriter(DefaultResourceIntermediatePath); // Construct the ResXWriters for each culture PerCultureResourceWriters = new Dictionary <string, UEResXWriter>(); foreach (string Culture in CulturesToStage) { string IntermediateStringResourcePath = Path.Combine(IntermediateResourceDirectory, Culture); string IntermediateStringResourceFile = Path.Combine(IntermediateStringResourcePath, "resources.resw"); if (!CreateCheckDirectory(IntermediateStringResourcePath)) { Log.TraceWarning("Culture {0} resources not staged.", Culture); CulturesToStage.Remove(Culture); if (Culture == DefaultCulture) { DefaultCulture = CulturesToStage[0]; Log.TraceWarning("Default culture skipped. Using {0} as default culture.", DefaultCulture); } continue; } PerCultureResourceWriters.Add(Culture, new UEResXWriter(IntermediateStringResourceFile)); } // Create the manifest document string IdentityName = null; var ManifestXmlDocument = new XDocument(GetManifest(InTargetConfigs, InExecutables, out IdentityName)); // Export manifest to the intermediate directory then compare the contents to any existing target manifest // and replace if there are differences. string ManifestIntermediatePath = Path.Combine(IntermediatePath, InManifestName); string ManifestTargetPath = Path.Combine(OutputPath, InManifestName); ManifestXmlDocument.Save(ManifestIntermediatePath); CompareAndReplaceModifiedTarget(ManifestIntermediatePath, ManifestTargetPath); ProcessManifest(InTargetConfigs, InExecutables, InManifestName, ManifestTargetPath, ManifestIntermediatePath); // Clean out any resource directories that we aren't staging string TargetResourcePath = Path.Combine(OutputPath, BuildResourceSubPath); if (Directory.Exists(TargetResourcePath)) { List <string> TargetResourceDirectories = new List <string>(Directory.GetDirectories(TargetResourcePath, "*.*", SearchOption.AllDirectories)); foreach (string ResourceDirectory in TargetResourceDirectories) { if (!CulturesToStage.Contains(Path.GetFileName(ResourceDirectory))) { RecursivelyForceDeleteDirectory(ResourceDirectory); } } } // Export the resource tables starting with the default culture string DefaultResourceTargetPath = Path.Combine(OutputPath, BuildResourceSubPath, "resources.resw"); DefaultResourceWriter.Close(); CompareAndReplaceModifiedTarget(DefaultResourceIntermediatePath, DefaultResourceTargetPath); foreach (var Writer in PerCultureResourceWriters) { Writer.Value.Close(); string IntermediateStringResourceFile = Path.Combine(IntermediateResourceDirectory, Writer.Key, "resources.resw"); string TargetStringResourceFile = Path.Combine(OutputPath, BuildResourceSubPath, Writer.Key, "resources.resw"); CompareAndReplaceModifiedTarget(IntermediateStringResourceFile, TargetStringResourceFile); } // Copy all the binary resources into the target directory. CopyResourcesToTargetDir(); // The resource database is dependent on everything else calculated here (manifest, resource string tables, binary resources). // So if any file has been updated we'll need to run the config. if (UpdatedFilePaths.Count > 0) { // Create resource index configuration string PriExecutable = GetMakePriBinaryPath(); string ResourceConfigFile = Path.Combine(IntermediatePath, "priconfig.xml"); bool bEnableAutoResourcePacks = false; EngineIni.GetBool(IniSection_PlatformTargetSettings, "bEnableAutoResourcePacks", out bEnableAutoResourcePacks); // If the game is not going to support language resource packs then merge the culture qualifiers. if (bEnableAutoResourcePacks || CulturesToStage.Count <= 1) { RunExternalProgram(PriExecutable, "createconfig /cf \"" + ResourceConfigFile + "\" /dq " + DefaultCulture + " /o"); } else { RunExternalProgram(PriExecutable, "createconfig /cf \"" + ResourceConfigFile + "\" /dq " + String.Join("_", CulturesToStage) + " /o"); } // Modify configuration to restrict indexing to the Resources directory (saves time and space) XmlDocument PriConfig = new XmlDocument(); PriConfig.Load(ResourceConfigFile); // If the game is not going to support resource packs then remove the autoResourcePackages. if (!bEnableAutoResourcePacks) { XmlNode PackagingNode = PriConfig.SelectSingleNode("/resources/packaging"); PackagingNode.ParentNode.RemoveChild(PackagingNode); } // The previous implementation using startIndexAt="Resources" did not produce the expected ResourceMapSubtree hierarchy, so this manually specifies all resources in a .resfiles instead. string ResourcesResFile = Path.Combine(IntermediatePath, "resources.resfiles"); XmlNode PriIndexNode = PriConfig.SelectSingleNode("/resources/index"); XmlAttribute PriStartIndex = PriIndexNode.Attributes["startIndexAt"]; PriStartIndex.Value = ResourcesResFile; // Swap the default folder indexer-config to a RESFILES indexer-config. XmlElement FolderIndexerConfigNode = PriConfig.SelectSingleNode("/resources/index/indexer-config[@type='folder']") as XmlElement; FolderIndexerConfigNode.SetAttribute("type", "RESFILES"); FolderIndexerConfigNode.RemoveAttribute("foldernameAsQualifier"); FolderIndexerConfigNode.RemoveAttribute("filenameAsQualifier"); PriConfig.Save(ResourceConfigFile); IEnumerable <string> Resources = Directory.EnumerateFiles(Path.Combine(OutputPath, BuildResourceSubPath), "*.*", SearchOption.AllDirectories); System.Text.StringBuilder ResourcesList = new System.Text.StringBuilder(); foreach (string Resource in Resources) { ResourcesList.AppendLine(Resource.Replace(OutputPath, "").TrimStart('\\')); } File.WriteAllText(ResourcesResFile, ResourcesList.ToString()); // Remove previous pri files so we can enumerate which ones are new since the resource generator could produce a file for each staged language. IEnumerable <string> OldPriFiles = Directory.EnumerateFiles(IntermediatePath, "*.pri"); foreach (string OldPri in OldPriFiles) { try { File.Delete(OldPri); } catch (Exception) { Log.TraceError("Could not delete file {0}.", OldPri); } } // Generate the resource index string ResourceLogFile = Path.Combine(IntermediatePath, "ResIndexLog.xml"); string ResourceIndexFile = Path.Combine(IntermediatePath, "resources.pri"); string MakePriCommandLine = "new /pr \"" + OutputPath + "\" /cf \"" + ResourceConfigFile + "\" /mn \"" + ManifestTargetPath + "\" /il \"" + ResourceLogFile + "\" /of \"" + ResourceIndexFile + "\" /o"; if (IdentityName != null) { MakePriCommandLine += " /indexName \"" + IdentityName + "\""; } RunExternalProgram(PriExecutable, MakePriCommandLine); // Remove any existing pri target files that were not generated by this latest update IEnumerable <string> NewPriFiles = Directory.EnumerateFiles(IntermediatePath, "*.pri"); IEnumerable <string> TargetPriFiles = Directory.EnumerateFiles(OutputPath, "*.pri"); foreach (string TargetPri in TargetPriFiles) { if (!NewPriFiles.Contains(TargetPri)) { try { File.Delete(TargetPri); } catch (Exception) { Log.TraceError("Could not remove stale file {0}.", TargetPri); } } } // Stage all the modified pri files to the output directory foreach (string NewPri in NewPriFiles) { string NewResourceIndexFile = Path.Combine(IntermediatePath, Path.GetFileName(NewPri)); string FinalResourceIndexFile = Path.Combine(OutputPath, Path.GetFileName(NewPri)); CompareAndReplaceModifiedTarget(NewResourceIndexFile, FinalResourceIndexFile); } } return(UpdatedFilePaths); }
protected void AddResourceEntry(string ResourceEntryName, string ConfigKey, string GenericINISection, string GenericINIKey, string DefaultValue, string ValueSuffix = "") { string ConfigScratchValue = null; // Get the default culture value string DefaultCultureScratchValue; if (EngineIni.GetString(IniSection_PlatformTargetSettings, "CultureStringResources", out DefaultCultureScratchValue)) { Dictionary <string, string> Values; if (!ConfigHierarchy.TryParse(DefaultCultureScratchValue, out Values)) { Log.TraceError("Invalid default culture string resources: \"{0}\". Unable to add resource entry.", DefaultCultureScratchValue); return; } ConfigScratchValue = Values[ConfigKey]; } if (string.IsNullOrEmpty(ConfigScratchValue)) { // No platform specific value is provided. Use the generic config or default value ConfigScratchValue = ReadIniString(GenericINIKey, GenericINISection, DefaultValue); } DefaultResourceWriter.AddResource(ResourceEntryName, ConfigScratchValue + ValueSuffix); // Find the default value List <string> PerCultureValues; if (EngineIni.GetArray(IniSection_PlatformTargetSettings, "PerCultureResources", out PerCultureValues)) { foreach (string CultureCombinedValues in PerCultureValues) { Dictionary <string, string> SeparatedCultureValues; if (!ConfigHierarchy.TryParse(CultureCombinedValues, out SeparatedCultureValues) || !SeparatedCultureValues.ContainsKey("CultureStringResources") || !SeparatedCultureValues.ContainsKey("CultureId")) { Log.TraceError("Invalid per-culture resource: \"{0}\". Unable to add resource entry.", CultureCombinedValues); continue; } var CultureId = SeparatedCultureValues["CultureId"]; if (CulturesToStage.Contains(CultureId)) { Dictionary <string, string> CultureStringResources; if (!ConfigHierarchy.TryParse(SeparatedCultureValues["CultureStringResources"], out CultureStringResources)) { Log.TraceError("Invalid culture string resources: \"{0}\". Unable to add resource entry.", CultureCombinedValues); continue; } var Value = CultureStringResources[ConfigKey]; if (CulturesToStage.Contains(CultureId) && !string.IsNullOrEmpty(Value)) { var Writer = PerCultureResourceWriters[CultureId]; Writer.AddResource(ResourceEntryName, Value + ValueSuffix); } } } } }