Esempio n. 1
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 7
0
        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;
		}
Esempio n. 10
0
        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);
        }