/* * Copyright Epic Games, Inc. All Rights Reserved. */ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; namespace iPhonePackager { internal class Config { /// /// The display name in title bars, popups, etc... /// public static string AppDisplayName = "Unreal iOS Configuration"; public static string RootRelativePath = @"..\..\..\..\"; public static string GameDirectory = ""; // "..\\..\\..\\..\\Engine\\Source\\UE4"; public static string BinariesDirectory = ""; // "..\\..\\..\\..\\Engine\\Binaries\\IOS\\" /// /// Optional Prefix to append to .xcent and .mobileprovision files, for handling multiple certificates on same source game /// public static string SigningPrefix = ""; public static string PCStagingRootDir = ""; /// /// The local staging directory for files needed by Xcode on the Mac (on PC) /// public static string PCXcodeStagingDir = ""; /// /// The staging directory from UAT that is passed into IPP (for repackaging, etc...) /// public static string RepackageStagingDirectory = ""; /// /// The delta manifest for deploying files for iterative deploy /// public static string DeltaManifest = ""; /// public static List FilesForBackup = new List(); /// The project file that is passed into IPP from UAT /// public static string ProjectFile = ""; /// /// The device to deploy or launch on /// public static string DeviceId = ""; /// /// Determine whether to use RPC Util or not /// public static bool bUseRPCUtil = true; /// /// Optional override for the dev root on the target mac for remote builds. /// public static string OverrideDevRoot = null; /// /// Optional value specifying that we are working with tvOS /// public static string OSString = "IOS"; /// /// The local build directory (on PC) /// public static string BuildDirectory { get { string StandardGameBuildDir = GameDirectory + @"\Build\IOS"; // if the normal Build dir exists, return it, otherwise, use the program Resources directory return Path.GetFullPath(Directory.Exists(StandardGameBuildDir) ? StandardGameBuildDir : GameDirectory + @"\Resources\IOS"); } } /// /// The shared provision library directory (on PC) /// public static string ProvisionDirectory { get { if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { return Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/"; } else { return Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/"; } } } /// /// The shared (Engine) build directory (on PC) /// public static string EngineBuildDirectory { get { if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { return Path.GetFullPath (RootRelativePath + "Engine/Build/" + Config.OSString); } else { return Path.GetFullPath (RootRelativePath + @"Engine\Build\" + Config.OSString); } } } /// /// The local build intermediate directory (on PC) /// public static string IntermediateDirectory { get { string IntermediateGameBuildDir = GameDirectory + @"\Intermediate\" + Config.OSString; // if the normal Build dir exists, return it, otherwise, use the program Resources directory return Path.GetFullPath(Directory.Exists(IntermediateGameBuildDir) ? IntermediateGameBuildDir : BuildDirectory); } } static Config() { if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { RootRelativePath = "../../../../"; } } /// /// The local directory cooked files are placed (on PC) /// public static string CookedDirectory { get { return Path.GetFullPath(GameDirectory + @"\Saved\Cooked\" + Config.OSString); } } /// /// The local directory config files are placed (on PC) /// public static string ConfigDirectory { get { return Path.GetFullPath(GameDirectory + @"\Saved\Config"); } } /// /// The engine config files are placed (on PC) /// public static string DefaultConfigDirectory { get { return Path.GetFullPath(RootRelativePath + @"Engine\Config"); } } /// /// The engine directory /// public static string EngineDirectory { get { return Path.GetFullPath(RootRelativePath + @"Engine"); } } /// /// The local directory that a payload (GameName.app) is assembled into before being copied to the Mac (on PC) /// //@TODO: Deprecate this directory public static string PayloadDirectory { get { return Path.GetFullPath(PCStagingRootDir + @"\Payload\" + GetTargetName() + Program.Architecture + ".app"); } } /// /// The local directory that a payload (GameName.app) is assembled into before being copied to the Mac (on PC) /// public static string PayloadCookedDirectory { get { return Path.GetFullPath(PayloadDirectory + @"\cookeddata"); } } /// /// The local directory that a payload (GameName.app) is assembled into before being copied to the Mac (on PC) /// public static string PayloadRootDirectory { get { return Path.GetFullPath(PCStagingRootDir + @"\Payload"); } } /// /// Returns the filename for the IPA (no path, just filename) /// public static string IPAFilenameOnMac { get { if (Program.GameConfiguration == "Development") { return SigningPrefix + GetTargetName() + Program.Architecture + ".ipa"; } else { return SigningPrefix + GetTargetName() + "-" + Config.OSString + "-" + Program.GameConfiguration + Program.Architecture + ".ipa"; } } } /// /// Returns the name of the file containing user overrides that will be applied when packaging on PC /// public static string GetPlistOverrideFilename() { return GetPlistOverrideFilename(false); } public static string GetPlistOverrideFilename(bool bWantDistributionOverlay) { string Prefix = ""; if (bWantDistributionOverlay) { Prefix = "Distro_"; } return Path.Combine(BuildDirectory, Prefix + Program.GameName + "Overrides.plist"); } /// /// Returns the full path for either the stub or final IPA on the PC /// public static string GetIPAPath(string FileSuffix) { // Quash the default Epic_ so that stubs for Launcher installers get named correctly and can be used string FilePrefix = (SigningPrefix == "Epic_") ? "" : SigningPrefix; string Filename; if (Program.GameConfiguration == "Development") { Filename = Path.Combine(Config.BinariesDirectory, FilePrefix + GetTargetName() + Program.Architecture + FileSuffix); } else { Filename = Path.Combine(Config.BinariesDirectory, FilePrefix + GetTargetName() + "-" + Config.OSString + "-" + Program.GameConfiguration + Program.Architecture + FileSuffix); } return Filename; } public static string RemapIPAPath(string FileSuffix) { // Quash the default Epic_ so that stubs for Launcher installers get named correctly and can be used string FilePrefix = (SigningPrefix == "Epic_") ? "" : SigningPrefix; string Filename; string BinariesDir = BinariesDirectory; string ExeName = Program.GameName; if (!String.IsNullOrEmpty(ProjectFile)) { BinariesDir = Path.Combine(Path.GetDirectoryName(ProjectFile), "Binaries", Config.OSString); ExeName = Path.GetFileNameWithoutExtension(ProjectFile); } if (!bIsCodeBasedProject) { ExeName = (Program.IsClient ? "UnrealClient" : "UnrealGame"); } else if(Program.IsClient) { ExeName += "Client"; } if (Program.GameConfiguration == "Development") { Filename = Path.Combine(BinariesDir, FilePrefix + ExeName + Program.Architecture + FileSuffix); } else { Filename = Path.Combine(BinariesDir, FilePrefix + ExeName + "-" + Config.OSString + "-" + Program.GameConfiguration + Program.Architecture + FileSuffix); } // ensure the directory exists return Filename; } /// /// Returns target name, either the GameName + optional Client or specified by -targetname /// public static string GetTargetName() { return TargetName != "" ? TargetName : Program.GameName + (Program.IsClient ? "Client" : ""); } /// /// Returns the full path for the stub IPA, following the signing prefix resolution rules /// public static string GetIPAPathForReading(string FileSuffix) { if (Program.GameConfiguration == "Development") { return FileOperations.FindPrefixedFile(Config.BinariesDirectory, GetTargetName() + Program.Architecture + FileSuffix); } else { return FileOperations.FindPrefixedFile(Config.BinariesDirectory, GetTargetName() + "-" + Config.OSString + "-" + Program.GameConfiguration + Program.Architecture + FileSuffix); } } /// /// Whether or not to output extra information (like every file copy and date/time stamp) /// public static bool bVerbose = true; /// /// Whether or not to output extra information in code signing /// public static bool bCodeSignVerbose = false; /// /// Whether or not non-critical files will be packaged (critical == required for signing or .app validation, the app may still fail to /// run with only 'critical' files present). Also affects the name and location of the IPA back on the PC /// public static bool bCreateStubSet = false; /// /// Is this a distribution packaging build? Controls a number of aspects of packing (which signing prefix and provisioning profile to use, etc...) /// public static bool bForDistribution = false; /// /// Is this a c++ code based project? So far used on repackagefromstage to properly choose the .ipa name /// public static bool bIsCodeBasedProject = false; /// /// Is this going to try automatic signing? /// public static bool bAutomaticSigning = false; /// /// Whether or not to strip symbols (they will always be stripped when packaging for distribution) /// public static bool bForceStripSymbols = false; /// /// Do a code signing update when repackaging? /// public static bool bPerformResignWhenRepackaging = false; /// /// Whether the cooked data will be cooked on the fly or was already cooked by the books. /// public static bool bCookOnTheFly = false; /// /// Whether the install should be performed on a provision /// public static bool bProvision = false; /// /// provision to be installed /// public static string Provision = ""; /// /// provision to be installed /// public static string ProvisionUUID = ""; /// /// provision to extract certificate for /// public static string ProvisionFile; /// /// target name specificied by the command line, if not set GameName + optional Client will be used /// public static string TargetName = ""; /// /// IOS Team ID to be used for automatic signing /// public static string TeamID = ""; /// /// Whether the install should be performed on a certificate /// public static bool bCert = false; /// /// Certificate to be installed /// public static string Certificate = ""; /// /// Certificate to be output /// public static string OutputCertificate = null; /// /// An override server Mac name /// public static string OverrideMacName = null; /// /// A name to use for the bundle indentifier and display name when resigning /// public static string OverrideBundleName = null; /// /// Whether to use None or Best for the compression setting when repackaging IPAs (int version of Ionic.Zlib.CompressionLevel) /// By making this an int, we are able to delay load the Ionic assembly from a different path (which is required) /// public static int RecompressionSetting = 0; //Ionic.Zlib.CompressionLevel.None; /// /// Returns the application directory inside the zip /// public static string AppDirectoryInZIP { get { return _AppDirectoryInZIP != null ? _AppDirectoryInZIP : String.Format("Payload/{0}{1}.app", GetTargetName(), Program.Architecture); } set { _AppDirectoryInZIP = value.TrimEnd("/".ToCharArray()); } } private static string _AppDirectoryInZIP = null; /// /// If the requirements blob is present in the existing executable when code signing, should it be carried forward /// (true) or should a dummy requirements blob be created with no actual requirements (false) /// public static bool bMaintainExistingRequirementsWhenCodeSigning = false; /// /// The code signing identity to use when signing via RPC /// public static string CodeSigningIdentity; /// /// The minimum OS version /// public static string MinOSVersion; /// /// Create an iterative IPA (i.e. a stub only with Icons and Splash images) /// public static bool bIterate = false; /// /// Returns a path to the place to back up documents from a device /// /// public static string GetRootBackedUpDocumentsDirectory() { return Path.GetFullPath(Path.Combine(GameDirectory + @"\" + Config.OSString + "_Backups")); } public static bool Initialize(string InitialCurrentDirectory, string GamePath) { bool bIsEpicInternal = File.Exists(@"..\..\EpicInternal.txt"); // if the path is a directory (relative to where the game was launched from), then get the absolute directory string FullGamePath = Path.GetFullPath(Path.Combine(InitialCurrentDirectory, GamePath)); string OrigGamePath = GamePath; if (Directory.Exists(FullGamePath)) { GameDirectory = FullGamePath; } // is it a file? if so, just use the file's directory else if (File.Exists(FullGamePath)) { GameDirectory = Path.GetDirectoryName(FullGamePath); } // else we assume old school game name and look for it else { if (Program.GameName == "UnrealGame") { GameDirectory = Path.GetFullPath (Path.Combine (Config.RootRelativePath, GamePath)); } else { GameDirectory = Path.GetFullPath (Path.Combine (Config.RootRelativePath, Program.GameName)); } } if (!Directory.Exists(GameDirectory)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(); Console.WriteLine("Unable to find a game or program {0}. You may need to specify a path to the program", OrigGamePath); Console.ResetColor(); return false ; } // special case handling for anything inside Engine/Source, it will go to the Engine/Binaries directory, not the Game's binaries directory if (OrigGamePath.Replace("\\", "/").Contains("Engine/Source")) { BinariesDirectory = Path.Combine(RootRelativePath, @"Engine\Binaries\" + Config.OSString + @"\"); } else if (!OrigGamePath.Contains(@"Binaries\" + Config.OSString)) { // no sense in adding Binaries\IOS when it's already there. This is a special case to handle packaging UnrealLaunchDaemon from the command line. BinariesDirectory = Path.Combine(GameDirectory, @"Binaries\" + Config.OSString + @"\"); } else { BinariesDirectory = GameDirectory; } // Root directory on PC for staging files to copy to Mac Config.PCStagingRootDir = String.Format(@"{0}-Deploy\{1}\{2}{3}\", IntermediateDirectory, GetTargetName(), Program.GameConfiguration, Program.Architecture); // make a directory for the shared XcodeSupportFiles directory Config.PCXcodeStagingDir = Path.Combine(Config.PCStagingRootDir, @"..\XcodeSupportFiles"); // Code signing identity // Rules: // An environment variable wins if set // Otherwise for internal development builds, an internal identity is used // Otherwise, developer or distribution are used // Distro builds won't succeed on a machine with multiple distro certs installed unless the environment variable is set. Config.CodeSigningIdentity = Config.bForDistribution ? "iPhone Distribution" : "iPhone Developer"; if (Config.bForDistribution) { Config.CodeSigningIdentity = Utilities.GetEnvironmentVariable("ue.IOSDistributionSigningIdentity", Config.CodeSigningIdentity); } else { Config.CodeSigningIdentity = Utilities.GetEnvironmentVariable("ue.IOSDeveloperSigningIdentity", Config.CodeSigningIdentity); } // Remember to also change the default min version in UBT (iPhoneToolChain.cs) Config.MinOSVersion = Utilities.GetEnvironmentVariable("ue3.iPhone_MinOSVersion", "12.0"); // look for the signing prefix environment variable string DefaultPrefix = ""; if (bIsEpicInternal) { // default Epic to "Epic_" prefix DefaultPrefix = "Epic_"; } if (Config.bForDistribution) { DefaultPrefix = "Distro_"; } Config.SigningPrefix = Utilities.GetEnvironmentVariable("ue.IOSSigningPrefix", DefaultPrefix); // Windows doesn't allow environment vars to be set to blank so detect "none" and treat it as such if (Config.SigningPrefix == "none") { Config.SigningPrefix = Config.bForDistribution ? "Distro_" : ""; } CompileTime.ConfigurePaths(); return true; } } }