Esempio n. 1
0
        /// <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");
            }
        }
        virtual public UnrealAppConfig CreateConfiguration(UnrealSessionRole Role)
        {
            List <string> Issues = new List <string>();

            Log.Verbose("Creating configuration Role {0}", Role);
            if (!CanSupportRole(Role, ref Issues))
            {
                Issues.ForEach(S => Log.Error(S));
                return(null);
            }

            UnrealAppConfig Config = new UnrealAppConfig();

            Config.Name          = this.BuildName;
            Config.ProjectName   = ProjectName;
            Config.ProcessType   = Role.RoleType;
            Config.Platform      = Role.Platform;
            Config.Configuration = Role.Configuration;
            Config.CommandLine   = "";
            Config.FilesToCopy   = new List <UnrealFileToCopy>();

            // new system of retrieving and encapsulating the info needed to install/launch. Android & Mac
            Config.Build = GetMatchingBuilds(Role.RoleType, Role.Platform, Role.Configuration, Role.RequiredBuildFlags).FirstOrDefault();

            if (Config.Build == null && Role.IsNullRole() == false)
            {
                var SupportedBuilds = String.Join("\n", DiscoveredBuilds.Select(B => B.ToString()));

                Log.Info("Available builds:\n{0}", SupportedBuilds);
                throw new AutomationException("No build found that can support a role of {0}.", Role);
            }

            if (Role.Options != null)
            {
                Role.Options.ApplyToConfig(Config);
            }

            if (string.IsNullOrEmpty(Role.CommandLine) == false)
            {
                Config.CommandLine += " " + Role.CommandLine;
            }

            bool IsContentOnlyProject = (Config.Build.Flags & BuildFlags.ContentOnlyProject) == BuildFlags.ContentOnlyProject;

            // Add in editor - TODO, should this be in the editor build?
            if (Role.RoleType.UsesEditor() || IsContentOnlyProject)
            {
                string ProjectParam = ProjectName;

                // if content only we need to provide a relative path to the uproject.
                if (IsContentOnlyProject)
                {
                    ProjectParam = string.Format("../../../{0}/{0}.uproject", ProjectName);
                }

                // project must be first
                Config.CommandLine = ProjectParam + " " + Config.CommandLine;

                // add in -game or -server
                if (Role.RoleType.IsClient())
                {
                    Config.CommandLine += " -game";
                }
                else if (Role.RoleType.IsServer())
                {
                    Config.CommandLine += " -server";
                }
            }

            if (Role.FilesToCopy != null)
            {
                Config.FilesToCopy = Role.FilesToCopy;
            }

            return(Config);
        }
        protected IAppInstall InstallStagedBuild(UnrealAppConfig AppConfig, StagedBuild InBuild)
        {
            bool SkipDeploy = Globals.Params.ParseParam("SkipDeploy");

            string BuildPath = InBuild.BuildPath;

            if (CanRunFromPath(BuildPath) == false)
            {
                string SubDir   = string.IsNullOrEmpty(AppConfig.Sandbox) ? AppConfig.ProjectName : AppConfig.Sandbox;
                string DestPath = Path.Combine(this.TempDir, SubDir, AppConfig.ProcessType.ToString());

                if (!SkipDeploy)
                {
                    Log.Info("Installing {0} to {1}", AppConfig.Name, ToString());
                    Log.Verbose("\tCopying {0} to {1}", BuildPath, DestPath);
                    Gauntlet.Utils.SystemHelpers.CopyDirectory(BuildPath, DestPath, Utils.SystemHelpers.CopyOptions.Mirror);
                }
                else
                {
                    Log.Info("Skipping install of {0} (-skipdeploy)", BuildPath);
                }

                Utils.SystemHelpers.MarkDirectoryForCleanup(DestPath);

                BuildPath = DestPath;
            }

            WindowsAppInstall WinApp = new WindowsAppInstall(AppConfig.Name, AppConfig.ProjectName, this);

            WinApp.RunOptions = RunOptions;

            // Set commandline replace any InstallPath arguments with the path we use
            WinApp.CommandArguments = Regex.Replace(AppConfig.CommandLine, @"\$\(InstallPath\)", BuildPath, RegexOptions.IgnoreCase);

            if (string.IsNullOrEmpty(UserDir) == false)
            {
                WinApp.CommandArguments += string.Format(" -userdir=\"{0}\"", UserDir);
                WinApp.ArtifactPath      = Path.Combine(UserDir, @"Saved");

                Utils.SystemHelpers.MarkDirectoryForCleanup(UserDir);
            }
            else
            {
                // e.g d:\Unreal\GameName\Saved
                WinApp.ArtifactPath = Path.Combine(BuildPath, AppConfig.ProjectName, @"Saved");
            }

            // clear artifact path
            if (Directory.Exists(WinApp.ArtifactPath))
            {
                try
                {
                    Directory.Delete(WinApp.ArtifactPath, true);
                }
                catch (Exception Ex)
                {
                    Log.Warning("Failed to delete {0}. {1}", WinApp.ArtifactPath, Ex.Message);
                }
            }

            if (LocalDirectoryMappings.Count == 0)
            {
                PopulateDirectoryMappings(Path.Combine(BuildPath, AppConfig.ProjectName), UserDir);
            }

            if (AppConfig.FilesToCopy != null)
            {
                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;
                        string DirectoryToCopyTo = Path.GetDirectoryName(PathToCopyTo);
                        if (!Directory.Exists(DirectoryToCopyTo))
                        {
                            Directory.CreateDirectory(DirectoryToCopyTo);
                        }
                        if (File.Exists(PathToCopyTo))
                        {
                            FileInfo ExistingFile = new FileInfo(PathToCopyTo);
                            ExistingFile.IsReadOnly = false;
                        }
                        SrcInfo.CopyTo(PathToCopyTo, true);
                        Log.Info("Copying {0} to {1}", FileToCopy.SourceFileLocation, PathToCopyTo);
                    }
                    else
                    {
                        Log.Warning("File to copy {0} not found", FileToCopy);
                    }
                }
            }

            if (Path.IsPathRooted(InBuild.ExecutablePath))
            {
                WinApp.ExecutablePath = InBuild.ExecutablePath;
            }
            else
            {
                // TODO - this check should be at a higher level....
                string BinaryPath = Path.Combine(BuildPath, InBuild.ExecutablePath);

                // check for a local newer executable
                if (Globals.Params.ParseParam("dev") && AppConfig.ProcessType.UsesEditor() == false)
                {
                    string LocalBinary = Path.Combine(Environment.CurrentDirectory, InBuild.ExecutablePath);

                    bool LocalFileExists = File.Exists(LocalBinary);
                    bool LocalFileNewer  = LocalFileExists && File.GetLastWriteTime(LocalBinary) > File.GetLastWriteTime(BinaryPath);

                    Log.Verbose("Checking for newer binary at {0}", LocalBinary);
                    Log.Verbose("LocalFile exists: {0}. Newer: {1}", LocalFileExists, LocalFileNewer);

                    if (LocalFileExists && LocalFileNewer)
                    {
                        // need to -basedir to have our exe load content from the path
                        WinApp.CommandArguments += string.Format(" -basedir={0}", Path.GetDirectoryName(BinaryPath));

                        BinaryPath = LocalBinary;
                    }
                }

                WinApp.ExecutablePath = BinaryPath;
            }

            return(WinApp);
        }
        protected IAppInstall InstallStagedBuild(UnrealAppConfig AppConfig, StagedBuild InBuild)
        {
            bool SkipDeploy = Globals.Params.ParseParam("SkipDeploy");

            string BuildPath = InBuild.BuildPath;

            // Must be on our volume to run
            string BuildVolume = GetVolumeName(BuildPath);
            string LocalRoot   = GetVolumeName(Environment.CurrentDirectory);

            if (BuildVolume.Equals(LocalRoot, StringComparison.OrdinalIgnoreCase) == false)
            {
                string SubDir   = string.IsNullOrEmpty(AppConfig.Sandbox) ? AppConfig.ProjectName : AppConfig.Sandbox;
                string DestPath = Path.Combine(this.TempDir, SubDir, AppConfig.ProcessType.ToString());

                if (!SkipDeploy)
                {
                    Log.Info("Installing {0} to {1}", AppConfig.Name, ToString());
                    Log.Verbose("\tCopying {0} to {1}", BuildPath, DestPath);
                    Gauntlet.Utils.SystemHelpers.CopyDirectory(BuildPath, DestPath, Utils.SystemHelpers.CopyOptions.Mirror);
                }
                else
                {
                    Log.Info("Skipping install of {0} (-skipdeploy)", BuildPath);
                }

                Utils.SystemHelpers.MarkDirectoryForCleanup(DestPath);

                BuildPath = DestPath;
            }

            MacAppInstall MacApp = new MacAppInstall(AppConfig.Name, this);

            MacApp.LocalPath        = BuildPath;
            MacApp.WorkingDirectory = MacApp.LocalPath;
            MacApp.RunOptions       = RunOptions;

            // Set commandline replace any InstallPath arguments with the path we use
            MacApp.CommandArguments = Regex.Replace(AppConfig.CommandLine, @"\$\(InstallPath\)", BuildPath, RegexOptions.IgnoreCase);

            // Mac always forces this to stop logs and other artifacts going to different places
            // Mac always forces this to stop logs and other artifacts going to different places
            MacApp.CommandArguments += string.Format(" -userdir=\"{0}\"", UserDir);
            MacApp.ArtifactPath      = Path.Combine(UserDir, @"Saved");

            // temp - Mac doesn't support -userdir?
            //MacApp.ArtifactPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library/Logs", AppConfig.ProjectName);

            // clear artifact path
            if (Directory.Exists(MacApp.ArtifactPath))
            {
                try
                {
                    Directory.Delete(MacApp.ArtifactPath, true);
                }
                catch (Exception Ex)
                {
                    Log.Warning("Failed to delete {0}. {1}", MacApp.ArtifactPath, Ex.Message);
                }
            }

            // For Mac turn Foo.app into Foo/Content/MacOS/Foo
            string AppPath  = Path.GetDirectoryName(InBuild.ExecutablePath);
            string FileName = Path.GetFileNameWithoutExtension(InBuild.ExecutablePath);

            MacApp.ExecutablePath = Path.Combine(InBuild.ExecutablePath, "Contents", "MacOS", FileName);

            if (!Path.IsPathRooted(MacApp.ExecutablePath))
            {
                // TODO - this check should be at a higher level....
                string BinaryPath = Path.Combine(BuildPath, MacApp.ExecutablePath);

                // check for a local newer executable
                if (Globals.Params.ParseParam("dev") && AppConfig.ProcessType.UsesEditor() == false)
                {
                    string LocalBinary = Path.Combine(Environment.CurrentDirectory, MacApp.ExecutablePath);

                    bool LocalFileExists = File.Exists(LocalBinary);
                    bool LocalFileNewer  = LocalFileExists && File.GetLastWriteTime(LocalBinary) > File.GetLastWriteTime(BinaryPath);

                    Log.Verbose("Checking for newer binary at {0}", LocalBinary);
                    Log.Verbose("LocalFile exists: {0}. Newer: {1}", LocalFileExists, LocalFileNewer);

                    if (LocalFileExists && LocalFileNewer)
                    {
                        string LocalAppPath = Path.Combine(Environment.CurrentDirectory, InBuild.ExecutablePath);

                        // need to -basedir to have our exe load content from the path
                        MacApp.CommandArguments += string.Format(" -basedir={0}", Path.GetDirectoryName(LocalAppPath));

                        BinaryPath = LocalBinary;
                    }
                }

                MacApp.ExecutablePath = BinaryPath;
            }

            return(MacApp);
        }
Esempio n. 5
0
        /// <summary>
        /// When running parallel tests using staged builds, local desktop devices only need one copy of client/server
        /// this method is used to coordinate the copy across multiple local devices
        /// </summary>
        public static void InstallBuildParallel(UnrealAppConfig AppConfig, StagedBuild InBuild, string BuildPath, string DestPath, string Desc)
        {
            // In parallel tests, we only want to copy the client/server once
            bool          Install = false;
            InstallStatus Status  = InstallStatus.Error;

            lock (Globals.MainLock)
            {
                if (!LocalInstalls.ContainsKey(InBuild))
                {
                    Install = true;
                    LocalInstalls[InBuild] = Status = InstallStatus.Installing;
                }
                else
                {
                    Status = LocalInstalls[InBuild];
                }
            }

            // check if we've already errored in another thread
            if (Status == InstallStatus.Error)
            {
                throw new AutomationException("Parallel build error installing {0} to {1}", BuildPath, DestPath);
            }

            if (Install)
            {
                try
                {
                    Log.Info("Installing {0} to {1}", AppConfig.Name, Desc);
                    Log.Verbose("\tCopying {0} to {1}", BuildPath, DestPath);
                    Utils.SystemHelpers.CopyDirectory(BuildPath, DestPath, Utils.SystemHelpers.CopyOptions.Mirror);
                }
                catch (Exception Ex)
                {
                    lock (Globals.MainLock)
                    {
                        LocalInstalls[InBuild] = InstallStatus.Error;
                    }

                    throw Ex;
                }

                lock (Globals.MainLock)
                {
                    LocalInstalls[InBuild] = InstallStatus.Installed;
                }
            }
            else
            {
                DateTime StartTime = DateTime.Now;
                while (true)
                {
                    if ((DateTime.Now - StartTime).TotalMinutes > 60)
                    {
                        throw new AutomationException("Parallel build error installing {0} to {1}, timed out after an hour", BuildPath, DestPath);
                    }

                    Thread.Sleep(1000);

                    lock (Globals.MainLock)
                    {
                        Status = LocalInstalls[InBuild];
                    }

                    // install process failed in other thread, disk full, etc
                    if (Status == InstallStatus.Error)
                    {
                        throw new AutomationException("Error installing parallel build from {0} to {1}", BuildPath, DestPath);
                    }

                    if (Status == InstallStatus.Installed)
                    {
                        Log.Verbose("Parallel build successfully installed from {0} to {1}", BuildPath, DestPath);
                        break;
                    }
                }
            }
        }
        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);
        }