public IAppInstall InstallApplication(UnrealAppConfig AppConfig) { // todo - pass this through AndroidBuild Build = AppConfig.Build as AndroidBuild; // Ensure APK exists if (Build == null) { throw new AutomationException("Invalid build for Android!"); } // kill any currently running instance: KillRunningProcess(Build.AndroidPackageName); bool SkipDeploy = Globals.Params.ParseParam("SkipDeploy"); if (SkipDeploy == false) { // Establish remote directory locations string DeviceStorageQueryCommand = AndroidPlatform.GetStorageQueryCommand(); IProcessResult StorageQueryResult = RunAdbDeviceCommand(DeviceStorageQueryCommand); string StorageLocation = StorageQueryResult.Output.Trim(); // "/mnt/sdcard"; // remote dir used to save things string RemoteDir = StorageLocation + "/UE4Game/" + AppConfig.ProjectName; // if not a bulk/dev build, remote dir will be under /{StorageLocation}/Android/data/{PackageName} if ((Build.Flags & (BuildFlags.Bulk | BuildFlags.CanReplaceExecutable)) == 0) { RemoteDir = StorageLocation + "/Android/data/" + Build.AndroidPackageName + "/files/UE4Game/" + AppConfig.ProjectName; } string DependencyDir = RemoteDir + "/deps"; // device artifact path, always clear between runs DeviceArtifactPath = string.Format("{0}/{1}/Saved", RemoteDir, AppConfig.ProjectName); RunAdbDeviceCommand(string.Format("shell rm -r {0}", DeviceArtifactPath)); // path for OBB files string OBBRemoteDestination = string.Format("{0}/obb/{1}", StorageLocation, Build.AndroidPackageName); if (Globals.Params.ParseParam("cleandevice")) { Log.Info("Cleaning previous builds due to presence of -cleandevice"); // we need to ununstall then install the apk - don't care if it fails, may have been deleted Log.Info("Uninstalling {0}", Build.AndroidPackageName); RunAdbDeviceCommand(string.Format("uninstall {0}", Build.AndroidPackageName)); Log.Info("Removing {0}", RemoteDir); RunAdbDeviceCommand(string.Format("shell rm -r {0}", RemoteDir)); Log.Info("Removing {0}", OBBRemoteDestination); RunAdbDeviceCommand(string.Format("shell rm -r {0}", OBBRemoteDestination)); } // remote dir on the device, create it if it doesn't exist RunAdbDeviceCommand(string.Format("shell mkdir -p {0}/", RemoteDir)); IProcessResult AdbResult; string AdbCommand; // path to the APK to install. string ApkPath = Build.SourceApkPath; // check for a local newer executable if (Globals.Params.ParseParam("dev")) { //string ApkFileName = Path.GetFileName(ApkPath); string ApkFileName2 = UnrealHelpers.GetExecutableName(AppConfig.ProjectName, UnrealTargetPlatform.Android, AppConfig.Configuration, AppConfig.ProcessType, "apk"); string LocalAPK = Path.Combine(Environment.CurrentDirectory, AppConfig.ProjectName, "Binaries/Android", ApkFileName2); bool LocalFileExists = File.Exists(LocalAPK); bool LocalFileNewer = LocalFileExists && File.GetLastWriteTime(LocalAPK) > File.GetLastWriteTime(ApkPath); Log.Verbose("Checking for newer binary at {0}", LocalAPK); Log.Verbose("LocalFile exists: {0}. Newer: {1}", LocalFileExists, LocalFileNewer); if (LocalFileExists && LocalFileNewer) { ApkPath = LocalAPK; } } // first install the APK CopyFileToDevice(Build.AndroidPackageName, ApkPath, ""); // obb files need to be named based on APK version (grrr), so find that out. This should return something like // versionCode=2 minSdk=21 targetSdk=21 string PackageInfo = RunAdbDeviceCommand(string.Format("shell dumpsys package {0} | grep versionCode", Build.AndroidPackageName)).Output; var Match = Regex.Match(PackageInfo, @"versionCode=([\d\.]+)\s"); if (Match.Success == false) { throw new AutomationException("Failed to find version info for APK!"); } string PackageVersion = Match.Groups[1].ToString(); // Convert the files from the source to final destination names Dictionary <string, string> FilesToInstall = new Dictionary <string, string>(); Console.WriteLine("trying to copy files over."); if (AppConfig.FilesToCopy != null) { if (LocalDirectoryMappings.Count == 0) { Console.WriteLine("Populating Directory"); PopulateDirectoryMappings(DeviceArtifactPath); } Console.WriteLine("trying to copy files over."); foreach (UnrealFileToCopy FileToCopy in AppConfig.FilesToCopy) { string PathToCopyTo = Path.Combine(LocalDirectoryMappings[FileToCopy.TargetBaseDirectory], FileToCopy.TargetRelativeLocation); if (File.Exists(FileToCopy.SourceFileLocation)) { FileInfo SrcInfo = new FileInfo(FileToCopy.SourceFileLocation); SrcInfo.IsReadOnly = false; FilesToInstall.Add(FileToCopy.SourceFileLocation, PathToCopyTo.Replace("\\", "/")); Console.WriteLine("Copying {0} to {1}", FileToCopy.SourceFileLocation, PathToCopyTo); } else { Log.Warning("File to copy {0} not found", FileToCopy); } } } Build.FilesToInstall.Keys.ToList().ForEach(K => { string SrcPath = K; string DestPath = Build.FilesToInstall[K]; string DestFile = Path.GetFileName(DestPath); // If we installed a new APK we need to change the package version Match OBBMatch = Regex.Match(DestFile, @"main\.(\d+)\.com.*\.obb"); if (OBBMatch.Success) { string NewFileName = DestFile.Replace(OBBMatch.Groups[1].ToString(), PackageVersion); DestPath = DestPath.Replace(DestFile, NewFileName); } DestPath = Regex.Replace(DestPath, "%STORAGE%", StorageLocation, RegexOptions.IgnoreCase); FilesToInstall.Add(SrcPath, DestPath); }); // get a list of files in the destination OBB directory AdbResult = RunAdbDeviceCommand(string.Format("shell ls {0}", OBBRemoteDestination)); // if != 0 then no folder exists if (AdbResult.ExitCode == 0) { IEnumerable <string> CurrentRemoteFileList = AdbResult.Output.Replace("\r\n", "\n").Split('\n'); IEnumerable <string> NewRemoteFileList = FilesToInstall.Values.Select(F => Path.GetFileName(F)); // delete any files that should not be there foreach (string FileName in CurrentRemoteFileList) { if (FileName.StartsWith(".") || FileName.Length == 0) { continue; } if (NewRemoteFileList.Contains(FileName) == false) { RunAdbDeviceCommand(string.Format("shell rm {0}/{1}", OBBRemoteDestination, FileName)); } } } foreach (var KV in FilesToInstall) { string LocalFile = KV.Key; string RemoteFile = KV.Value; CopyFileToDevice(Build.AndroidPackageName, LocalFile, RemoteFile); } // create a tempfile, insert the command line, and push it over string TmpFile = Path.GetTempFileName(); CommandLineFilePath = string.Format("{0}/UE4CommandLine.txt", RemoteDir); // I've seen a weird thing where adb push truncates by a byte, so add some padding... File.WriteAllText(TmpFile, AppConfig.CommandLine + " "); AdbCommand = string.Format("push {0} {1}", TmpFile, CommandLineFilePath); RunAdbDeviceCommand(AdbCommand); File.Delete(TmpFile); } else { Log.Info("Skipping install of {0} (-skipdeploy)", Build.AndroidPackageName); } AndroidAppInstall AppInstall = new AndroidAppInstall(this, AppConfig.ProjectName, Build.AndroidPackageName, AppConfig.CommandLine); return(AppInstall); }
/// <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"); } }
public IAppInstall InstallApplication(UnrealAppConfig AppConfig) { LuminBuild Build = AppConfig.Build as LuminBuild; string QualifiedPackageName = GetQualifiedProjectName(AppConfig.ProjectName); InstalledAppName = AppConfig.ProjectName; // Ensure MPK exists if (Build == null) { throw new AutomationException("Invalid build for Lumin!"); } // kill any currently running instance: KillRunningProcess(QualifiedPackageName); bool SkipDeploy = Globals.Params.ParseParam("SkipDeploy"); if (SkipDeploy == false) { // remote dir used to save things string RemoteDir = "/documents/c2/" + AppConfig.ProjectName.ToLower(); //string DependencyDir = RemoteDir + "/deps"; // device artifact path, always clear between runs // This clear is from andorid, but currently not needed on mldb. Everything is removed when you remove the pak // If in the future mldb supports a "sidecar" of data like the obb on Android, might need to delete that here DeviceArtifactPath = string.Format("{0}/{1}/saved", RemoteDir.ToLower(), AppConfig.ProjectName.ToLower()); if (Globals.Params.ParseParam("cleandevice")) { Log.Info("Cleaning previous builds due to presence of -cleandevice"); // we need to ununstall then install the mpk - don't care if it fails, may have been deleted Log.Info("Uninstalling {0}", QualifiedPackageName); RunMldbDeviceCommand(string.Format("uninstall {0}", QualifiedPackageName)); } // path to the MPK to install. string MpkPath = Build.SourceMpkPath; // check for a local newer executable if (Globals.Params.ParseParam("dev")) { string MpkFileName = UnrealHelpers.GetExecutableName(AppConfig.ProjectName, UnrealTargetPlatform.Lumin, AppConfig.Configuration, AppConfig.ProcessType, "mpk"); string LocalMPK = Path.Combine(Environment.CurrentDirectory, AppConfig.ProjectName, "Binaries/Lumin", MpkFileName); bool LocalFileExists = File.Exists(LocalMPK); bool LocalFileNewer = LocalFileExists && File.GetLastWriteTime(LocalMPK) > File.GetLastWriteTime(MpkPath); Log.Verbose("Checking for newer binary at {0}", LocalMPK); Log.Verbose("LocalFile exists: {0}. Newer: {1}", LocalFileExists, LocalFileNewer); if (LocalFileExists && LocalFileNewer) { MpkPath = LocalMPK; } } // first install the MPK RunMldbDeviceCommand(string.Format("install -u {0}", MpkPath)); // If in the future mldb supports a "sidecar" of data like the obb on Android, might need to install that here // The Gauntlet.TargetDeviceAndroid.cs implementation should be a good starting point } LuminAppInstall AppInstall = new LuminAppInstall(this, AppConfig.ProjectName, Build.LuminPackageName, AppConfig.CommandLine); return AppInstall; }