Beispiel #1
0
        /**
         * This function should be used as the ActionHandler delegate method for Actions that
         * need to run over RPCUtility. It will block until the remote command completes
         */
        static public void RPCActionHandler(Action Action, out int ExitCode, out string Output)
        {
            Hashtable Results = RPCUtilHelper.Command(Action.WorkingDirectory, Action.CommandPath, Action.CommandArguments,
                                                      Action.ProducedItems.Count > 0 ? Action.ProducedItems[0].AbsolutePath : null);

            if (Results == null)
            {
                ExitCode = -1;
                Output   = null;
                Log.TraceInformation("Command failed to execute! {0} {1}", Action.CommandPath, Action.CommandArguments);
            }
            else
            {
                // capture the exit code
                if (Results["ExitCode"] != null)
                {
                    ExitCode = (int)(Int64)Results["ExitCode"];
                }
                else
                {
                    ExitCode = 0;
                }

                // pass back the string
                Output = Results["CommandOutput"] as string;
            }
        }
Beispiel #2
0
        public override int GetMaxActionsToExecuteInParallel()
        {
            Int32 RemoteCPUCount = RPCUtilHelper.GetCommandSlots();

            if (RemoteCPUCount == 0)
            {
                RemoteCPUCount = Environment.ProcessorCount;
            }
            return(RemoteCPUCount);
        }
Beispiel #3
0
        public RemoteExecutor()
        {
            Int32 RemoteCPUCount = RPCUtilHelper.GetCommandSlots();

            if (RemoteCPUCount == 0)
            {
                RemoteCPUCount = Environment.ProcessorCount;
            }

            AdjustedProcessorCountMultiplierValue = (Double)RemoteCPUCount / (Double)Environment.ProcessorCount;
            Log.TraceVerbose("Adjusting the remote Mac compile process multiplier to " + AdjustedProcessorCountMultiplierValue.ToString());
        }
Beispiel #4
0
        static public Double GetAdjustedProcessorCountMultiplier()
        {
            if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
            {
                Int32 RemoteCPUCount = RPCUtilHelper.GetCommandSlots();
                if (RemoteCPUCount == 0)
                {
                    RemoteCPUCount = Environment.ProcessorCount;
                }

                Double AdjustedMultiplier = (Double)RemoteCPUCount / (Double)Environment.ProcessorCount;
                Log.TraceVerbose("Adjusting the remote Mac compile process multiplier to " + AdjustedMultiplier.ToString());
                return(AdjustedMultiplier);
            }
            else
            {
                return(1.0);
            }
        }
Beispiel #5
0
        // Gather a users root path from the remote server. Should only be called once.
        public static void SetUserDevRootFromServer()
        {
            if (!bUseRPCUtil)
            {
                // Only set relative to the users root when using rsync, for now
                Hashtable Results = RPCUtilHelper.Command("/", "echo $HOME", null);

                if (Results == null)
                {
                    Log.TraceInformation("UserDevRoot Command failed to execute!");
                }
                else if (Results["CommandOutput"] != null)
                {
                    // pass back the string
                    string HomeLocation = Results["CommandOutput"] as string;
                    UserDevRootMac = HomeLocation + UserDevRootMac;
                }
            }
        }
        private static void SelectXcode(ref string DeveloperDir, bool bVerbose)
        {
            string Reason = "hardcoded";

            if (DeveloperDir == "xcode-select")
            {
                Reason = "xcode-select";

                if (Utils.IsRunningOnMono)
                {
                    // on the Mac, run xcode-select directly
                    DeveloperDir = Utils.RunLocalProcessAndReturnStdOut("xcode-select", "--print-path");

                    // make sure we get a full path
                    if (Directory.Exists(DeveloperDir) == false)
                    {
                        throw new BuildException("Selected Xcode ('{0}') doesn't exist, cannot continue.", DeveloperDir);
                    }
                }
                else
                {
                    Hashtable Results = RPCUtilHelper.Command("/", "xcode-select", "--print-path", null);
                    if (Results != null)
                    {
                        DeveloperDir = (string)Results["CommandOutput"];
                        DeveloperDir = DeveloperDir.TrimEnd();
                    }
                }

                if (DeveloperDir.EndsWith("/") == false)
                {
                    // we expect this to end with a slash
                    DeveloperDir += "/";
                }
            }

            if (bVerbose && !DeveloperDir.StartsWith("/Applications/Xcode.app"))
            {
                Log.TraceInformationOnce("Compiling with non-standard Xcode ({0}): {1}", Reason, DeveloperDir);
            }
        }
Beispiel #7
0
 /// <summary>
 /// Resolve any outstanding remote file info lookups
 /// </summary>
 private void LookupOutstandingFiles()
 {
     // for remote files, look up any outstanding files
     if (bIsRemoteFile)
     {
         FileItem[] Files = null;
         lock (DelayedRemoteLookupFiles)
         {
             if (DelayedRemoteLookupFiles.Count > 0)
             {
                 // make an array so we can clear the original array, just in case BatchFileInfo does something that uses
                 // DelayedRemoteLookupFiles, so we don't deadlock
                 Files = DelayedRemoteLookupFiles.ToArray();
                 DelayedRemoteLookupFiles.Clear();
             }
         }
         if (Files != null)
         {
             RPCUtilHelper.BatchFileInfo(Files);
         }
     }
 }
Beispiel #8
0
        // Gather a users root path from the remote server. Should only be called once.
        public static void SetUserDevRootFromServer()
        {
            if (!bUseRPCUtil && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac && !UEBuildConfiguration.bListBuildFolders)
            {
                // Only set relative to the users root when using rsync, for now
                Hashtable Results = RPCUtilHelper.Command("/", "echo $HOME", null);

                if (Results == null)
                {
                    Log.TraceInformation("UserDevRoot Command failed to execute!");
                }
                else if (Results["CommandOutput"] != null)
                {
                    // pass back the string
                    string HomeLocation = Results["CommandOutput"] as string;
                    UserDevRootMac = HomeLocation + UserDevRootMacBase;
                }
            }
            else
            {
                UserDevRootMac = UserDevRootMacBase;
            }
        }
Beispiel #9
0
        public override void PreBuildSync()
        {
            // no need to sync on the Mac!
            if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
            {
                return;
            }

            if (bUseRPCUtil)
            {
                string ExtString = "";

                // look only for useful extensions
                foreach (string Ext in RsyncExtensions)
                {
                    // for later ls
                    ExtString += Ext.StartsWith(".") ? ("*" + Ext) : Ext;
                    ExtString += " ";
                }

                List <string> BatchUploadCommands = new List <string>();
                // for each directory we visited, add all the files in that directory
                foreach (string Dir in RsyncDirs)
                {
                    List <string> LocalFilenames = new List <string>();

                    if (!Directory.Exists(Dir))
                    {
                        Directory.CreateDirectory(Dir);
                    }

                    // look only for useful extensions
                    foreach (string Ext in RsyncExtensions)
                    {
                        string[] Files = Directory.GetFiles(Dir, "*" + Ext);
                        foreach (string SyncFile in Files)
                        {
                            // remember all local files
                            LocalFilenames.Add(Path.GetFileName(SyncFile));

                            string RemoteFilePath = ConvertPath(SyncFile);
                            // an upload command is local name and remote name
                            BatchUploadCommands.Add(SyncFile + ";" + RemoteFilePath);
                        }
                    }
                }

                // batch upload
                RPCUtilHelper.BatchUpload(BatchUploadCommands.ToArray());
            }
            else
            {
                List <string> RelativeRsyncDirs = new List <string>();
                foreach (string Dir in RsyncDirs)
                {
                    RelativeRsyncDirs.Add(Utils.CleanDirectorySeparators(Dir.Replace(":", ""), '/') + "/");
                }

                // write out directories to copy
                string RSyncPathsFile  = Path.GetTempFileName();
                string IncludeFromFile = Path.GetTempFileName();
                File.WriteAllLines(RSyncPathsFile, RelativeRsyncDirs.ToArray());
                File.WriteAllLines(IncludeFromFile, RsyncExtensions);

                // source and destination paths in the format rsync wants
                string CygRootPath = "/cygdrive";                // ConvertPathToCygwin(Path.GetFullPath(""));
                string RemotePath  = string.Format("{0}{1}",
                                                   UserDevRootMac,
                                                   Environment.MachineName);

                // get the executable dir for SSH, so Rsync can call it easily
                string ExeDir = Path.GetDirectoryName(ResolvedSSHExe);

                Process RsyncProcess = new Process();
                if (ExeDir != "")
                {
                    RsyncProcess.StartInfo.WorkingDirectory = ExeDir;
                }

                // --exclude='*'  ??? why???
                RsyncProcess.StartInfo.FileName  = ResolvedRSyncExe;
                RsyncProcess.StartInfo.Arguments = string.Format(
                    "-vzae \"{0}\" --rsync-path=\"mkdir -p {2} && rsync\" --chmod=ug=rwX,o=rxX --delete --files-from=\"{4}\" --include-from=\"{5}\" --include='*/' --exclude='*.o' --exclude='Timestamp' '{1}' {6}@{3}:'{2}'",
                    ResolvedRsyncAuthentication,
                    CygRootPath,
                    RemotePath,
                    RemoteServerName,
                    ConvertPathToCygwin(RSyncPathsFile),
                    ConvertPathToCygwin(IncludeFromFile),
                    RSyncUsername);
                Console.WriteLine("Command: " + RsyncProcess.StartInfo.Arguments);

                RsyncProcess.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedForRsync);
                RsyncProcess.ErrorDataReceived  += new DataReceivedEventHandler(OutputReceivedForRsync);

                // run rsync
                Utils.RunLocalProcess(RsyncProcess);

                File.Delete(IncludeFromFile);
                File.Delete(RSyncPathsFile);
            }

            // we can now clear out the set of files
            RsyncDirs.Clear();
            RsyncExtensions.Clear();
        }
Beispiel #10
0
        private RemoteToolChainErrorCode InitializeRemoteExecution()
        {
            if (bHasBeenInitialized)
            {
                return(InitializationErrorCode);
            }

            // don't need to set up the remote environment if we're simply listing build folders.
            if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac && !UEBuildConfiguration.bListBuildFolders)
            {
                // If we don't care which machine we're going to build on, query and
                // pick the one with the most free command slots available
                if (RemoteServerName == "best_available")
                {
                    int AvailableSlots = 0;
                    int Attempts       = 0;
                    if (!ProjectFileGenerator.bGenerateProjectFiles)
                    {
                        Log.TraceInformation("Picking a random Mac builder...");
                    }
                    while (AvailableSlots < 2 && Attempts < 20)
                    {
                        RemoteServerName = PotentialServerNames.OrderBy(x => Guid.NewGuid()).FirstOrDefault();

                        // make sure it's ready to take commands
                        AvailableSlots = GetAvailableCommandSlotCount(RemoteServerName);

                        Attempts++;
                    }

                    // make sure it succeeded
                    if (AvailableSlots <= 1)
                    {
                        throw new BuildException("Failed to find a Mac available to take commands!");
                    }
                    else if (!ProjectFileGenerator.bGenerateProjectFiles)
                    {
                        Log.TraceInformation("Chose {0} after {1} attempts to find a Mac, with {2} slots", RemoteServerName, Attempts, AvailableSlots);
                    }

                    /*
                     * this does not work right, because it pushes a lot of tasks to machines that have substantially more slots than others
                     *                                      Log.TraceInformation("Picking the best available Mac builder...");
                     *                                      Int32 MostAvailableCount = Int32.MinValue;
                     *                                      foreach (string NextMacName in PotentialServerNames)
                     *                                      {
                     *                                              Int32 NextAvailableCount = GetAvailableCommandSlotCount(NextMacName);
                     *                                              if (NextAvailableCount > MostAvailableCount)
                     *                                              {
                     *                                                      MostAvailableName = NextMacName;
                     *                                                      MostAvailableCount = NextAvailableCount;
                     *                                              }
                     *
                     *                                              Log.TraceVerbose("... " + NextMacName + " has " + NextAvailableCount + " slots available");
                     *                                      }
                     *                                      Log.TraceVerbose("Picking the compile server with the most available command slots: " + MostAvailableName);
                     *
                     *                                      // Finally, assign the name of the Mac we're going to use
                     *                                      RemoteServerName = MostAvailableName;
                     */
                }
                else if (!ProjectFileGenerator.bGenerateProjectFiles)
                {
                    Log.TraceInformation("Picking the default remote server " + RemoteServerName);
                }

                // we need a server name!
                if (string.IsNullOrEmpty(RemoteServerName))
                {
                    Log.TraceError("Remote compiling requires a server name. Use the editor (Project Settings, IOS) to set up your remote compilation settings.");
                    return(RemoteToolChainErrorCode.ServerNameNotSpecified);
                }

                // Split port out from RemoteServerName
                String[] RemoteServerNameSplit = RemoteServerName.Split(':');
                if (RemoteServerNameSplit.Length > 1)
                {
                    if (RemoteServerNameSplit.Length != 2)
                    {
                        Log.TraceError("Remote compiling server name contains too many colons.");
                        return(RemoteToolChainErrorCode.ServerNameNotSpecified);
                    }

                    RemoteServerName = RemoteServerNameSplit[0];
                    RemoteServerPort = Convert.ToInt32(RemoteServerNameSplit[1]);
                }

                if (!bUseRPCUtil)
                {
                    // Verify the Delta Copy install path
                    ResolvedRSyncExe = ResolveString(RSyncExe, true);
                    ResolvedSSHExe   = ResolveString(SSHExe, true);

                    if (!File.Exists(ResolvedRSyncExe) || !File.Exists(ResolvedSSHExe))
                    {
                        Log.TraceError("Remote compiling requires Delta Copy to be installed. Use the editor (Project Settings, IOS) to set up your remote compilation settings.");
                        return(RemoteToolChainErrorCode.MissingDeltaCopyInstall);
                    }

                    // we need the RemoteServerName and the Username to find the private key
                    ResolvedRSyncUsername = ResolveString(RSyncUsername, false);
                    if (string.IsNullOrEmpty(ResolvedRSyncUsername))
                    {
                        Log.TraceError("Remote compiling requires a user name. Use the editor (Project Settings, IOS) to set up your remote compilation settings.");
                        return(RemoteToolChainErrorCode.MissingRemoteUserName);
                    }

                    bool bFoundOverrideSSHPrivateKey = false;

                    // if the override path is set, just use it directly
                    if (!string.IsNullOrEmpty(SSHPrivateKeyOverridePath))
                    {
                        ResolvedSSHPrivateKey = ResolveString(SSHPrivateKeyOverridePath, true);

                        bFoundOverrideSSHPrivateKey = File.Exists(ResolvedSSHPrivateKey);

                        // make sure it exists
                        if (!bFoundOverrideSSHPrivateKey)
                        {
                            Log.TraceWarning("An SSHKey override was specified [" + SSHPrivateKeyOverridePath + "] but it doesn't exist. Looking elsewhere...");
                        }
                    }

                    if (!bFoundOverrideSSHPrivateKey)
                    {
                        // all the places to look for a key
                        List <string> Locations = new List <string>();
                        Locations.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Unreal Engine", "UnrealBuildTool"));
                        Locations.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Unreal Engine", "UnrealBuildTool"));
                        if (ProjectFile != null)
                        {
                            Locations.Add(Path.Combine(ProjectFile.Directory.FullName, "Build", "NotForLicensees"));
                            Locations.Add(Path.Combine(ProjectFile.Directory.FullName, "Build", "NoRedist"));
                            Locations.Add(Path.Combine(ProjectFile.Directory.FullName, "Build"));
                        }
                        Locations.Add(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NotForLicensees"));
                        Locations.Add(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NoRedist"));
                        Locations.Add(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build"));

                        // look for a key file
                        foreach (string Location in Locations)
                        {
                            string KeyPath = Path.Combine(Location, "SSHKeys", RemoteServerName, ResolvedRSyncUsername, "RemoteToolChainPrivate.key");
                            if (File.Exists(KeyPath))
                            {
                                ResolvedSSHPrivateKey       = KeyPath;
                                bFoundOverrideSSHPrivateKey = true;
                                break;
                            }
                        }
                    }

                    // resolve the rest of the strings
                    ResolvedRsyncAuthentication = ResolveString(RsyncAuthentication, false) + " -p " + RemoteServerPort;
                    ResolvedSSHAuthentication   = ResolveString(SSHAuthentication, false) + " -p " + RemoteServerPort;
                }

                // start up remote communication and record if it succeeds
                InitializationErrorCode = (RemoteToolChainErrorCode)RPCUtilHelper.Initialize(RemoteServerName);
                if (InitializationErrorCode != RemoteToolChainErrorCode.NoError && InitializationErrorCode != RemoteToolChainErrorCode.MissingSSHKey)
                {
                    Log.TraceError("Failed to initialize a connection to the Remote Server {0}", RemoteServerName);
                    return(InitializationErrorCode);
                }
                else if (InitializationErrorCode == RemoteToolChainErrorCode.MissingSSHKey)
                {
                    // Allow the user to set up a key from here.
                    Process KeyProcess = new Process();
                    KeyProcess.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "BatchFiles"));
                    KeyProcess.StartInfo.FileName         = "MakeAndInstallSSHKey.bat";
                    KeyProcess.StartInfo.Arguments        = string.Format(
                        "\"{0}\" {1} \"{2}\" {3} {4} \"{5}\" \"{6}\" \"{7}\"",
                        ResolvedSSHExe,
                        RemoteServerPort,
                        ResolvedRSyncExe,
                        ResolvedRSyncUsername,
                        RemoteServerName,
                        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                        ConvertPathToCygwin(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)),
                        Path.GetFullPath(BuildConfiguration.RelativeEnginePath));

                    KeyProcess.Start();
                    KeyProcess.WaitForExit();

                    // make sure it succeeded if we want to re-init
                    if (KeyProcess.ExitCode == 0)
                    {
                        InitializationErrorCode = InitializeRemoteExecution();
                    }
                }
            }
            else
            {
                RemoteServerName = Environment.MachineName;
                // can't error in this case
            }

            bHasBeenInitialized = true;
            return(InitializationErrorCode);
        }
Beispiel #11
0
        /// <summary>
        /// The actual function to run in a thread. This is potentially long and blocking
        /// </summary>
        private void ThreadFunc()
        {
            // thread start time
            Action.StartTime = DateTimeOffset.Now;

            if (Action.ActionHandler != null)
            {
                // call the function and get the ExitCode and an output string
                string Output;
                Action.ActionHandler(Action, out ExitCode, out Output);

                // Output status description (file name) when no output is returned
                if (string.IsNullOrEmpty(Output))
                {
                    if (Action.bShouldOutputStatusDescription)
                    {
                        Output = string.Format("[{0}/{1}] {2} {3}", JobNumber, TotalJobs, Action.CommandDescription, Action.StatusDescription);
                    }
                    else
                    {
                        Output = Action.StatusDescription;
                    }
                }

                SendOutputToEventHandler(Action, Output);
            }
            else
            {
                // batch files (.bat, .cmd) need to be run via or cmd /c or shellexecute,
                // the latter which we can't use because we want to redirect input/output
                bool bLaunchViaCmdExe = !Utils.IsRunningOnMono && !Path.GetExtension(Action.CommandPath).ToLower().EndsWith("exe");

                // Create the action's process.
                ProcessStartInfo ActionStartInfo = new ProcessStartInfo();
                ActionStartInfo.WorkingDirectory = ExpandEnvironmentVariables(Action.WorkingDirectory);


                string ExpandedCommandPath = ExpandEnvironmentVariables(Action.CommandPath);
                if (bLaunchViaCmdExe)
                {
                    ActionStartInfo.FileName  = "cmd.exe";
                    ActionStartInfo.Arguments = string.Format
                                                (
                        "/c \"{0} {1}\"",
                        ExpandEnvironmentVariables(ExpandedCommandPath),
                        ExpandEnvironmentVariables(Action.CommandArguments)
                                                );
                }
                else
                {
                    ActionStartInfo.FileName  = ExpandedCommandPath;
                    ActionStartInfo.Arguments = ExpandEnvironmentVariables(Action.CommandArguments);
                }

                ActionStartInfo.UseShellExecute        = false;
                ActionStartInfo.RedirectStandardInput  = false;
                ActionStartInfo.RedirectStandardOutput = false;
                ActionStartInfo.RedirectStandardError  = false;

                // Log command-line used to execute task if debug info printing is enabled.
                if (UnrealBuildTool.bPrintDebugInfo)
                {
                    Log.TraceVerbose("Executing: {0} {1}", ExpandedCommandPath, ActionStartInfo.Arguments);
                }
                // Log summary if wanted.
                else if (Action.bShouldOutputStatusDescription)
                {
                    string CommandDescription = Action.CommandDescription != null ? Action.CommandDescription : Path.GetFileName(ExpandedCommandPath);
                    if (string.IsNullOrEmpty(CommandDescription))
                    {
                        Log.TraceInformation(Action.StatusDescription);
                    }
                    else
                    {
                        Log.TraceInformation("[{0}/{1}] {2} {3}", JobNumber, TotalJobs, CommandDescription, Action.StatusDescription);
                    }
                }

                // Try to launch the action's process, and produce a friendly error message if it fails.
                Process ActionProcess = null;
                try
                {
                    try
                    {
                        ActionProcess           = new Process();
                        ActionProcess.StartInfo = ActionStartInfo;
                        bool bShouldRedirectOuput = Action.OutputEventHandler != null || Action.bPrintDebugInfo;
                        if (bShouldRedirectOuput)
                        {
                            ActionStartInfo.RedirectStandardOutput = true;
                            ActionStartInfo.RedirectStandardError  = true;
                            ActionProcess.EnableRaisingEvents      = true;

                            if (Action.OutputEventHandler != null)
                            {
                                ActionProcess.OutputDataReceived += Action.OutputEventHandler;
                                ActionProcess.ErrorDataReceived  += Action.OutputEventHandler;
                            }

                            if (Action.bPrintDebugInfo)
                            {
                                ActionProcess.OutputDataReceived += new DataReceivedEventHandler(ActionDebugOutput);
                                ActionProcess.ErrorDataReceived  += new DataReceivedEventHandler(ActionDebugOutput);
                            }
                        }
                        ActionProcess.Start();
                        if (bShouldRedirectOuput)
                        {
                            ActionProcess.BeginOutputReadLine();
                            ActionProcess.BeginErrorReadLine();
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new BuildException(ex, "Failed to start local process for action: {0} {1}\r\n{2}", Action.CommandPath, Action.CommandArguments, ex.ToString());
                    }

                    // wait for process to start
                    // NOTE: this may or may not be necessary; seems to depend on whether the system UBT is running on start the process in a timely manner.
                    int  checkIterations       = 0;
                    bool haveConfiguredProcess = false;
                    do
                    {
                        if (ActionProcess.HasExited)
                        {
                            if (haveConfiguredProcess == false)
                            {
                                Debug.WriteLine("Process for action exited before able to configure!");
                            }
                            break;
                        }

                        Thread.Sleep(100);

                        if (!haveConfiguredProcess)
                        {
                            try
                            {
                                ActionProcess.PriorityClass = ProcessPriorityClass.BelowNormal;
                                haveConfiguredProcess       = true;
                            }
                            catch (Exception)
                            {
                            }
                            break;
                        }

                        checkIterations++;
                    } while (checkIterations < 10);
                    if (checkIterations == 10)
                    {
                        throw new BuildException("Failed to configure local process for action: {0} {1}", Action.CommandPath, Action.CommandArguments);
                    }

                    // block until it's complete
                    // @todo iosmerge: UBT had started looking at:	if (Utils.IsValidProcess(Process))
                    //    do we need to check that in the thread model?
                    while (!ActionProcess.HasExited)
                    {
                        Thread.Sleep(10);
                    }

                    // capture exit code
                    ExitCode = ActionProcess.ExitCode;
                }
                finally
                {
                    // As the process has finished now, free its resources. On non-Windows platforms, processes depend
                    // on POSIX/BSD threading and these are limited per application. Disposing the Process releases
                    // these thread resources.
                    if (ActionProcess != null)
                    {
                        ActionProcess.Close();
                    }
                }
            }

            // track how long it took
            Action.EndTime = DateTimeOffset.Now;

            if (!Utils.IsRunningOnMono)
            {
                // let RPCUtilHelper clean up anything thread related
                RPCUtilHelper.OnThreadComplete();
            }

            // we are done!!
            bComplete = true;
        }
        protected static void SelectSDK(string BaseSDKDir, string OSPrefix, ref string PlatformSDKVersion, bool bVerbose)
        {
            if (PlatformSDKVersion == "latest")
            {
                PlatformSDKVersion = "";
                try
                {
                    string[] SubDirs = null;
                    if (Utils.IsRunningOnMono)
                    {
                        // on the Mac, we can just get the directory name
                        SubDirs = System.IO.Directory.GetDirectories(BaseSDKDir);
                    }
                    else
                    {
                        Hashtable Results = RPCUtilHelper.Command("/", "ls", BaseSDKDir, null);
                        if (Results != null)
                        {
                            string Result = (string)Results["CommandOutput"];
                            SubDirs = Result.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        }
                    }

                    // loop over the subdirs and parse out the version
                    int    MaxSDKVersionMajor  = 0;
                    int    MaxSDKVersionMinor  = 0;
                    string MaxSDKVersionString = null;
                    foreach (string SubDir in SubDirs)
                    {
                        string SubDirName = Path.GetFileNameWithoutExtension(SubDir);
                        if (SubDirName.StartsWith(OSPrefix))
                        {
                            // get the SDK version from the directory name
                            string SDKString = SubDirName.Replace(OSPrefix, "");
                            int    Major     = 0;
                            int    Minor     = 0;

                            // parse it into whole and fractional parts (since 10.10 > 10.9 in versions, but not in math)
                            try
                            {
                                string[] Tokens = SDKString.Split(".".ToCharArray());
                                if (Tokens.Length == 2)
                                {
                                    Major = int.Parse(Tokens[0]);
                                    Minor = int.Parse(Tokens[1]);
                                }
                            }
                            catch (Exception)
                            {
                                // weirdly formatted SDKs
                                continue;
                            }

                            // update largest SDK version number
                            if (Major > MaxSDKVersionMajor || (Major == MaxSDKVersionMajor && Minor > MaxSDKVersionMinor))
                            {
                                MaxSDKVersionString = SDKString;
                                MaxSDKVersionMajor  = Major;
                                MaxSDKVersionMinor  = Minor;
                            }
                        }
                    }

                    // use the largest version
                    if (MaxSDKVersionString != null)
                    {
                        PlatformSDKVersion = MaxSDKVersionString;
                    }
                }
                catch (Exception Ex)
                {
                    // on any exception, just use the backup version
                    Log.TraceInformation("Triggered an exception while looking for SDK directory in Xcode.app");
                    Log.TraceInformation("{0}", Ex.ToString());
                }
            }

            // make sure we have a valid SDK directory
            if (Utils.IsRunningOnMono && !Directory.Exists(Path.Combine(BaseSDKDir, OSPrefix + PlatformSDKVersion + ".sdk")))
            {
                throw new BuildException("Invalid SDK {0}{1}.sdk, not found in {2}", OSPrefix, PlatformSDKVersion, BaseSDKDir);
            }

            if (bVerbose && !ProjectFileGenerator.bGenerateProjectFiles)
            {
                if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
                {
                    Log.TraceInformation("Compiling with {0} SDK {1} on Mac {2}", OSPrefix, PlatformSDKVersion, RemoteServerName);
                }
                else
                {
                    Log.TraceInformation("Compiling with {0} SDK {1}", OSPrefix, PlatformSDKVersion);
                }
            }
        }
Beispiel #13
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="ProjectFile"></param>
        /// <param name="Executable"></param>
        /// <param name="StageDirectory"></param>
        /// <param name="PlatformType"></param>
        public static void GenerateAssetCatalog(FileReference ProjectFile, string Executable, string StageDirectory, UnrealTargetPlatform PlatformType)
        {
            // Initialize the toolchain.
            IOSProjectSettings ProjectSettings = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(PlatformType)).ReadProjectSettings(null);
            IOSToolChain       ToolChain       = new IOSToolChain(ProjectFile, ProjectSettings);

            // Determine whether the user has modified icons that require a remote Mac to build.
            CppPlatform Platform         = PlatformType == UnrealTargetPlatform.IOS ? CppPlatform.IOS : CppPlatform.TVOS;
            bool        bUserImagesExist = false;

            ToolChain.GenerateAssetCatalog(Platform, ref bUserImagesExist);

            // Don't attempt to do anything remotely if the user is using the default UE4 images.
            if (!bUserImagesExist)
            {
                return;
            }

            // Also don't attempt to use a remote Mac if packaging for TVOS on PC.
            if (Platform == CppPlatform.TVOS && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
            {
                return;
            }

            // Save off the current bUseRPCUtil setting to restore at the end of this function.
            // At this time, iPhonePackager needs to be called with bUseRPCUtil == true.
            bool bSaveUseRPCUtil = RemoteToolChain.bUseRPCUtil;

            // Initialize the remote calling environment, taking into account the user's SSH setting.
            ToolChain.SetUpGlobalEnvironment(false);

            // Build the asset catalog ActionGraph.
            ActionGraph     ActionGraph = new ActionGraph();
            List <FileItem> OutputFiles = new List <FileItem>();

            ToolChain.CompileAssetCatalog(FileItem.GetItemByPath(Executable), Platform, ActionGraph, OutputFiles);

            ActionGraph.FinalizeActionGraph();

            // I'm not sure how to derive the UE4Game and Development arguments programmatically.
            string[] Arguments = new string[] { "UE4Game", (PlatformType == UnrealTargetPlatform.IOS ? "IOS" : "TVOS"), "Development", "-UniqueBuildEnvironment" };

            // Perform all of the setup necessary to actually execute the ActionGraph instance.
            ReadOnlyBuildVersion Version        = new ReadOnlyBuildVersion(BuildVersion.ReadDefault());
            List <string[]>      TargetSettings = new List <string[]>();

            TargetSettings.Add(Arguments);
            var Targets = new List <UEBuildTarget>();
            Dictionary <UEBuildTarget, CPPHeaders> TargetToHeaders = new Dictionary <UEBuildTarget, CPPHeaders>();
            List <TargetDescriptor> TargetDescs = new List <TargetDescriptor>();

            foreach (string[] TargetSetting in TargetSettings)
            {
                TargetDescs.AddRange(TargetDescriptor.ParseCommandLine(TargetSetting, ref ProjectFile));
            }
            foreach (TargetDescriptor TargetDesc in TargetDescs)
            {
                UEBuildTarget Target = UEBuildTarget.CreateTarget(TargetDesc, Arguments, false, Version);
                if (Target == null)
                {
                    continue;
                }
                Targets.Add(Target);
                TargetToHeaders.Add(Target, null);
            }

            bool bIsRemoteCompile = BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac;

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            CommandLine.ParseArguments(Arguments, BuildConfiguration);
            BuildConfiguration.bUseUBTMakefiles = false;

            Action[] PrerequisiteActions;
            {
                HashSet <Action> PrerequisiteActionsSet = new HashSet <Action>();
                foreach (FileItem OutputFile in OutputFiles)
                {
                    ActionGraph.GatherPrerequisiteActions(OutputFile, ref PrerequisiteActionsSet);
                }
                PrerequisiteActions = PrerequisiteActionsSet.ToArray();
            }

            // Copy any asset catalog files to the remote Mac, if necessary.
            foreach (UEBuildTarget Target in Targets)
            {
                UEBuildPlatform.GetBuildPlatform(Target.Platform).PreBuildSync();
            }

            // Begin execution of the ActionGraph.
            Dictionary <UEBuildTarget, List <FileItem> > TargetToOutdatedPrerequisitesMap;
            List <Action> ActionsToExecute = ActionGraph.GetActionsToExecute(BuildConfiguration, PrerequisiteActions, Targets, TargetToHeaders, true, true, out TargetToOutdatedPrerequisitesMap);
            string        ExecutorName     = "Unknown";
            bool          bSuccess         = ActionGraph.ExecuteActions(BuildConfiguration, ActionsToExecute, bIsRemoteCompile, out ExecutorName, "", EHotReload.Disabled);

            if (bSuccess)
            {
                if (bIsRemoteCompile)
                {
                    // Copy the remotely built AssetCatalog directory locally.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string   RemoteDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        FileItem LocalExecutable = ToolChain.RemoteToLocalFileItem(FileItem.GetItemByPath(Executable));
                        string   LocalDirectory  = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(LocalExecutable.AbsolutePath), "AssetCatalog");
                        LocalDirectory = StageDirectory;
                        RPCUtilHelper.CopyDirectory(RemoteDirectory, LocalDirectory, RPCUtilHelper.ECopyOptions.DoNotReplace);
                    }
                }
                else
                {
                    // Copy the built AssetCatalog directory to the StageDirectory.
                    foreach (FileItem OutputFile in OutputFiles)
                    {
                        string SourceDirectory = System.IO.Path.GetDirectoryName(OutputFile.AbsolutePath).Replace("\\", "/");
                        System.IO.DirectoryInfo SourceDirectoryInfo = new System.IO.DirectoryInfo(SourceDirectory);
                        if (!System.IO.Directory.Exists(StageDirectory))
                        {
                            System.IO.Directory.CreateDirectory(StageDirectory);
                        }
                        System.IO.FileInfo[] SourceFiles = SourceDirectoryInfo.GetFiles();
                        foreach (System.IO.FileInfo SourceFile in SourceFiles)
                        {
                            string DestinationPath = System.IO.Path.Combine(StageDirectory, SourceFile.Name);
                            SourceFile.CopyTo(DestinationPath, true);
                        }
                    }
                }
            }

            // Restore the former bUseRPCUtil setting.
            RemoteToolChain.bUseRPCUtil = bSaveUseRPCUtil;
        }
        private static int InitializeRemoteExecution()
        {
            if (bHasBeenInitialized)
            {
                return(InitializationErrorCode);
            }

            if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac)
            {
                // If we don't care which machine we're going to build on, query and
                // pick the one with the most free command slots available
                if (RemoteServerName == "best_available")
                {
                    int AvailableSlots = 0;
                    int Attempts       = 0;
                    if (!ProjectFileGenerator.bGenerateProjectFiles)
                    {
                        Log.TraceInformation("Picking a random Mac builder...");
                    }
                    while (AvailableSlots < 2 && Attempts < 20)
                    {
                        RemoteServerName = PotentialServerNames.OrderBy(x => Guid.NewGuid()).FirstOrDefault();

                        // make sure it's ready to take commands
                        AvailableSlots = GetAvailableCommandSlotCount(RemoteServerName);

                        Attempts++;
                    }

                    // make sure it succeeded
                    if (AvailableSlots <= 1)
                    {
                        throw new BuildException("Failed to find a Mac available to take commands!");
                    }
                    else if (!ProjectFileGenerator.bGenerateProjectFiles)
                    {
                        Log.TraceInformation("Chose {0} after {1} attempts to find a Mac, with {2} slots", RemoteServerName, Attempts, AvailableSlots);
                    }

/*
 * this does not work right, because it pushes a lot of tasks to machines that have substantially more slots than others
 *                                      Log.TraceInformation("Picking the best available Mac builder...");
 *                      Int32 MostAvailableCount = Int32.MinValue;
 *                                      foreach (string NextMacName in PotentialServerNames)
 *                      {
 *                      Int32 NextAvailableCount = GetAvailableCommandSlotCount(NextMacName);
 *                      if (NextAvailableCount > MostAvailableCount)
 *                      {
 *                              MostAvailableName = NextMacName;
 *                              MostAvailableCount = NextAvailableCount;
 *                      }
 *
 *                                              Log.TraceVerbose("... " + NextMacName + " has " + NextAvailableCount + " slots available");
 *                      }
 *                      Log.TraceVerbose("Picking the compile server with the most available command slots: " + MostAvailableName);
 *
 *                      // Finally, assign the name of the Mac we're going to use
 *                                      RemoteServerName = MostAvailableName;
 */
                }
                else if (!ProjectFileGenerator.bGenerateProjectFiles)
                {
                    Log.TraceInformation("Picking the default remote server " + RemoteServerName);
                }

                // we need a server name!
                if (string.IsNullOrEmpty(RemoteServerName))
                {
                    Log.TraceError("Remote compiling requires a server name. Use the editor to set up your remote compilation settings.");
                    InitializationErrorCode = 99;
                }

                if (!bUseRPCUtil)
                {
                    // we need the RemoteServerName and the Username to find the private key
                    ResolvedRSyncUsername = ResolveString(RSyncUsername, false);

                    // if the override path is set, just use it directly
                    if (!string.IsNullOrEmpty(SSHPrivateKeyOverridePath))
                    {
                        ResolvedSSHPrivateKey = ResolveString(SSHPrivateKeyOverridePath, true);
                        // make sure it exists
                        if (!File.Exists(ResolvedSSHPrivateKey))
                        {
                            throw new BuildException("An SSHKey override was specified [" + SSHPrivateKeyOverridePath + "] but it doesn't exist. Can't continue...");
                        }
                    }
                    else
                    {
                        // all the places to look for a key
                        string[] Locations = new string[] {
                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Unreal Engine", "UnrealBuildTool"),
                            Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Unreal Engine", "UnrealBuildTool"),
                            Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NotForLicensees"),
                            Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NoRedist"),
                            Path.Combine(BuildConfiguration.RelativeEnginePath, "Build"),
                        };

                        // look for a key file
                        foreach (string Location in Locations)
                        {
                            string KeyPath = Path.Combine(Location, "SSHKeys", RemoteServerName, ResolvedRSyncUsername, "RemoteToolChainPrivate.key");
                            if (File.Exists(KeyPath))
                            {
                                ResolvedSSHPrivateKey = KeyPath;
                                break;
                            }
                        }
                    }

                    // resolve the rest of the strings
                    ResolvedRSyncExe            = ResolveString(RSyncExe, true);
                    ResolvedSSHExe              = ResolveString(SSHExe, true);
                    ResolvedRsyncAuthentication = ResolveString(RsyncAuthentication, false);
                    ResolvedSSHAuthentication   = ResolveString(SSHAuthentication, false);
                }

                // start up remote communication and record if it succeeds
                InitializationErrorCode = RPCUtilHelper.Initialize(RemoteServerName);

                // allow user to set up
                if (InitializationErrorCode == 100)
                {
                    Process KeyProcess = new Process();
                    KeyProcess.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "BatchFiles"));
                    KeyProcess.StartInfo.FileName         = "MakeAndInstallSSHKey.bat";
                    KeyProcess.StartInfo.Arguments        = string.Format(
                        "\"{0}\" \"{1}\" {2} {3} \"{4}\" \"{5}\" \"{6}\"",
                        ResolvedSSHExe,
                        ResolvedRSyncExe,
                        ResolvedRSyncUsername,
                        RemoteServerName,
                        Environment.GetFolderPath(Environment.SpecialFolder.Personal),
                        ConvertPathToCygwin(Environment.GetFolderPath(Environment.SpecialFolder.Personal)),
                        Path.GetFullPath(BuildConfiguration.RelativeEnginePath));

                    KeyProcess.Start();
                    KeyProcess.WaitForExit();

                    // make sure it succeeded if we want to re-init
                    if (KeyProcess.ExitCode == 0)
                    {
                        InitializeRemoteExecution();
                    }
                }
            }
            else
            {
                RemoteServerName = Environment.MachineName;
                // can't error in this case
            }

            bHasBeenInitialized = true;
            return(InitializationErrorCode);
        }