// Get rid of any zombie lldb/iosdeploy processes, this needs to be reworked to use tracked process id's when running parallel tests across multiple AutomationTool.exe processes on test workers
        void KillZombies()
        {
            if (Globals.IsWorker || ZombiesKilled)
            {
                return;
            }

            ZombiesKilled = true;

            IOSBuild.ExecuteCommand("killall", "ios-deploy");
            Thread.Sleep(2500);
            IOSBuild.ExecuteCommand("killall", "lldb");
            Thread.Sleep(2500);
        }
        public bool Reboot()
        {
            const string Cmd = "/usr/local/bin/idevicediagnostics";

            if (!File.Exists(Cmd))
            {
                Log.Verbose("Rebooting iOS device requires idevicediagnostics binary");
                return(true);
            }

            var Result = IOSBuild.ExecuteCommand(Cmd, string.Format("restart -u {0}", DeviceName));

            if (Result.ExitCode != 0)
            {
                Log.Warning(string.Format("Failed to reboot iOS device {0}, restart command failed", DeviceName));
                return(true);
            }

            // initial wait 20 seconds
            Thread.Sleep(20 * 1000);

            const int WaitPeriod = 10;
            int       WaitTime   = 120;
            bool      rebooted   = false;

            do
            {
                Result = IOSBuild.ExecuteCommand(Cmd, string.Format("diagnostics WiFi -u {0}", DeviceName));
                if (Result.ExitCode == 0)
                {
                    rebooted = true;
                    break;
                }

                Thread.Sleep(WaitPeriod * 1000);
                WaitTime -= WaitPeriod;
            } while (WaitTime > 0);

            if (!rebooted)
            {
                Log.Warning("Failed to reboot iOS device {0}, device didn't come back after restart", DeviceName);
            }

            return(true);
        }
        /// <summary>
        /// Resign application using local executable and update debug symbols
        /// </summary>
        void ResignApplication(UnrealAppConfig AppConfig)
        {
            // check that we have the signing stuff we need
            string SignProvision    = Globals.Params.ParseValue("signprovision", String.Empty);
            string SignEntitlements = Globals.Params.ParseValue("signentitlements", String.Empty);
            string SigningIdentity  = Globals.Params.ParseValue("signidentity", String.Empty);

            // handle signing provision
            if (string.IsNullOrEmpty(SignProvision) || !File.Exists(SignProvision))
            {
                throw new AutomationException("Absolute path to existing provision must be specified, example: -signprovision=/path/to/myapp.provision");
            }

            // handle entitlements
            // Note this extracts entitlements: which may be useful when using same provision/entitlements?: codesign -d --entitlements :entitlements.plist ~/.gauntletappcache/Payload/Example.app/

            if (string.IsNullOrEmpty(SignEntitlements) || !File.Exists(SignEntitlements))
            {
                throw new AutomationException("Absolute path to existing entitlements must be specified, example: -signprovision=/path/to/entitlements.plist");
            }

            // signing identity
            if (string.IsNullOrEmpty(SigningIdentity))
            {
                throw new AutomationException("Signing identity must be specified, example: -signidentity=\"iPhone Developer: John Smith\"");
            }

            string ProjectName    = AppConfig.ProjectName;
            string BundleName     = Path.GetFileNameWithoutExtension(LocalAppBundle);
            string ExecutableName = UnrealHelpers.GetExecutableName(ProjectName, UnrealTargetPlatform.IOS, AppConfig.Configuration, AppConfig.ProcessType, "");
            string CachedAppPath  = Path.Combine(GauntletAppCache, "Payload", string.Format("{0}.app", BundleName));

            string LocalExecutable = Path.Combine(Environment.CurrentDirectory, ProjectName, string.Format("Binaries/IOS/{0}", ExecutableName));

            if (!File.Exists(LocalExecutable))
            {
                throw new AutomationException("Local executable not found for -dev argument: {0}", LocalExecutable);
            }

            File.WriteAllText(CacheResignedFilename, "The application has been resigned");

            // copy local executable
            FileInfo SrcInfo  = new FileInfo(LocalExecutable);
            string   DestPath = Path.Combine(CachedAppPath, BundleName);

            SrcInfo.CopyTo(DestPath, true);
            Log.Verbose("Copied local executable from {0} to {1}", LocalExecutable, DestPath);

            // copy provision
            SrcInfo  = new FileInfo(SignProvision);
            DestPath = Path.Combine(CachedAppPath, "embedded.mobileprovision");
            SrcInfo.CopyTo(DestPath, true);
            Log.Verbose("Copied provision from {0} to {1}", SignProvision, DestPath);

            // handle symbols
            string LocalSymbolsDir = Path.Combine(Environment.CurrentDirectory, ProjectName, string.Format("Binaries/IOS/{0}.dSYM", ExecutableName));

            DestPath = Path.Combine(GauntletAppCache, string.Format("Symbols/{0}.dSYM", ExecutableName));

            if (Directory.Exists(DestPath))
            {
                Directory.Delete(DestPath, true);
            }

            if (Directory.Exists(LocalSymbolsDir))
            {
                CommandUtils.CopyDirectory_NoExceptions(LocalSymbolsDir, DestPath, true);
            }
            else
            {
                Log.Warning("No symbols found for local build at {0}, removing cached app symbols", LocalSymbolsDir);
            }

            // resign application
            // @todo: this asks for password unless "Always Allow" is selected, also for builders, document how to permanently grant codesign access to keychain
            string SignArgs = string.Format("-f -s \"{0}\" --entitlements \"{1}\" \"{2}\"", SigningIdentity, SignEntitlements, CachedAppPath);

            Log.Info("\nResigning app, please enter keychain password if prompted:\n\ncodesign {0}", SignArgs);
            var Result = IOSBuild.ExecuteCommand("codesign", SignArgs);

            if (Result.ExitCode != 0)
            {
                throw new AutomationException("Failed to resign application");
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Generate MD5 and cache IPA bundle files
        /// </summary>
        private bool PrepareIPA(IOSBuild Build)
        {
            Log.Info("Preparing IPA {0}", Build.SourceIPAPath);

            try
            {
                // cache the unzipped app using a MD5 checksum, avoiding needing to unzip
                string Hash       = null;
                string StoredHash = null;
                using (var MD5Hash = MD5.Create())
                {
                    using (var Stream = File.OpenRead(Build.SourceIPAPath))
                    {
                        Hash = BitConverter.ToString(MD5Hash.ComputeHash(Stream)).Replace("-", "").ToLowerInvariant();
                    }
                }
                string PayloadDir = Path.Combine(GauntletAppCache, "Payload");
                string SymbolsDir = Path.Combine(GauntletAppCache, "Symbols");

                if (File.Exists(IPAHashFilename) && Directory.Exists(PayloadDir))
                {
                    StoredHash = File.ReadAllText(IPAHashFilename).Trim();
                    if (Hash != StoredHash)
                    {
                        Log.Verbose("IPA hash out of date, clearing cache");
                        StoredHash = null;
                    }
                }

                if (String.IsNullOrEmpty(StoredHash) || Hash != StoredHash)
                {
                    if (Directory.Exists(PayloadDir))
                    {
                        Directory.Delete(PayloadDir, true);
                    }

                    if (Directory.Exists(SymbolsDir))
                    {
                        Directory.Delete(SymbolsDir, true);
                    }

                    if (File.Exists(CacheResignedFilename))
                    {
                        File.Delete(CacheResignedFilename);
                    }

                    Log.Verbose("Unzipping IPA {0} to cache at: {1}", Build.SourceIPAPath, GauntletAppCache);

                    IProcessResult Result = IOSBuild.ExecuteCommand("unzip", String.Format("{0} -d {1}", Build.SourceIPAPath, GauntletAppCache));
                    if (Result.ExitCode != 0 || !Directory.Exists(PayloadDir))
                    {
                        throw new Exception(String.Format("Unable to extract IPA {0}", Build.SourceIPAPath));
                    }

                    // Cache symbols for symbolicated callstacks
                    string SymbolsZipFile = string.Format("{0}/../../Symbols/{1}.dSYM.zip", Path.GetDirectoryName(Build.SourceIPAPath), Path.GetFileNameWithoutExtension(Build.SourceIPAPath));

                    Log.Verbose("Checking Symbols at {0}", SymbolsZipFile);

                    if (File.Exists(SymbolsZipFile))
                    {
                        Log.Verbose("Unzipping Symbols {0} to cache at: {1}", SymbolsZipFile, SymbolsDir);

                        Result = IOSBuild.ExecuteCommand("unzip", String.Format("{0} -d {1}", SymbolsZipFile, SymbolsDir));
                        if (Result.ExitCode != 0 || !Directory.Exists(SymbolsDir))
                        {
                            throw new Exception(String.Format("Unable to extract build symbols {0} -> {1}", SymbolsZipFile, SymbolsDir));
                        }
                    }

                    // store hash
                    File.WriteAllText(IPAHashFilename, Hash);

                    Log.Verbose("IPA cached");
                }
                else
                {
                    Log.Verbose("Using cached IPA");
                }

                LocalAppBundle = Directory.GetDirectories(PayloadDir).Where(D => Path.GetExtension(D) == ".app").FirstOrDefault();

                if (String.IsNullOrEmpty(LocalAppBundle))
                {
                    throw new Exception(String.Format("Unable to find app in local app bundle {0}", PayloadDir));
                }
            }
            catch (Exception Ex)
            {
                throw new AutomationException("Unable to prepare {0} : {1}", Build.SourceIPAPath, Ex.Message);
            }

            return(true);
        }