/** * Handle the plethora of environment variables required to remote to the Mac */ static public void ConfigurePaths() { string MachineName = Environment.MachineName; XcodeDeveloperDir = Utilities.GetEnvironmentVariable("ue.XcodeDeveloperDir", "/Applications/Xcode.app/Contents/Developer/"); // MacName=%ue4.iPhone_SigningServerName% MacName = Config.OverrideMacName != null ? Config.OverrideMacName : Utilities.GetEnvironmentVariable("ue.IOSSigningServer", "a1487"); iPhone_SigningDevRootMac = Config.OverrideDevRoot != null ? Config.OverrideDevRoot : "/UE4/Builds"; if (!Config.bUseRPCUtil) { bool Results = SSHCommandHelper.Command(MacName, "xcode-select --print-path", "/usr/bin"); if (Results) { XcodeDeveloperDir = (string)SSHCommandHelper.SSHReturn["CommandOutput"] + "/"; XcodeDeveloperDir = XcodeDeveloperDir.TrimEnd(); } } // get the path to mirror into on the Mac string BinariesDir = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, @"..\..")); string Root = Path.GetPathRoot(BinariesDir); string BranchPath = MachineName + "/" + Root[0].ToString() + "/" + BinariesDir.Substring(Root.Length); BranchPath = BranchPath.Replace('\\', '/'); // similar for the game path (strip off the D:\ tpe root) BinariesDir = Path.GetFullPath(Path.Combine(Config.BinariesDirectory, "..")); Root = Path.GetPathRoot(BinariesDir); string GameBranchPath; if (Program.GameName == "UE4Game") { GameBranchPath = BranchPath; } else { GameBranchPath = MachineName + "/" + Root[0].ToString() + "/" + BinariesDir.Substring(Root.Length); GameBranchPath = GameBranchPath.Replace('\\', '/'); } Console.WriteLine("BranchPath = {0} --- GameBranchPath = {1}", BranchPath, GameBranchPath); // generate the directories to recursively copy into later on MacStagingRootDir = string.Format("{0}/{1}/" + Config.OSString, iPhone_SigningDevRootMac, GameBranchPath); MacStagingRootDir = MacStagingRootDir.Replace("//", "/"); MacBinariesDir = string.Format("{0}/{1}/" + Config.OSString, iPhone_SigningDevRootMac, GameBranchPath); MacBinariesDir = MacBinariesDir.Replace("//", "/"); MacXcodeStagingDir = string.Format("{0}/{1}/" + Config.OSString + "/XcodeSupportFiles", iPhone_SigningDevRootMac, GameBranchPath); MacXcodeStagingDir = MacXcodeStagingDir.Replace("//", "/"); MacMobileProvisionFilename = MachineName + "_UE4Temp.mobileprovision"; MacSigningIdentityFilename = MachineName + "_UE4Temp.p12"; }
/** * Handle spawning of the RPCUtility with parameters */ public static bool RunRPCUtilty(string RPCCommand, bool bIsSilent = false) { string CommandLine = ""; string WorkingFolder = ""; string DisplayCommandLine = ""; string TempKeychain = "$HOME/Library/Keychains/UE4TempKeychain.keychain"; string Certificate = "XcodeSupportFiles/" + MacSigningIdentityFilename; string LoginKeychain = "$HOME/Library/Keychains/login.keychain"; ErrorCodes Error = ErrorCodes.Error_Unknown; switch (RPCCommand.ToLowerInvariant()) { case "deletemacstagingfiles": Program.Log(" ... deleting staging files on the Mac"); DisplayCommandLine = "rm -rf Payload"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "ensureprovisiondirexists": Program.Log(" ... creating provisioning profiles directory"); DisplayCommandLine = String.Format("mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles"); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "\""; break; case "installprovision": // Note: The provision must have already been copied over to the Mac Program.Log(" ... installing .mobileprovision"); DisplayCommandLine = String.Format("cp -f {0} ~/Library/MobileDevice/Provisioning\\ Profiles", MacMobileProvisionFilename); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "\""; break; case "removeprovision": Program.Log(" ... removing .mobileprovision"); DisplayCommandLine = String.Format("rm -f ~/Library/MobileDevice/Provisioning\\ Profiles/{0}", MacMobileProvisionFilename); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "\""; break; case "setexec": // Note: The executable must have already been copied over Program.Log(" ... setting executable bit"); DisplayCommandLine = "chmod a+x \'" + RemoteExecutablePath + "\'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "prepackage": Program.Log(" ... running prepackage script remotely "); DisplayCommandLine = String.Format("sh prepackage.sh {0} " + Config.OSString + " {1} {2}", Program.GameName, Program.GameConfiguration, Program.Architecture); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "\""; break; case "makeapp": Program.Log(" ... making application (codesign, etc...)"); Program.Log(" Using signing identity '{0}'", Config.CodeSigningIdentity); DisplayCommandLine = "security -v unlock-keychain -p \"A\" \"" + TempKeychain + "\" && " + CurrentBaseXCodeCommandLine; CommandLine = "\"" + MacXcodeStagingDir + "/..\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "/..\""; Error = ErrorCodes.Error_RemoteCertificatesNotFound; break; case "createkeychain": Program.Log(" ... creating temporary key chain with signing certificate"); Program.Log(" Using signing identity '{0}'", Config.CodeSigningIdentity); DisplayCommandLine = "security create-keychain -p \"A\" \"" + TempKeychain + "\" && security list-keychains -s \"" + TempKeychain + "\" && security list-keychains && security set-keychain-settings -t 3600 -l \"" + TempKeychain + "\" && security -v unlock-keychain -p \"A\" \"" + TempKeychain + "\" && security import " + Certificate + " -k \"" + TempKeychain + "\" -P \"A\" -T /usr/bin/codesign -T /usr/bin/security -t agg && CERT_IDENTITY=$(security find-identity -v -p codesigning \"" + TempKeychain + "\" | head -1 | grep '\"' | sed -e 's/[^\"]*\"//' -e 's/\".*//') && security default-keychain -s \"" + TempKeychain + "\" && security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k \"A\" -D \"$CERT_IDENTITY\" -t private " + TempKeychain; CommandLine = "\"" + MacXcodeStagingDir + "/..\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "/..\""; break; case "deletekeychain": Program.Log(" ... remove temporary key chain"); Program.Log(" Using signing identity '{0}'", Config.CodeSigningIdentity); DisplayCommandLine = "security list-keychains -s \"" + LoginKeychain + "\" && security delete-keychain \"" + TempKeychain + "\""; CommandLine = "\"" + MacXcodeStagingDir + "/..\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "/..\""; break; case "validation": Program.Log(" ... validating distribution package"); DisplayCommandLine = XcodeDeveloperDir + "Platforms/iPhoneOS.platform/Developer/usr/bin/Validation " + RemoteAppDirectory; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "deleteipa": Program.Log(" ... deleting IPA on Mac"); DisplayCommandLine = "rm -f " + Config.IPAFilenameOnMac; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "kill": Program.Log(" ... killing"); DisplayCommandLine = "killall " + Program.GameName; CommandLine = ". " + DisplayCommandLine; WorkingFolder = "."; break; case "strip": Program.Log(" ... stripping"); DisplayCommandLine = "/usr/bin/xcrun strip '" + RemoteExecutablePath + "'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "resign": Program.Log("... resigning"); DisplayCommandLine = "bash -c '" + "chmod a+x ResignScript" + ";" + "./ResignScript" + "'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "zip": Program.Log(" ... zipping"); // NOTE: -y preserves symbolic links which is needed for iOS distro builds // -x excludes a file (excluding the dSYM keeps sizes smaller, and it shouldn't be in the IPA anyways) string dSYMName = "Payload/" + Program.GameName + Program.Architecture + ".app.dSYM"; DisplayCommandLine = String.Format("zip -q -r -y -{0} -T {1} Payload iTunesArtwork -x {2}/ -x {2}/* " + "-x {2}/Contents/ -x {2}/Contents/* -x {2}/Contents/Resources/ -x {2}/Contents/Resources/* " + " -x {2}/Contents/Resources/DWARF/ -x {2}/Contents/Resources/DWARF/*", (int)Config.RecompressionSetting, Config.IPAFilenameOnMac, dSYMName); CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; case "gendsym": Program.Log(" ... generating DSYM"); string ExePath = "Payload/" + Program.GameName + ".app/" + Program.GameName; string dSYMPath = Program.GameName + ".app.dSYM"; DisplayCommandLine = String.Format("dsymutil -o {0} {1}", dSYMPath, ExePath); CommandLine = "\"" + MacStagingRootDir + "\"" + DisplayCommandLine; WorkingFolder = "\"" + MacStagingRootDir + "\""; break; default: Program.Error("Unrecognized RPC command"); return(false); } Program.Log(" ... working folder: " + WorkingFolder); Program.Log(" ... " + DisplayCommandLine); Program.Log(" ... full command: " + MacName + " " + CommandLine); bool bSuccess = false; if (Config.bUseRPCUtil) { Program.Log("Running RPC on " + MacName + " ... "); Process RPCUtil = new Process(); RPCUtil.StartInfo.FileName = @"..\RPCUtility.exe"; RPCUtil.StartInfo.UseShellExecute = false; RPCUtil.StartInfo.Arguments = MacName + " " + CommandLine; RPCUtil.StartInfo.RedirectStandardOutput = true; RPCUtil.StartInfo.RedirectStandardError = true; RPCUtil.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedRemoteProcessCall); RPCUtil.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedRemoteProcessCall); RPCUtil.Start(); RPCUtil.BeginOutputReadLine(); RPCUtil.BeginErrorReadLine(); RPCUtil.WaitForExit(); bSuccess = (RPCUtil.ExitCode == 0); if (bSuccess == false && !bIsSilent) { Program.Error("RPCCommand {0} failed with return code {1}", RPCCommand, RPCUtil.ExitCode); switch (RPCCommand.ToLowerInvariant()) { case "installprovision": Program.Error("Ensure your access permissions for '~/Library/MobileDevice/Provisioning Profiles' are set correctly."); break; default: break; } } } else { Program.Log("Running SSH on " + MacName + " ... "); bSuccess = SSHCommandHelper.Command(MacName, DisplayCommandLine, WorkingFolder); if (bSuccess == false && !bIsSilent) { Program.Error("RPCCommand {0} failed with return code {1}", RPCCommand, Error); Program.ReturnCode = (int)Error; } } return(bSuccess); }
/** * Handle spawning of the RPCUtility with parameters */ public static bool RunRPCUtilty(string RPCCommand) { string CommandLine = ""; string WorkingFolder = ""; string DisplayCommandLine = ""; switch (RPCCommand.ToLowerInvariant()) { case "deletemacstagingfiles": Program.Log(" ... deleting staging files on the Mac"); DisplayCommandLine = "rm -rf Payload"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "ensureprovisiondirexists": Program.Log(" ... creating provisioning profiles directory"); DisplayCommandLine = String.Format("mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles"); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = MacXcodeStagingDir; break; case "installprovision": // Note: The provision must have already been copied over to the Mac Program.Log(" ... installing .mobileprovision"); DisplayCommandLine = String.Format("cp -f {0} ~/Library/MobileDevice/Provisioning\\ Profiles", MacMobileProvisionFilename); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = MacXcodeStagingDir; break; case "removeprovision": Program.Log(" ... removing .mobileprovision"); DisplayCommandLine = String.Format("rm -f ~/Library/MobileDevice/Provisioning\\ Profiles/{0}", MacMobileProvisionFilename); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = MacXcodeStagingDir; break; case "setexec": // Note: The executable must have already been copied over Program.Log(" ... setting executable bit"); DisplayCommandLine = "chmod a+x \'" + RemoteExecutablePath + "\'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "prepackage": Program.Log(" ... running prepackage script remotely "); DisplayCommandLine = String.Format("sh prepackage.sh {0} IOS {1} {2}", Program.GameName, Program.GameConfiguration, Program.Architecture); CommandLine = "\"" + MacXcodeStagingDir + "\" " + DisplayCommandLine; WorkingFolder = MacXcodeStagingDir; break; case "makeapp": Program.Log(" ... making application (codesign, etc...)"); Program.Log(" Using signing identity '{0}'", Config.CodeSigningIdentity); DisplayCommandLine = CurrentBaseXCodeCommandLine; CommandLine = "\"" + MacXcodeStagingDir + "/..\" " + DisplayCommandLine; WorkingFolder = "\"" + MacXcodeStagingDir + "/..\""; break; case "validation": Program.Log(" ... validating distribution package"); DisplayCommandLine = XcodeDeveloperDir + "Platforms/iPhoneOS.platform/Developer/usr/bin/Validation " + RemoteAppDirectory; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "deleteipa": Program.Log(" ... deleting IPA on Mac"); DisplayCommandLine = "rm -f " + Config.IPAFilenameOnMac; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "kill": Program.Log(" ... killing"); DisplayCommandLine = "killall " + Program.GameName; CommandLine = ". " + DisplayCommandLine; WorkingFolder = "."; break; case "strip": Program.Log(" ... stripping"); DisplayCommandLine = XcodeDeveloperDir + "Platforms/iPhoneOS.platform/Developer/usr/bin/strip '" + RemoteExecutablePath + "'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "resign": Program.Log("... resigning"); DisplayCommandLine = "bash -c '" + "chmod a+x ResignScript" + ";" + "./ResignScript" + "'"; CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "zip": Program.Log(" ... zipping"); // NOTE: -y preserves symbolic links which is needed for iOS distro builds // -x excludes a file (excluding the dSYM keeps sizes smaller, and it shouldn't be in the IPA anyways) string dSYMName = "Payload/" + Program.GameName + Program.Architecture + ".app.dSYM"; DisplayCommandLine = String.Format("zip -q -r -y -{0} -T {1} Payload iTunesArtwork -x {2}/ -x {2}/* " + "-x {2}/Contents/ -x {2}/Contents/* -x {2}/Contents/Resources/ -x {2}/Contents/Resources/* " + " -x {2}/Contents/Resources/DWARF/ -x {2}/Contents/Resources/DWARF/*", (int)Config.RecompressionSetting, Config.IPAFilenameOnMac, dSYMName); CommandLine = "\"" + MacStagingRootDir + "\" " + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; case "gendsym": Program.Log(" ... generating DSYM"); string ExePath = "Payload/" + Program.GameName + ".app/" + Program.GameName; string dSYMPath = Program.GameName + ".app.dSYM"; DisplayCommandLine = String.Format("dsymutil -o {0} {1}", dSYMPath, ExePath); CommandLine = "\"" + MacStagingRootDir + "\"" + DisplayCommandLine; WorkingFolder = MacStagingRootDir; break; default: Program.Error("Unrecognized RPC command"); return(false); } Program.Log(" ... working folder: " + WorkingFolder); Program.Log(" ... " + DisplayCommandLine); Program.Log(" ... full command: " + MacName + " " + CommandLine); bool bSuccess = false; if (Config.bUseRPCUtil) { Program.Log("Running RPC on " + MacName + " ... "); Process RPCUtil = new Process(); RPCUtil.StartInfo.FileName = @"..\RPCUtility.exe"; RPCUtil.StartInfo.UseShellExecute = false; RPCUtil.StartInfo.Arguments = MacName + " " + CommandLine; RPCUtil.StartInfo.RedirectStandardOutput = true; RPCUtil.StartInfo.RedirectStandardError = true; RPCUtil.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedRemoteProcessCall); RPCUtil.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedRemoteProcessCall); RPCUtil.Start(); RPCUtil.BeginOutputReadLine(); RPCUtil.BeginErrorReadLine(); RPCUtil.WaitForExit(); bSuccess = (RPCUtil.ExitCode == 0); if (bSuccess == false) { Program.Error("RPCCommand {0} failed with return code {1}", RPCCommand, RPCUtil.ExitCode); switch (RPCCommand.ToLowerInvariant()) { case "installprovision": Program.Error("Ensure your access permissions for '~/Library/MobileDevice/Provisioning Profiles' are set correctly."); break; default: break; } } } else { Program.Log("Running SSH on " + MacName + " ... "); bSuccess = SSHCommandHelper.Command(MacName, DisplayCommandLine, WorkingFolder); } return(bSuccess); }