public string GetPlatformPath(UnrealTargetRole Type, UnrealTargetPlatform Platform) { if (Type.UsesEditor()) { return(UnrealPath.FullName); } string BuildPath = BuildPaths.ElementAt(0); if (string.IsNullOrEmpty(BuildPath)) { return(null); } string PlatformPath = Path.Combine(BuildPath, UnrealHelpers.GetPlatformName(Platform, Type, UsesSharedBuildType)); // On some builds we stage the actual loose files into a "Staged" folder if (Directory.Exists(PlatformPath) && Directory.Exists(Path.Combine(PlatformPath, "staged"))) { PlatformPath = Path.Combine(PlatformPath, "Staged"); } // Urgh - build share uses a different style... if (Platform == UnrealTargetPlatform.Android && BuildName.Equals("Local", StringComparison.OrdinalIgnoreCase) == false) { PlatformPath = PlatformPath.Replace("Android_ETC2Client", "Android\\FullPackages"); } return(PlatformPath); }
public static IEnumerable <IOSBuild> CreateFromPath(string InProjectName, string InPath) { string BuildPath = InPath; List <IOSBuild> DiscoveredBuilds = new List <IOSBuild>(); DirectoryInfo Di = new DirectoryInfo(BuildPath); // find all install batchfiles FileInfo[] InstallFiles = Di.GetFiles("*.ipa"); foreach (FileInfo Fi in InstallFiles) { var UnrealConfig = UnrealHelpers.GetConfigurationFromExecutableName(InProjectName, Fi.Name); Log.Verbose("Pulling package data from {0}", Fi.FullName); string AbsPath = Fi.Directory.FullName; // IOS builds are always packaged, and can always replace the command line and executable as we cache the unzip'd IPA BuildFlags Flags = BuildFlags.Packaged | BuildFlags.CanReplaceCommandLine | BuildFlags.CanReplaceExecutable; if (AbsPath.Contains("Bulk")) { Flags |= BuildFlags.Bulk; } else { Flags |= BuildFlags.NotBulk; } string SourceIPAPath = Fi.FullName; string PackageName = GetBundleIdentifier(SourceIPAPath); if (String.IsNullOrEmpty(PackageName)) { continue; } Dictionary <string, string> FilesToInstall = new Dictionary <string, string>(); IOSBuild NewBuild = new IOSBuild(UnrealConfig, PackageName, SourceIPAPath, FilesToInstall, Flags); DiscoveredBuilds.Add(NewBuild); Log.Verbose("Found {0} {1} build at {2}", UnrealConfig, ((Flags & BuildFlags.Bulk) == BuildFlags.Bulk) ? "(bulk)" : "(not bulk)", AbsPath); } return(DiscoveredBuilds); }
EditorBuild CreateEditorBuild(string InProjectName, string InUnrealPath) { if (string.IsNullOrEmpty(InUnrealPath)) { return(null); } // check for the editor string EditorExe = Path.Combine(InUnrealPath, GetRelativeExecutablePath(UnrealTargetRole.Editor, BuildHostPlatform.Current.Platform, UnrealTargetConfiguration.Development)); if (!Utils.SystemHelpers.ApplicationExists(EditorExe)) { return(null); } // figure out the game name - they may have passed Foo or FooGame string ProjectPath = UnrealHelpers.GetProjectPath(InProjectName); if (File.Exists(ProjectPath)) { ProjectName = InProjectName; } else { // todo - this is ok, because we want people to be able to run staged builds // where no uproject file is available. /*throw new AutomationException("Unable to find project file for {0}. Neither {1} nor {2} exists.", * InProjectName, ProjectOption1, ProjectOption2);*/ return(null); } EditorBuild NewBuild = new EditorBuild(EditorExe); return(NewBuild); //List<string> Empty = new List<string>(); //return CanSupportRole(new UnrealSessionRole(UnrealRoleType.Editor, BuildHostPlatform.Current.Platform, UnrealTargetConfiguration.Development), ref Empty); ; }
/// <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) { // 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); }
public static IEnumerable <T> CreateFromPath <T>(UnrealTargetPlatform InPlatform, string InProjectName, string InPath, string InExecutableExtension) where T : StagedBuild { string BuildPath = InPath; List <T> DiscoveredBuilds = new List <T>(); // Turn FooGame into just Foo as we need to check for client/server builds too string ShortName = Regex.Replace(InProjectName, "Game", "", RegexOptions.IgnoreCase); string ContentPath = Path.Combine(InPath, InProjectName, "Content", "Paks"); if (Directory.Exists(ContentPath)) { string EngineBinaryPath = Path.Combine(InPath, "Engine", "Binaries", InPlatform.ToString()); string GameBinaryPath = Path.Combine(InPath, InProjectName, "Binaries", InPlatform.ToString()); // Executable will either be Project*.exe or for content-only UE4Game.exe string[] ExecutableMatches = new string[] { ShortName + "*" + InExecutableExtension, "UE4Game*" + InExecutableExtension, }; // check // 1) Path/Project/Binaries/Platform // 2) Path (content only builds on some platforms write out a stub exe here) // 3) path/Engine/Binaries/Platform string[] ExecutablePaths = new string[] { Path.Combine(InPath, InProjectName, "Binaries", InPlatform.ToString()), Path.Combine(InPath), Path.Combine(InPath, "Engine", "Binaries", InPlatform.ToString()), }; List <FileSystemInfo> Binaries = new List <FileSystemInfo>(); foreach (var BinaryPath in ExecutablePaths) { if (Directory.Exists(BinaryPath)) { DirectoryInfo Di = new DirectoryInfo(BinaryPath); foreach (var FileMatch in ExecutableMatches) { // Look at files & directories since apps on Mac are bundles FileSystemInfo[] AppFiles = Di.GetFileSystemInfos(FileMatch); Binaries.AddRange(AppFiles); } } } foreach (FileSystemInfo App in Binaries) { UnrealTargetConfiguration Config = UnrealHelpers.GetConfigurationFromExecutableName(InProjectName, App.Name); UnrealTargetRole Role = UnrealHelpers.GetRoleFromExecutableName(InProjectName, App.Name); if (Config != UnrealTargetConfiguration.Unknown && Role != UnrealTargetRole.Unknown) { // store the exe path as relative to the staged dir path T NewBuild = Activator.CreateInstance(typeof(T), new object[] { InPlatform, Config, Role, InPath, Utils.SystemHelpers.MakePathRelative(App.FullName, InPath) }) as T; if (App.Name.StartsWith("UE4Game", StringComparison.OrdinalIgnoreCase)) { NewBuild.Flags |= BuildFlags.ContentOnlyProject; } DiscoveredBuilds.Add(NewBuild); } } } return(DiscoveredBuilds); }
virtual protected bool ResolveBuildReference(string InBuildReference, Func <string, string> ResolutionDelegate, out IEnumerable <string> OutBuildPaths, out string OutBuildName) { OutBuildName = null; OutBuildPaths = null; if (string.IsNullOrEmpty(InBuildReference)) { return(false); } if (InBuildReference.Equals("AutoP4", StringComparison.InvariantCultureIgnoreCase)) { if (!CommandUtils.P4Enabled) { throw new AutomationException("-Build=AutoP4 requires -P4"); } if (CommandUtils.P4Env.Changelist < 1000) { throw new AutomationException("-Build=AutoP4 requires a CL from P4 and we have {0}", CommandUtils.P4Env.Changelist); } string BuildRoot = CommandUtils.CombinePaths(CommandUtils.RootBuildStorageDirectory()); string CachePath = InternalUtils.GetEnvironmentVariable("UE-BuildCachePath", ""); string SrcBuildPath = CommandUtils.CombinePaths(BuildRoot, ProjectName); string SrcBuildPath2 = CommandUtils.CombinePaths(BuildRoot, ProjectName.Replace("Game", "").Replace("game", "")); string SrcBuildPath_Cache = CommandUtils.CombinePaths(CachePath, ProjectName); string SrcBuildPath2_Cache = CommandUtils.CombinePaths(CachePath, ProjectName.Replace("Game", "").Replace("game", "")); if (!InternalUtils.SafeDirectoryExists(SrcBuildPath)) { if (!InternalUtils.SafeDirectoryExists(SrcBuildPath2)) { throw new AutomationException("-Build=AutoP4: Neither {0} nor {1} exists.", SrcBuildPath, SrcBuildPath2); } SrcBuildPath = SrcBuildPath2; SrcBuildPath_Cache = SrcBuildPath2_Cache; } string SrcCLPath = CommandUtils.CombinePaths(SrcBuildPath, CommandUtils.EscapePath(CommandUtils.P4Env.Branch) + "-CL-" + CommandUtils.P4Env.Changelist.ToString()); string SrcCLPath_Cache = CommandUtils.CombinePaths(SrcBuildPath_Cache, CommandUtils.EscapePath(CommandUtils.P4Env.Branch) + "-CL-" + CommandUtils.P4Env.Changelist.ToString()); if (!InternalUtils.SafeDirectoryExists(SrcCLPath)) { throw new AutomationException("-Build=AutoP4: {0} does not exist.", SrcCLPath); } if (InternalUtils.SafeDirectoryExists(SrcCLPath_Cache)) { InBuildReference = SrcCLPath_Cache; } else { InBuildReference = SrcCLPath; } Log.Verbose("Using AutoP4 path {0}", InBuildReference); } // BuildParam could be a path, a name that we should resolve to a path, Staged, or Editor DirectoryInfo BuildDir = new DirectoryInfo(InBuildReference); if (BuildDir.Exists) { // Easy option first - is this a full path? OutBuildName = BuildDir.Name; OutBuildPaths = new string[] { BuildDir.FullName }; } else if (BuildDir.Name.Equals("local", StringComparison.OrdinalIgnoreCase) || BuildDir.Name.Equals("staged", StringComparison.OrdinalIgnoreCase)) { string ProjectDir = Path.GetDirectoryName(UnrealHelpers.GetProjectPath(ProjectName)); if (string.IsNullOrEmpty(ProjectDir)) { throw new AutomationException("Could not find uproject for {0}.", ProjectName); } // First special case - "Staged" means use whats locally staged OutBuildName = "Local"; string StagedPath = Path.Combine(ProjectDir, "Saved", "StagedBuilds"); if (Directory.Exists(StagedPath) == false) { Log.Error("BuildReference was Staged but staged directory {0} not found", StagedPath); return(false); } // include binaries path for packaged builds string BinariesPath = Path.Combine(ProjectDir, "Binaries"); OutBuildPaths = new string[] { StagedPath, BinariesPath }; } else if (BuildDir.Name.Equals("editor", StringComparison.OrdinalIgnoreCase)) { // Second special case - "Editor" means run using the editor, no path needed OutBuildName = "Editor"; OutBuildPaths = new string[] { Environment.CurrentDirectory }; } else { // todo - make this more generic if (BuildDir.Name.Equals("usesyncedbuild", StringComparison.OrdinalIgnoreCase)) { BuildVersion Version; if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) { InBuildReference = Version.BranchName + "-CL-" + Version.Changelist.ToString(); } } // See if it's in the passed locations if (ResolutionDelegate != null) { string FullPath = ResolutionDelegate(InBuildReference); if (string.IsNullOrEmpty(FullPath) == false) { DirectoryInfo Di = new DirectoryInfo(FullPath); if (Di.Exists == false) { throw new AutomationException("Resolution delegate returned non existent path"); } OutBuildName = Di.Name; OutBuildPaths = new string[] { Di.FullName }; } } } if (string.IsNullOrEmpty(OutBuildName) || (OutBuildPaths == null || OutBuildPaths.Count() == 0)) { Log.Error("Unable to resolve build argument '{0}'", InBuildReference); return(false); } return(true); }
public static IEnumerable <LuminBuild> CreateFromPath(string InProjectName, string InPath) { string BuildPath = InPath; List <LuminBuild> DiscoveredBuilds = new List <LuminBuild>(); DirectoryInfo Di = new DirectoryInfo(BuildPath); if (Di.Exists) { // find all install batchfiles FileInfo[] InstallFiles = Di.GetFiles("Install_*"); foreach (FileInfo Fi in InstallFiles) { Log.Verbose("Pulling install data from {0}", Fi.FullName); string AbsPath = Fi.Directory.FullName; // read contents and replace linefeeds (regex doesn't stop on them :(( string BatContents = File.ReadAllText(Fi.FullName).Replace(Environment.NewLine, "\n"); // Replace .bat with .mpk and strip up to and including the first _, that is then our MPK name string SourceMpkPath = Regex.Replace(Fi.Name, ".bat", ".mpk", RegexOptions.IgnoreCase); SourceMpkPath = SourceMpkPath.Substring(SourceMpkPath.IndexOf("_") + 1); SourceMpkPath = Path.Combine(AbsPath, SourceMpkPath); Match Info = Regex.Match(BatContents, @"install\s+(-u+)\s""%~dp0\\(.+)"""); string LuminPackageName = Info.Groups[2].ToString(); if (string.IsNullOrEmpty(SourceMpkPath)) { Log.Warning("No MPK found for build at {0}", Fi.FullName); continue; } if (string.IsNullOrEmpty(LuminPackageName)) { Log.Warning("No product name found for build at {0}", Fi.FullName); continue; } UnrealTargetConfiguration UnrealConfig = UnrealHelpers.GetConfigurationFromExecutableName(InProjectName, Fi.Name); // Lumin builds are always packaged, and we can always replace the command line BuildFlags Flags = BuildFlags.Packaged | BuildFlags.CanReplaceCommandLine; if (AbsPath.Contains("Bulk")) { Flags |= BuildFlags.Bulk; } else { Flags |= BuildFlags.NotBulk; } LuminBuild NewBuild = new LuminBuild(UnrealConfig, LuminPackageName, SourceMpkPath, Flags); DiscoveredBuilds.Add(NewBuild); Log.Verbose("Found {0} {1} build at {2}", UnrealConfig, ((Flags & BuildFlags.Bulk) == BuildFlags.Bulk) ? "(bulk)" : "(not bulk)", AbsPath); } } return(DiscoveredBuilds); }
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; }
public static IEnumerable <AndroidBuild> CreateFromPath(string InProjectName, string InPath) { string BuildPath = InPath; List <AndroidBuild> DiscoveredBuilds = new List <AndroidBuild>(); DirectoryInfo Di = new DirectoryInfo(BuildPath); // find all install batchfiles FileInfo[] InstallFiles = Di.GetFiles("Install_*"); foreach (FileInfo Fi in InstallFiles) { bool PackageIs32Bit = Fi.FullName.Contains("armv7"); UnrealTargetConfiguration UnrealConfig = UnrealHelpers.GetConfigurationFromExecutableName(InProjectName, Fi.FullName); UnrealTargetRole UnrealRole = UnrealHelpers.GetRoleFromExecutableName(InProjectName, Fi.FullName); if (UnrealConfig == UnrealTargetConfiguration.Unknown) { Log.Info("Skipping unrecognized build {0}", Fi.FullName); continue; } bool TestInstall = Fi.Name.EndsWith("_TEST.bat", StringComparison.OrdinalIgnoreCase); bool PatchInstall = Fi.Name.EndsWith("_Patch.bat", StringComparison.OrdinalIgnoreCase); // filter out non-matching or test installation batch files // test installation scripts are intended to be manually invoked if (TestInstall || PatchInstall) { if (TestInstall || PatchInstall) { Log.Verbose("Ignoring {0} installation batch file {1}", TestInstall ? "test" : "patch", Fi.Name); } continue; } Log.Verbose("Pulling install data from {0}", Fi.FullName); string AbsPath = Fi.Directory.FullName; // read contents and replace linefeeds (regex doesn't stop on them :(( string BatContents = File.ReadAllText(Fi.FullName).Replace(Environment.NewLine, "\n"); // Replace .bat with .apk and strip up to and including the first _, that is then our APK name var SourceApkMatch = Regex.Match(BatContents, @" install\s+(.+\.apk)"); if (SourceApkMatch.Groups.Count <= 0) { Log.Warning("Could not parse install command from {0}", Fi.FullName); continue; } string SourceApkPath = Path.Combine(AbsPath, SourceApkMatch.Groups[1].ToString()); // save com.companyname.product string AndroidPackageName = Regex.Match(BatContents, @"uninstall\s+(com\..+)").Groups[1].ToString(); // pull all OBBs (probably just one..) var OBBMatches = Regex.Matches(BatContents, @"push\s+(.+?)\s+(.+)"); // save them as a dict of full paths as keys and dest paths as values Dictionary <string, string> FilesToInstall = OBBMatches.Cast <Match>().ToDictionary(M => Path.Combine(AbsPath, M.Groups[1].ToString()), M => M.Groups[2].ToString()); if (string.IsNullOrEmpty(SourceApkPath)) { Log.Warning("No APK found for build at {0}", Fi.FullName); continue; } if (!File.Exists(SourceApkPath)) { Log.Warning("Resolved APK name but it doesn't exist {0}", SourceApkPath); continue; } if (string.IsNullOrEmpty(AndroidPackageName)) { Log.Warning("No product name found for build at {0}", Fi.FullName); continue; } // Android builds are always packaged, and we can always replace the command line BuildFlags Flags = BuildFlags.Packaged | BuildFlags.CanReplaceCommandLine; // if there's data then the pak files are in an obb and we can sub in a new exe if (FilesToInstall.Count() > 0) { Flags |= BuildFlags.CanReplaceExecutable; } if (AbsPath.Contains("Bulk")) { Flags |= BuildFlags.Bulk; } else { Flags |= BuildFlags.NotBulk; } AndroidBuild NewBuild = new AndroidBuild(UnrealConfig, AndroidPackageName, SourceApkPath, FilesToInstall, Flags, PackageIs32Bit); DiscoveredBuilds.Add(NewBuild); Log.Verbose("Found {0} {1} build at {2}", UnrealConfig, ((Flags & BuildFlags.Bulk) == BuildFlags.Bulk) ? "(bulk)" : "(not bulk)", AbsPath); } // If we have both 32 and 64-bit builds, prefer 64-bit if (DiscoveredBuilds.Where(B => B.Is32Bit == false).Any()) { DiscoveredBuilds = DiscoveredBuilds.Where(B => !B.Is32Bit).ToList(); } return(DiscoveredBuilds); }
virtual protected bool ResolveBuildReference(string InBuildReference, Func <string, string> ResolutionDelegate, out string OutBuildPath, out string OutBuildName) { OutBuildName = null; OutBuildPath = null; if (string.IsNullOrEmpty(InBuildReference)) { return(false); } // BuildParam could be a path, a name that we should resolve to a path, Staged, or Editor DirectoryInfo BuildDir = new DirectoryInfo(InBuildReference); if (BuildDir.Exists) { // Easy option first - is this a full path? OutBuildName = BuildDir.Name; OutBuildPath = BuildDir.FullName; } else if (BuildDir.Name.Equals("staged", StringComparison.OrdinalIgnoreCase)) { string ProjectDir = Path.GetDirectoryName(UnrealHelpers.GetProjectPath(ProjectName)); if (string.IsNullOrEmpty(ProjectDir)) { throw new AutomationException("Could not find uproject for {0}.", ProjectName); } // First special case - "Staged" means use whats locally staged OutBuildName = "Staged"; OutBuildPath = Path.Combine(ProjectDir, "Saved", "StagedBuilds"); if (Directory.Exists(OutBuildPath) == false) { Log.Error("BuildReference was Staged but staged directory {0} not found", OutBuildPath); return(false); } } else if (BuildDir.Name.Equals("editor", StringComparison.OrdinalIgnoreCase)) { // Second special case - "Editor" means run using the editor, no path needed OutBuildName = "Editor"; OutBuildPath = Environment.CurrentDirectory; } else { // todo - make this more generic if (BuildDir.Name.Equals("usesyncedbuild", StringComparison.OrdinalIgnoreCase)) { BuildVersion Version; if (BuildVersion.TryRead(BuildVersion.GetDefaultFileName(), out Version)) { InBuildReference = Version.BranchName + "-CL-" + Version.Changelist.ToString(); } } // See if it's in the passed locations if (ResolutionDelegate != null) { string FullPath = ResolutionDelegate(InBuildReference); if (string.IsNullOrEmpty(FullPath) == false) { DirectoryInfo Di = new DirectoryInfo(FullPath); if (Di.Exists == false) { throw new AutomationException("Resolution delegate returned non existent path"); } OutBuildName = Di.Name; OutBuildPath = Di.FullName; } } } if (string.IsNullOrEmpty(OutBuildName) || string.IsNullOrEmpty(OutBuildPath)) { Log.Error("Unable to resolve build argument '{0}'", InBuildReference); return(false); } return(true); }