/// <summary> /// Copy the files always needed (even in a stub IPA) /// </summary> static public void CopyFilesNeededForMakeApp() { // Copy Info.plist over (modifiying it as needed) string SourcePListFilename = Utilities.GetPrecompileSourcePListFilename(); Utilities.PListHelper Info = Utilities.PListHelper.CreateFromFile(SourcePListFilename); // Edit the plist CookTime.UpdateVersion(Info); // Write out the <GameName>-Info.plist file to the xcode staging directory string TargetPListFilename = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + "-Info.plist"); Directory.CreateDirectory(Path.GetDirectoryName(TargetPListFilename)); string OutString = Info.SaveToString(); OutString = OutString.Replace("${EXECUTABLE_NAME}", Program.GameName); OutString = OutString.Replace("${BUNDLE_IDENTIFIER}", Program.GameName.Replace("_", "")); // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist // eventually we want to generate this based on what the user has set in the project settings string[] IPhoneConfigs = { "Default-IPhone6", "Landscape", "{375, 667}", "Default-IPhone6", "Portrait", "{375, 667}", "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}", "Default-IPhone6Plus-Portrait", "Portrait", "{414, 736}", "Default", "Landscape", "{320, 480}", "Default", "Portrait", "{320, 480}", "Default-568h", "Landscape", "{320, 568}", "Default-568h", "Portrait", "{320, 568}", "Default-IPhoneX-Landscape", "Landscape", "{375, 812}", "Default-IPhoneX-Portrait", "Portrait", "{375, 812}", }; StringBuilder NewLaunchImagesString = new StringBuilder("<key>UILaunchImages~iphone</key>\n\t\t<array>\n"); for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 3) { NewLaunchImagesString.Append("\t\t\t<dict>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageMinimumOSVersion</key>\n"); NewLaunchImagesString.Append("\t\t\t\t<string>8.0</string>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageName</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 0]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageOrientation</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 1]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageSize</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 2]); NewLaunchImagesString.Append("\t\t\t</dict>\n"); } // close it out NewLaunchImagesString.Append("\t\t\t</array>\n\t\t<key>UILaunchImages~ipad</key>"); OutString = OutString.Replace("<key>UILaunchImages~ipad</key>", NewLaunchImagesString.ToString()); byte[] RawInfoPList = Encoding.UTF8.GetBytes(OutString); File.WriteAllBytes(TargetPListFilename, RawInfoPList); Program.Log("Updating .plist: {0} --> {1}", SourcePListFilename, TargetPListFilename); // look for an entitlements file (optional) string SourceEntitlements = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements"); // set where to make the entitlements file ( string TargetEntitlements = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + ".entitlements"); if (File.Exists(SourceEntitlements)) { FileOperations.CopyRequiredFile(SourceEntitlements, TargetEntitlements); } else { // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, // which is based on distribution or not (true means debuggable) File.WriteAllText(TargetEntitlements, string.Format("<plist><dict><key>get-task-allow</key><{0}/></dict></plist>", Config.bForDistribution ? "false" : "true")); } // Copy the mobile provision file over string CFBundleIdentifier = null; Info.GetString("CFBundleIdentifier", out CFBundleIdentifier); bool bNameMatch; string ProvisionWithPrefix = MobileProvision.FindCompatibleProvision(CFBundleIdentifier, out bNameMatch); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory + "/NotForLicensees/", Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory, "UE4Game.mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory + "/NotForLicensees/", "UE4Game.mobileprovision"); } } } } string FinalMobileProvisionFilename = Path.Combine(Config.PCXcodeStagingDir, MacMobileProvisionFilename); FileOperations.CopyRequiredFile(ProvisionWithPrefix, FinalMobileProvisionFilename); // make sure this .mobileprovision file is newer than any other .mobileprovision file on the Mac (this file gets multiple games named the same file, // so the time stamp checking can fail when moving between games, a la the buildmachines!) File.SetLastWriteTime(FinalMobileProvisionFilename, DateTime.UtcNow); string ProjectFile = Config.RootRelativePath + @"Engine\Intermediate\ProjectFiles\UE4.xcodeproj\project.pbxproj"; if (Program.GameName != "UE4Game") { ProjectFile = Path.GetDirectoryName(Config.IntermediateDirectory) + @"\ProjectFiles\" + Program.GameName + @".xcodeproj\project.pbxproj"; } FileOperations.CopyRequiredFile(ProjectFile, Path.Combine(Config.PCXcodeStagingDir, @"project.pbxproj.datecheck")); // copy the signing certificate over // export the signing certificate to a file MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionWithPrefix); var Certificate = CodeSignatureBuilder.FindCertificate(Provision); byte[] Data = Certificate.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pkcs12, "A"); File.WriteAllBytes(Path.Combine(Config.PCXcodeStagingDir, MacSigningIdentityFilename), Data); Config.CodeSigningIdentity = Certificate.FriendlyName; // since the pipeline will use a temporary keychain that will contain only this certificate, this should be the only identity that will work CurrentBaseXCodeCommandLine = GetBaseXcodeCommandline(); // get the UUID string AllText = File.ReadAllText(FinalMobileProvisionFilename); string UUID = ""; int idx = AllText.IndexOf("<key>UUID</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; UUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } CurrentBaseXCodeCommandLine += String.Format(" PROVISIONING_PROFILE=" + UUID); // needs Mac line endings so it can be executed string SrcPath = @"..\..\..\Build\" + Config.OSString + @"\XcodeSupportFiles\prepackage.sh"; string DestPath = Path.Combine(Config.PCXcodeStagingDir, @"prepackage.sh"); Program.Log(" ... '" + SrcPath + "' -> '" + DestPath + "'"); string SHContents = File.ReadAllText(SrcPath); SHContents = SHContents.Replace("\r\n", "\n"); File.WriteAllText(DestPath, SHContents); CookTime.CopySignedFiles(); }
static public void ExecuteCommand(string Command, string RPCCommand) { if (ReturnCode > 0) { Warning("Error in previous command; suppressing: " + Command + " " + RPCCommand ?? ""); return; } if (Config.bVerbose) { Log(""); Log("----------"); Log(String.Format("Executing command '{0}' {1}...", Command, (RPCCommand != null) ? ("'" + RPCCommand + "' ") : "")); } try { bool bHandledCommand = CookTime.ExecuteCookCommand(Command, RPCCommand); if (!bHandledCommand) { bHandledCommand = CompileTime.ExecuteCompileCommand(Command, RPCCommand); } if (!bHandledCommand) { bHandledCommand = DeploymentHelper.ExecuteDeployCommand(Command, GamePath, RPCCommand); } if (!bHandledCommand) { bHandledCommand = true; switch (Command) { case "configure": if (Config.bForDistribution) { Error("configure cannot be used with -distribution"); Program.ReturnCode = (int)ErrorCodes.Error_Arguments; } else { RunInVisualMode(delegate { return(new ConfigureMobileGame()); }); } break; default: bHandledCommand = false; break; } } if (!bHandledCommand) { Error("Unknown command"); Program.ReturnCode = (int)ErrorCodes.Error_UnknownCommand; } } catch (Exception Ex) { Error("Error while executing command: " + Ex.ToString()); if (Program.ReturnCode == 0) { Program.ReturnCode = (int)ErrorCodes.Error_Unknown; } } Console.WriteLine(); }
static int Main(string[] args) { // remember the working directory at start, as the game path could be relative to this path string InitialCurrentDirectory = Environment.CurrentDirectory; // set the working directory to the location of the application (so relative paths always work) Environment.CurrentDirectory = Path.GetDirectoryName(Application.ExecutablePath); AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); // A simple, top-level try-catch block try { if (!ParseCommandLine(ref args)) { Log("Usage: iPhonePackager <Command> <GameName> [RPCCommand &| Switch]"); Log(""); Log("Common commands:"); Log(" ... RepackageIPA GameName"); Log(" ... PackageIPA GameName"); Log(" ... PackageApp GameName"); Log(" ... Deploy PathToIPA"); Log(" ... RepackageFromStage GameName"); Log(" ... Devices"); Log(" ... Validate"); Log(" ... Install"); Log(""); Log("Configuration switches:"); Log(" -stagedir <path> sets the directory to copy staged files from (defaults to none)"); Log(" -project <path> path to the project being packaged"); Log(" -compress=fast|best|none packaging compression level (defaults to none)"); Log(" -strip strip symbols during packaging"); Log(" -config game configuration (e.g., Shipping, Development, etc...)"); Log(" -distribution packaging for final distribution"); Log(" -createstub packaging stub IPA for later repackaging"); Log(" -mac <MacName> overrides the machine to use for any Mac operations"); Log(" -arch <Architecture> sets the architecture to use (blank for default, -simulator for simulator builds)"); Log(" -device <DeviceID> sets the device to install the IPA on"); Log(""); Log("Commands: RPC, Clean"); Log(" StageMacFiles, GetIPA, Deploy, Install, Uninstall"); Log(""); Log("RPC Commands: SetExec, InstallProvision, MakeApp, DeleteIPA, Copy, Kill, Strip, Zip, GenDSYM"); Log(""); Log("Sample commandlines:"); Log(" ... iPhonePackager Deploy UDKGame Release"); Log(" ... iPhonePackager RPC SwordGame Shipping MakeApp"); return((int)ErrorCodes.Error_Arguments); } Log("Executing iPhonePackager " + String.Join(" ", args)); Log("CWD: " + Directory.GetCurrentDirectory()); Log("Initial Dir: " + InitialCurrentDirectory); Log("Env CWD: " + Environment.CurrentDirectory); // Ensure shipping configuration for final distributions if (Config.bForDistribution && (GameConfiguration != "Shipping")) { Program.Warning("Distribution builds should be made in the Shipping configuration!"); } // process the GamePath (if could be ..\Samples\MyDemo\ or ..\Samples\MyDemo\MyDemo.uproject GameName = Path.GetFileNameWithoutExtension(GamePath); if (GameName.Equals("UE4", StringComparison.InvariantCultureIgnoreCase) || GameName.Equals("Engine", StringComparison.InvariantCultureIgnoreCase)) { GameName = "UE4Game"; } // setup configuration if (!Config.Initialize(InitialCurrentDirectory, GamePath)) { return((int)ErrorCodes.Error_Arguments); } switch (MainCommand.ToLowerInvariant()) { case "validate": // check to see if iTunes is installed string dllPath = ""; if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { dllPath = "/Applications/Xcode.app/Contents/MacOS/Xcode"; } else { dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "iTunesMobileDeviceDLL", null) as string; if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) { dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "MobileDeviceDLL", null) as string; } } if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) { Error("iTunes Not Found!!", (int)ErrorCodes.Error_SDKNotFound); } else { // validate there is a useable provision and cert MobileProvision Provision; X509Certificate2 Cert; bool bHasOverrides; bool bNameMatch; bool foundPlist = CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrides, out bNameMatch); if (!foundPlist) { Error("Could not find a valid plist file!!", (int)ErrorCodes.Error_InfoPListNotFound); } else if (Provision == null && Cert == null) { Error("No Provision or cert found!!", (int)ErrorCodes.Error_ProvisionAndCertificateNotFound); } else if (Provision == null) { Error("No Provision found!!", (int)ErrorCodes.Error_ProvisionNotFound); } else if (Cert == null) { Error("No Signing Certificate found!!", (int)ErrorCodes.Error_CertificateNotFound); } } break; case "packageapp": if (CheckArguments()) { if (Config.bCreateStubSet) { Error("packageapp cannot be used with the -createstub switch"); Program.ReturnCode = (int)ErrorCodes.Error_Arguments; } else { // Create the .app on the Mac CompileTime.CreateApplicationDirOnMac(); } } break; case "repackagefromstage": if (CheckArguments()) { if (Config.bCreateStubSet) { Error("repackagefromstage cannot be used with the -createstub switches"); Program.ReturnCode = (int)ErrorCodes.Error_Arguments; } else { bool bProbablyCreatedStub = Utilities.GetEnvironmentVariable("ue.IOSCreateStubIPA", true); if (!bProbablyCreatedStub) { Warning("ue.IOSCreateStubIPA is currently FALSE, which means you may be repackaging with an out of date stub IPA!"); } CookTime.RepackageIPAFromStub(); } } break; // this is the "super fast just move executable" mode for quick programmer iteration case "dangerouslyfast": if (CheckArguments()) { CompileTime.DangerouslyFastMode(); } break; case "packageipa": if (CheckArguments()) { CompileTime.PackageIPAOnMac(); } break; case "install": GameName = ""; if (Config.bProvision) { ToolsHub.TryInstallingMobileProvision(Config.Provision, false); } if (Config.bCert) { ToolsHub.TryInstallingCertificate_PromptForKey(Config.Certificate, false); } CodeSignatureBuilder.FindCertificates(); CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName); break; case "certificates": { CodeSignatureBuilder.FindCertificates(); CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName); } break; case "resigntool": RunInVisualMode(delegate { return(new GraphicalResignTool()); }); break; case "certrequest": RunInVisualMode(delegate { return(new GenerateSigningRequestDialog()); }); break; case "gui": RunInVisualMode(delegate { return(ToolsHub.CreateShowingTools()); }); break; case "devices": ListDevices(); break; default: // Commands by themself default to packaging for the device if (CheckArguments()) { ExecuteCommand(MainCommand, MainRPCCommand); } break; } } catch (Exception Ex) { Error("Application exception: " + Ex.ToString()); if (ReturnCode == 0) { Program.ReturnCode = (int)ErrorCodes.Error_Unknown; } } finally { if (DeploymentHelper.DeploymentServerProcess != null) { DeploymentHelper.DeploymentServerProcess.Close(); } } Environment.ExitCode = ReturnCode; return(ReturnCode); }
/// <summary> /// Copy the files always needed (even in a stub IPA) /// </summary> static public void CopyFilesNeededForMakeApp() { // Copy Info.plist over (modifiying it as needed) string SourcePListFilename = Utilities.GetPrecompileSourcePListFilename(); Utilities.PListHelper Info = Utilities.PListHelper.CreateFromFile(SourcePListFilename); // Edit the plist CookTime.UpdateVersion(Info); // Write out the <GameName>-Info.plist file to the xcode staging directory string TargetPListFilename = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + "-Info.plist"); Directory.CreateDirectory(Path.GetDirectoryName(TargetPListFilename)); string OutString = Info.SaveToString(); OutString = OutString.Replace("${EXECUTABLE_NAME}", Program.GameName); OutString = OutString.Replace("${BUNDLE_IDENTIFIER}", Program.GameName.Replace("_", "")); // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist // eventually we want to generate this based on what the user has set in the project settings string[] IPhoneConfigs = { "Default-IPhone6", "Landscape", "{375, 667}", "Default-IPhone6", "Portrait", "{375, 667}", "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}", "Default-IPhone6Plus-Portrait", "Portrait", "{414, 736}", "Default", "Landscape", "{320, 480}", "Default", "Portrait", "{320, 480}", "Default-568h", "Landscape", "{320, 568}", "Default-568h", "Portrait", "{320, 568}", }; StringBuilder NewLaunchImagesString = new StringBuilder("<key>UILaunchImages~iphone</key>\n\t\t<array>\n"); for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 3) { NewLaunchImagesString.Append("\t\t\t<dict>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageMinimumOSVersion</key>\n"); NewLaunchImagesString.Append("\t\t\t\t<string>8.0</string>\n"); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageName</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 0]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageOrientation</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 1]); NewLaunchImagesString.Append("\t\t\t\t<key>UILaunchImageSize</key>\n"); NewLaunchImagesString.AppendFormat("\t\t\t\t<string>{0}</string>\n", IPhoneConfigs[ConfigIndex + 2]); NewLaunchImagesString.Append("\t\t\t</dict>\n"); } // close it out NewLaunchImagesString.Append("\t\t\t</array>\n\t\t<key>UILaunchImages~ipad</key>"); OutString = OutString.Replace("<key>UILaunchImages~ipad</key>", NewLaunchImagesString.ToString()); byte[] RawInfoPList = Encoding.UTF8.GetBytes(OutString); File.WriteAllBytes(TargetPListFilename, RawInfoPList); Program.Log("Updating .plist: {0} --> {1}", SourcePListFilename, TargetPListFilename); // look for an entitlements file (optional) string SourceEntitlements = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".entitlements"); // set where to make the entitlements file ( string TargetEntitlements = Path.Combine(Config.PCXcodeStagingDir, Program.GameName + ".entitlements"); if (File.Exists(SourceEntitlements)) { FileOperations.CopyRequiredFile(SourceEntitlements, TargetEntitlements); } else { // we need to have something so Xcode will compile, so we just set the get-task-allow, since we know the value, // which is based on distribution or not (true means debuggable) File.WriteAllText(TargetEntitlements, string.Format("<plist><dict><key>get-task-allow</key><{0}/></dict></plist>", Config.bForDistribution ? "false" : "true")); } // Copy the no sign resource rules file over if (!Config.bForDistribution) { FileOperations.CopyRequiredFile(@"..\..\..\Build\IOS\XcodeSupportFiles\CustomResourceRules.plist", Path.Combine(Config.PCStagingRootDir, "CustomResourceRules.plist")); } // Copy the mobile provision file over string ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory, Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.BuildDirectory + "/NotForLicensees/", Program.GameName + ".mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory, "UE4Game.mobileprovision"); if (!File.Exists(ProvisionWithPrefix)) { ProvisionWithPrefix = FileOperations.FindPrefixedFile(Config.EngineBuildDirectory + "/NotForLicensees/", "UE4Game.mobileprovision"); } } } string FinalMobileProvisionFilename = Path.Combine(Config.PCXcodeStagingDir, MacMobileProvisionFilename); FileOperations.CopyRequiredFile(ProvisionWithPrefix, FinalMobileProvisionFilename); // make sure this .mobileprovision file is newer than any other .mobileprovision file on the Mac (this file gets multiple games named the same file, // so the time stamp checking can fail when moving between games, a la the buildmachines!) File.SetLastWriteTime(FinalMobileProvisionFilename, DateTime.UtcNow); FileOperations.CopyRequiredFile(Config.RootRelativePath + @"Engine\Intermediate\IOS\UE4.xcodeproj\project.pbxproj", Path.Combine(Config.PCXcodeStagingDir, @"project.pbxproj.datecheck")); // needs Mac line endings so it can be executed string SrcPath = @"..\..\..\Build\IOS\XcodeSupportFiles\prepackage.sh"; string DestPath = Path.Combine(Config.PCXcodeStagingDir, @"prepackage.sh"); Program.Log(" ... '" + SrcPath + "' -> '" + DestPath + "'"); string SHContents = File.ReadAllText(SrcPath); SHContents = SHContents.Replace("\r\n", "\n"); File.WriteAllText(DestPath, SHContents); CookTime.CopySignedFiles(); }