示例#1
0
		static ConfigFile ReadProjectConfigFile(string LocalRootPath, string SelectedLocalFileName, TextWriter Log)
		{
			// Find the valid config file paths
			List<string> ProjectConfigFileNames = new List<string>();
			ProjectConfigFileNames.Add(Path.Combine(LocalRootPath, "Engine", "Programs", "UnrealGameSync", "UnrealGameSync.ini"));
			if(SelectedLocalFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
			{
				ProjectConfigFileNames.Add(Path.Combine(Path.GetDirectoryName(SelectedLocalFileName), "Build", "UnrealGameSync.ini"));
			}

			// Read them in
			ConfigFile ProjectConfig = new ConfigFile();
			foreach(string ProjectConfigFileName in ProjectConfigFileNames)
			{
				if(File.Exists(ProjectConfigFileName))
				{
					try
					{
						string[] Lines = File.ReadAllLines(ProjectConfigFileName);
						ProjectConfig.Parse(Lines);
						Log.WriteLine("Read config file from {0}", ProjectConfigFileName);
					}
					catch(Exception Ex)
					{
						Log.WriteLine("Failed to read config file from {0}: {1}", ProjectConfigFileName, Ex.ToString());
					}
				}
			}
			return ProjectConfig;
		}
示例#2
0
		public void Update(WorkspaceUpdateContext Context)
		{
			// Kill any existing sync
			CancelUpdate();

			// Set the initial progress message
			if(CurrentChangeNumber != Context.ChangeNumber)
			{
				PendingChangeNumber = Context.ChangeNumber;
				if(!Context.Options.HasFlag(WorkspaceUpdateOptions.SyncSingleChange))
				{
					CurrentChangeNumber = -1;
					ProjectConfigFile = null;
				}
			}
			Progress.Clear();
			bSyncing = true;

			// Spawn the new thread
			WorkerThread = new Thread(x => UpdateWorkspace(Context));
			WorkerThread.Start();
		}
示例#3
0
		WorkspaceUpdateResult UpdateWorkspaceInternal(WorkspaceUpdateContext Context, out string StatusMessage)
		{
			string CmdExe = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
			if(!File.Exists(CmdExe))
			{
				StatusMessage = String.Format("Missing {0}.", CmdExe);
				return WorkspaceUpdateResult.FailedToSync;
			}

			List<Tuple<string, TimeSpan>> Times = new List<Tuple<string,TimeSpan>>();

			int NumFilesSynced = 0;
			if(Context.Options.HasFlag(WorkspaceUpdateOptions.Sync) || Context.Options.HasFlag(WorkspaceUpdateOptions.SyncSingleChange))
			{
				using(TelemetryStopwatch Stopwatch = new TelemetryStopwatch("Sync", TelemetryProjectPath))
				{
					Log.WriteLine("Syncing to {0}...", PendingChangeNumber);

					// Find all the files that are out of date
					Progress.Set("Finding files to sync...");

					// Get the user's sync filter
					FileFilter UserFilter = null;
					if(Context.SyncFilter != null)
					{
						UserFilter = new FileFilter(FileFilterType.Include);
						UserFilter.AddRules(Context.SyncFilter.Select(x => x.Trim()).Where(x => x.Length > 0 && !x.StartsWith(";") && !x.StartsWith("#")));
					}

					// Find all the server changes, and anything that's opened for edit locally. We need to sync files we have open to schedule a resolve.
					List<string> SyncFiles = new List<string>();
					foreach(string SyncPath in SyncPaths)
					{
						List<PerforceFileRecord> SyncRecords;
						if(!Perforce.SyncPreview(SyncPath, PendingChangeNumber, !Context.Options.HasFlag(WorkspaceUpdateOptions.Sync), out SyncRecords, Log))
						{
							StatusMessage = String.Format("Couldn't enumerate changes matching {0}.", SyncPath);
							return WorkspaceUpdateResult.FailedToSync;
						}

						if(UserFilter != null)
						{
							SyncRecords.RemoveAll(x => !String.IsNullOrEmpty(x.ClientPath) && !MatchFilter(x.ClientPath, UserFilter));
						}

						SyncFiles.AddRange(SyncRecords.Select(x => x.DepotPath));

						List<PerforceFileRecord> OpenRecords;
						if(!Perforce.GetOpenFiles(SyncPath, out OpenRecords, Log))
						{
							StatusMessage = String.Format("Couldn't find open files matching {0}.", SyncPath);
							return WorkspaceUpdateResult.FailedToSync;
						}

						// don't force a sync on added files
						SyncFiles.AddRange(OpenRecords.Where(x => x.Action != "add" && x.Action != "branch" && x.Action != "move/add").Select(x => x.DepotPath));
					}

					// Filter out all the binaries that we don't want
					FileFilter Filter = new FileFilter(FileFilterType.Include);
					Filter.Exclude("..." + BuildVersionFileName);
					Filter.Exclude("..." + VersionHeaderFileName);
					Filter.Exclude("..." + ObjectVersionFileName);
					if(Context.Options.HasFlag(WorkspaceUpdateOptions.SkipShaders))
					{
						Filter.Exclude("*.usf");
					}
					SyncFiles.RemoveAll(x => !Filter.Matches(x));

					// Sync them all
					List<string> TamperedFiles = new List<string>();
					HashSet<string> RemainingFiles = new HashSet<string>(SyncFiles, StringComparer.InvariantCultureIgnoreCase);
					if(!Perforce.Sync(SyncFiles, PendingChangeNumber, Record => UpdateSyncProgress(Record, RemainingFiles, SyncFiles.Count), TamperedFiles, Log))
					{
						StatusMessage = "Aborted sync due to errors.";
						return WorkspaceUpdateResult.FailedToSync;
					}

					// If any files need to be clobbered, defer to the main thread to figure out which ones
					if(TamperedFiles.Count > 0)
					{
						int NumNewFilesToClobber = 0;
						foreach(string TamperedFile in TamperedFiles)
						{
							if(!Context.ClobberFiles.ContainsKey(TamperedFile))
							{
								Context.ClobberFiles[TamperedFile] = true;
								NumNewFilesToClobber++;
							}
						}
						if(NumNewFilesToClobber > 0)
						{
							StatusMessage = String.Format("Cancelled sync after checking files to clobber ({0} new files).", NumNewFilesToClobber);
							return WorkspaceUpdateResult.FilesToClobber;
						}
						foreach(string TamperedFile in TamperedFiles)
						{
							if(Context.ClobberFiles[TamperedFile] && !Perforce.ForceSync(TamperedFile, PendingChangeNumber, Log))
							{
								StatusMessage = String.Format("Couldn't sync {0}.", TamperedFile);
								return WorkspaceUpdateResult.FailedToSync;
							}
						}
					}

					ConfigFile NewProjectConfigFile = null;
					if(Context.Options.HasFlag(WorkspaceUpdateOptions.Sync))
					{
						// Read the new config file
						NewProjectConfigFile = ReadProjectConfigFile(LocalRootPath, SelectedLocalFileName, Log);

						// Get the branch name
						string BranchOrStreamName;
						if(!Perforce.GetActiveStream(out BranchOrStreamName, Log))
						{
							string DepotFileName;
							if(!Perforce.ConvertToDepotPath(ClientRootPath + "/GenerateProjectFiles.bat", out DepotFileName, Log))
							{
								StatusMessage = String.Format("Couldn't determine branch name for {0}.", SelectedClientFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}
							BranchOrStreamName = PerforceUtils.GetClientOrDepotDirectoryName(DepotFileName);
						}

						// Find the last code change before this changelist. For consistency in versioning between local builds and precompiled binaries, we need to use the last submitted code changelist as our version number.
						List<PerforceChangeSummary> CodeChanges;
						if(!Perforce.FindChanges(new string[]{ ".cs", ".h", ".cpp", ".usf" }.SelectMany(x => SyncPaths.Select(y => String.Format("{0}{1}@<={2}", y, x, PendingChangeNumber))), 1, out CodeChanges, Log))
						{
							StatusMessage = String.Format("Couldn't determine last code changelist before CL {0}.", PendingChangeNumber);
							return WorkspaceUpdateResult.FailedToSync;
						}
						if(CodeChanges.Count == 0)
						{
							StatusMessage = String.Format("Could not find any code changes before CL {0}.", PendingChangeNumber);
							return WorkspaceUpdateResult.FailedToSync;
						}

						// Get the last code change
						int VersionChangeNumber;
						if(NewProjectConfigFile.GetValue("Options.VersionToLastCodeChange", true))
						{
							VersionChangeNumber = CodeChanges.Max(x => x.Number);
						}
						else
						{
							VersionChangeNumber = PendingChangeNumber;
						}

						// Update the version files
						if(NewProjectConfigFile.GetValue("Options.UseFastModularVersioning", false))
						{
							Dictionary<string, string> BuildVersionStrings = new Dictionary<string,string>();
							BuildVersionStrings["\"Changelist\":"] = String.Format(" {0},", VersionChangeNumber);
							BuildVersionStrings["\"BranchName\":"] = String.Format(" \"{0}\"", BranchOrStreamName.Replace('/', '+'));
							if(!UpdateVersionFile(ClientRootPath + BuildVersionFileName, BuildVersionStrings, PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}.", BuildVersionFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}

							Dictionary<string, string> VersionHeaderStrings = new Dictionary<string,string>();
							VersionHeaderStrings["#define ENGINE_IS_PROMOTED_BUILD"] = " (0)";
							VersionHeaderStrings["#define BUILT_FROM_CHANGELIST"] = " 0";
							VersionHeaderStrings["#define BRANCH_NAME"] = " \"" + BranchOrStreamName.Replace('/', '+') + "\"";
							if(!UpdateVersionFile(ClientRootPath + VersionHeaderFileName, VersionHeaderStrings, PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}.", VersionHeaderFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}
							if(!UpdateVersionFile(ClientRootPath + ObjectVersionFileName, new Dictionary<string,string>(), PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}.", ObjectVersionFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}
						}
						else
						{
							if(!UpdateVersionFile(ClientRootPath + BuildVersionFileName, new Dictionary<string, string>(), PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}", BuildVersionFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}

							Dictionary<string, string> VersionStrings = new Dictionary<string,string>();
							VersionStrings["#define ENGINE_VERSION"] = " " + VersionChangeNumber.ToString();
							VersionStrings["#define ENGINE_IS_PROMOTED_BUILD"] = " (0)";
							VersionStrings["#define BUILT_FROM_CHANGELIST"] = " " + VersionChangeNumber.ToString();
							VersionStrings["#define BRANCH_NAME"] = " \"" + BranchOrStreamName.Replace('/', '+') + "\"";
							if(!UpdateVersionFile(ClientRootPath + VersionHeaderFileName, VersionStrings, PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}", VersionHeaderFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}
							if(!UpdateVersionFile(ClientRootPath + ObjectVersionFileName, VersionStrings, PendingChangeNumber))
							{
								StatusMessage = String.Format("Failed to update {0}", ObjectVersionFileName);
								return WorkspaceUpdateResult.FailedToSync;
							}
						}

						// Remove all the receipts for build targets in this branch
						if(SelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
						{
							Perforce.Sync(PerforceUtils.GetClientOrDepotDirectoryName(SelectedClientFileName) + "/Build/Receipts/...#0", Log);
						}
					}

					// Check if there are any files which need resolving
					List<PerforceFileRecord> UnresolvedFiles;
					if(!FindUnresolvedFiles(SyncPaths, out UnresolvedFiles))
					{
						StatusMessage = "Couldn't get list of unresolved files.";
						return WorkspaceUpdateResult.FailedToSync;
					}
					if(UnresolvedFiles.Count > 0 && Context.Options.HasFlag(WorkspaceUpdateOptions.AutoResolveChanges))
					{
						foreach (PerforceFileRecord UnresolvedFile in UnresolvedFiles)
						{
							Perforce.AutoResolveFile(UnresolvedFile.DepotPath, Log);
						}
						if(!FindUnresolvedFiles(SyncPaths, out UnresolvedFiles))
						{
							StatusMessage = "Couldn't get list of unresolved files.";
							return WorkspaceUpdateResult.FailedToSync;
						}
					}
					if(UnresolvedFiles.Count > 0)
					{
						Log.WriteLine("{0} files need resolving:", UnresolvedFiles.Count);
						foreach(PerforceFileRecord UnresolvedFile in UnresolvedFiles)
						{
							Log.WriteLine("  {0}", UnresolvedFile.ClientPath);
						}
						StatusMessage = "Files need resolving.";
						return WorkspaceUpdateResult.FilesToResolve;
					}

					// Update the current change number. Everything else happens for the new change.
					if(Context.Options.HasFlag(WorkspaceUpdateOptions.Sync))
					{
						ProjectConfigFile = NewProjectConfigFile;
						CurrentChangeNumber = PendingChangeNumber;
					}

					// Update the timing info
					Times.Add(new Tuple<string,TimeSpan>("Sync", Stopwatch.Stop("Success")));

					// Save the number of files synced
					NumFilesSynced = SyncFiles.Count;
					Log.WriteLine();
				}
			}

			// Extract an archive from the depot path
			if(Context.Options.HasFlag(WorkspaceUpdateOptions.SyncArchives))
			{
				using(TelemetryStopwatch Stopwatch = new TelemetryStopwatch("Archives", TelemetryProjectPath))
				{
					// Create the directory for extracted archive manifests
					string ManifestDirectoryName;
					if(SelectedLocalFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
					{
						ManifestDirectoryName = Path.Combine(Path.GetDirectoryName(SelectedLocalFileName), "Saved", "UnrealGameSync");
					}
					else
					{
						ManifestDirectoryName = Path.Combine(Path.GetDirectoryName(SelectedLocalFileName), "Engine", "Saved", "UnrealGameSync");
					}
					Directory.CreateDirectory(ManifestDirectoryName);

					// Sync and extract (or just remove) the given archives
					foreach(KeyValuePair<string, string> ArchiveTypeAndDepotPath in Context.ArchiveTypeToDepotPath)
					{
						// Remove any existing binaries
						string ManifestFileName = Path.Combine(ManifestDirectoryName, String.Format("{0}.zipmanifest", ArchiveTypeAndDepotPath.Key));
						if(File.Exists(ManifestFileName))
						{
							Log.WriteLine("Removing {0} binaries...", ArchiveTypeAndDepotPath.Key);
							Progress.Set(String.Format("Removing {0} binaries...", ArchiveTypeAndDepotPath.Key), 0.0f);
							ArchiveUtils.RemoveExtractedFiles(LocalRootPath, ManifestFileName, Progress, Log);
							Log.WriteLine();
						}

						// If we have a new depot path, sync it down and extract it
						if(ArchiveTypeAndDepotPath.Value != null)
						{
							string TempZipFileName = Path.GetTempFileName();
							try
							{
								Log.WriteLine("Syncing {0} binaries...", ArchiveTypeAndDepotPath.Key.ToLowerInvariant());
								Progress.Set(String.Format("Syncing {0} binaries...", ArchiveTypeAndDepotPath.Key.ToLowerInvariant()), 0.0f);
								if(!Perforce.PrintToFile(ArchiveTypeAndDepotPath.Value, TempZipFileName, Log) || new FileInfo(TempZipFileName).Length == 0)
								{
									StatusMessage = String.Format("Couldn't read {0}", ArchiveTypeAndDepotPath.Value);
									return WorkspaceUpdateResult.FailedToSync;
								}
								ArchiveUtils.ExtractFiles(TempZipFileName, LocalRootPath, ManifestFileName, Progress, Log);
								Log.WriteLine();
							}
							finally
							{
								File.SetAttributes(TempZipFileName, FileAttributes.Normal);
								File.Delete(TempZipFileName);
							}
						}
					}

					// Add the finish time
					Times.Add(new Tuple<string,TimeSpan>("Archive", Stopwatch.Stop("Success")));
				}
			}

			// Generate project files in the workspace
			if(Context.Options.HasFlag(WorkspaceUpdateOptions.GenerateProjectFiles))
			{
				using(TelemetryStopwatch Stopwatch = new TelemetryStopwatch("Prj gen", TelemetryProjectPath))
				{
					Progress.Set("Generating project files...", 0.0f);

					string ProjectFileArgument = "";
					if(SelectedLocalFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
					{
						ProjectFileArgument = String.Format("\"{0}\" ", SelectedLocalFileName);
					}

					string CommandLine = String.Format("/C \"\"{0}\" {1}-progress\"", Path.Combine(LocalRootPath, "GenerateProjectFiles.bat"), ProjectFileArgument);

					Log.WriteLine("Generating project files...");
					Log.WriteLine("gpf> Running {0} {1}", CmdExe, CommandLine);

					int GenerateProjectFilesResult = Utility.ExecuteProcess(CmdExe, CommandLine, null, new ProgressTextWriter(Progress, new PrefixedTextWriter("gpf> ", Log)));
					if(GenerateProjectFilesResult != 0)
					{
						StatusMessage = String.Format("Failed to generate project files (exit code {0}).", GenerateProjectFilesResult);
						return WorkspaceUpdateResult.FailedToCompile;
					}

					Log.WriteLine();
					Times.Add(new Tuple<string,TimeSpan>("Prj gen", Stopwatch.Stop("Success")));
				}
			}

			// Build everything using MegaXGE
			if(Context.Options.HasFlag(WorkspaceUpdateOptions.Build))
			{
				// Compile all the build steps together
				Dictionary<Guid, ConfigObject> BuildStepObjects = Context.DefaultBuildSteps.ToDictionary(x => x.Key, x => new ConfigObject(x.Value));
				BuildStep.MergeBuildStepObjects(BuildStepObjects, ProjectConfigFile.GetValues("Build.Step", new string[0]).Select(x => new ConfigObject(x)));
				BuildStep.MergeBuildStepObjects(BuildStepObjects, Context.UserBuildStepObjects);

				// Construct build steps from them
				List<BuildStep> BuildSteps = BuildStepObjects.Values.Select(x => new BuildStep(x)).OrderBy(x => x.OrderIndex).ToList();
				if(Context.CustomBuildSteps != null && Context.CustomBuildSteps.Count > 0)
				{
					BuildSteps.RemoveAll(x => !Context.CustomBuildSteps.Contains(x.UniqueId));
				}
				else if(Context.Options.HasFlag(WorkspaceUpdateOptions.ScheduledBuild))
				{
					BuildSteps.RemoveAll(x => !x.bScheduledSync);
				}
				else
				{
					BuildSteps.RemoveAll(x => !x.bNormalSync);
				}

				// Check if the last successful build was before a change that we need to force a clean for
				bool bForceClean = false;
				if(LastBuiltChangeNumber != 0)
				{
					foreach(string CleanBuildChange in ProjectConfigFile.GetValues("ForceClean.Changelist", new string[0]))
					{
						int ChangeNumber;
						if(int.TryParse(CleanBuildChange, out ChangeNumber))
						{
							if((LastBuiltChangeNumber >= ChangeNumber) != (CurrentChangeNumber >= ChangeNumber))
							{
								Log.WriteLine("Forcing clean build due to changelist {0}.", ChangeNumber);
								Log.WriteLine();
								bForceClean = true;
								break;
							}
						}
					}
				}

				// Execute them all
				string TelemetryEventName = (Context.UserBuildStepObjects.Count > 0)? "CustomBuild" : Context.Options.HasFlag(WorkspaceUpdateOptions.UseIncrementalBuilds) ? "Compile" : "FullCompile";
				using(TelemetryStopwatch Stopwatch = new TelemetryStopwatch(TelemetryEventName, TelemetryProjectPath))
				{
					Progress.Set("Starting build...", 0.0f);

					// Check we've built UBT (it should have been compiled by generating project files)
					string UnrealBuildToolPath = Path.Combine(LocalRootPath, "Engine", "Binaries", "DotNET", "UnrealBuildTool.exe");
					if(!File.Exists(UnrealBuildToolPath))
					{
						StatusMessage = String.Format("Couldn't find {0}", UnrealBuildToolPath);
						return WorkspaceUpdateResult.FailedToCompile;
					}

					// Execute all the steps
					float MaxProgressFraction = 0.0f;
					foreach (BuildStep Step in BuildSteps)
					{
						MaxProgressFraction += (float)Step.EstimatedDuration / (float)Math.Max(BuildSteps.Sum(x => x.EstimatedDuration), 1);

						Progress.Set(Step.StatusText);
						Progress.Push(MaxProgressFraction);

						Log.WriteLine(Step.StatusText);

						switch(Step.Type)
						{
							case BuildStepType.Compile:
								using(TelemetryStopwatch StepStopwatch = new TelemetryStopwatch("Compile:" + Step.Target, TelemetryProjectPath))
								{
									string CommandLine = String.Format("{0} {1} {2} {3} -NoHotReloadFromIDE", Step.Target, Step.Platform, Step.Configuration, Utility.ExpandVariables(Step.Arguments, Context.Variables));
									if(!Context.Options.HasFlag(WorkspaceUpdateOptions.UseIncrementalBuilds) || bForceClean)
									{
										Log.WriteLine("ubt> Running {0} {1} -clean", UnrealBuildToolPath, CommandLine);
										Utility.ExecuteProcess(UnrealBuildToolPath, CommandLine + " -clean", null, new ProgressTextWriter(Progress, new PrefixedTextWriter("ubt> ", Log)));
									}

									Log.WriteLine("ubt> Running {0} {1} -progress", UnrealBuildToolPath, CommandLine);

									int ResultFromBuild = Utility.ExecuteProcess(UnrealBuildToolPath, CommandLine + " -progress", null, new ProgressTextWriter(Progress, new PrefixedTextWriter("ubt> ", Log)));
									if(ResultFromBuild != 0)
									{
										StatusMessage = String.Format("Failed to compile {0}.", Step.Target);
										return (HasModifiedSourceFiles() || Context.UserBuildStepObjects.Count > 0)? WorkspaceUpdateResult.FailedToCompile : WorkspaceUpdateResult.FailedToCompileWithCleanWorkspace;
									}
								}
								break;
							case BuildStepType.Cook:
								using(TelemetryStopwatch StepStopwatch = new TelemetryStopwatch("Cook/Launch: " + Path.GetFileNameWithoutExtension(Step.FileName), TelemetryProjectPath))
								{
									string LocalRunUAT = Path.Combine(LocalRootPath, "Engine", "Build", "BatchFiles", "RunUAT.bat");
									string Arguments = String.Format("/C \"\"{0}\" -profile=\"{1}\"\"", LocalRunUAT, Path.Combine(LocalRootPath, Step.FileName));
									Log.WriteLine("uat> Running {0} {1}", LocalRunUAT, Arguments);

									int ResultFromUAT = Utility.ExecuteProcess(CmdExe, Arguments, null, new ProgressTextWriter(Progress, new PrefixedTextWriter("uat> ", Log)));
									if(ResultFromUAT != 0)
									{
										StatusMessage = String.Format("Cook failed. ({0})", ResultFromUAT);
										return WorkspaceUpdateResult.FailedToCompile;
									}
								}
								break;
							case BuildStepType.Other:
								using(TelemetryStopwatch StepStopwatch = new TelemetryStopwatch("Custom: " + Path.GetFileNameWithoutExtension(Step.FileName), TelemetryProjectPath))
								{
									string ToolFileName = Path.Combine(LocalRootPath, Utility.ExpandVariables(Step.FileName, Context.Variables));
									string ToolArguments = Utility.ExpandVariables(Step.Arguments, Context.Variables);
									Log.WriteLine("tool> Running {0} {1}", ToolFileName, ToolArguments);

									if(Step.bUseLogWindow)
									{
										int ResultFromTool = Utility.ExecuteProcess(ToolFileName, ToolArguments, null, new ProgressTextWriter(Progress, new PrefixedTextWriter("tool> ", Log)));
										if(ResultFromTool != 0)
										{
											StatusMessage = String.Format("Tool terminated with exit code {0}.", ResultFromTool);
											return WorkspaceUpdateResult.FailedToCompile;
										}
									}
									else
									{
										using(Process.Start(ToolFileName, ToolArguments))
										{
										}
									}
								}
								break;
						}

						Log.WriteLine();
						Progress.Pop();
					}

					Times.Add(new Tuple<string,TimeSpan>("Build", Stopwatch.Stop("Success")));
				}

				// Update the last successful build change number
				if(Context.CustomBuildSteps == null || Context.CustomBuildSteps.Count == 0)
				{
					LastBuiltChangeNumber = CurrentChangeNumber;
				}
			}

			// Write out all the timing information
			Log.WriteLine("Total time : " + FormatTime(Times.Sum(x => (long)(x.Item2.TotalMilliseconds / 1000))));
			foreach(Tuple<string, TimeSpan> Time in Times)
			{
				Log.WriteLine("   {0,-8}: {1}", Time.Item1, FormatTime((long)(Time.Item2.TotalMilliseconds / 1000)));
			}
			if(NumFilesSynced > 0)
			{
				Log.WriteLine("{0} files synced.", NumFilesSynced);
			}

			Log.WriteLine();
			Log.WriteLine("UPDATE SUCCEEDED");

			StatusMessage = "Update succeeded";
			return WorkspaceUpdateResult.Success;
		}
示例#4
0
		static void MergeUpdateSettings(string UpdateConfigFile, ref string UpdatePath, ref string UpdateSpawn)
		{
			try
			{
				ConfigFile UpdateConfig = new ConfigFile();
				if(File.Exists(UpdateConfigFile))
				{
					UpdateConfig.Load(UpdateConfigFile);
				}

				if(UpdatePath == null)
				{
					UpdatePath = UpdateConfig.GetValue("Update.Path", null);
				}
				else
				{
					UpdateConfig.SetValue("Update.Path", UpdatePath);
				}

				if(UpdateSpawn == null)
				{
					UpdateSpawn = UpdateConfig.GetValue("Update.Spawn", null);
				}
				else
				{
					UpdateConfig.SetValue("Update.Spawn", UpdateSpawn);
				}

				UpdateConfig.Save(UpdateConfigFile);
			}
			catch(Exception)
			{
			}
		}
		private Dictionary<Guid, ConfigObject> GetProjectBuildStepObjects(ConfigFile ProjectConfigFile)
		{
			Dictionary<Guid, ConfigObject> ProjectBuildSteps = GetDefaultBuildStepObjects();
			foreach(string Line in ProjectConfigFile.GetValues("Build.Step", new string[0]))
			{
				AddOrUpdateBuildStep(ProjectBuildSteps, new ConfigObject(Line));
			}
			return ProjectBuildSteps;
		}
示例#6
0
        public bool Run(PerforceConnection Perforce, TextWriter Log, out string ErrorMessage)
        {
            // Use the cached client path to the file if it's available; it's much quicker than trying to find the correct workspace.
            if (!String.IsNullOrEmpty(SelectedProject.ClientPath))
            {
                // Get the client path
                NewSelectedClientFileName = SelectedProject.ClientPath;

                // Get the client name
                string ClientName;
                if (!PerforceUtils.TryGetClientName(NewSelectedClientFileName, out ClientName))
                {
                    ErrorMessage = String.Format("Couldn't get client name from {0}", NewSelectedClientFileName);
                    return(false);
                }

                // Create the client
                PerforceClient = new PerforceConnection(Perforce.UserName, ClientName, Perforce.ServerAndPort);

                // Figure out the path on the client. Use the cached location if it's valid.
                if (SelectedProject.LocalPath != null && File.Exists(SelectedProject.LocalPath))
                {
                    NewSelectedFileName = SelectedProject.LocalPath;
                }
                else
                {
                    if (!PerforceClient.ConvertToLocalPath(NewSelectedClientFileName, out NewSelectedFileName, Log))
                    {
                        ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedClientFileName);
                        return(false);
                    }
                }
            }
            else
            {
                // Get the perforce server settings
                PerforceInfoRecord PerforceInfo;
                if (!Perforce.Info(out PerforceInfo, Log))
                {
                    ErrorMessage = String.Format("Couldn't get Perforce server info");
                    return(false);
                }

                // Use the path as the selected filename
                NewSelectedFileName = SelectedProject.LocalPath;

                // Make sure the project exists
                if (!File.Exists(SelectedProject.LocalPath))
                {
                    ErrorMessage = String.Format("{0} does not exist.", SelectedProject.LocalPath);
                    return(false);
                }

                // Find all the clients for this user
                Log.WriteLine("Enumerating clients for {0}...", PerforceInfo.UserName);

                List <PerforceClientRecord> Clients;
                if (!Perforce.FindClients(PerforceInfo.UserName, out Clients, Log))
                {
                    ErrorMessage = String.Format("Couldn't find any clients for this host.");
                    return(false);
                }

                List <PerforceConnection> CandidateClients = FilterClients(Clients, Perforce.ServerAndPort, PerforceInfo.HostName, PerforceInfo.UserName);
                if (CandidateClients.Count == 0)
                {
                    // Search through all workspaces. We may find a suitable workspace which is for any user.
                    Log.WriteLine("Enumerating shared clients...");
                    if (!Perforce.FindClients("", out Clients, Log))
                    {
                        ErrorMessage = "Failed to enumerate clients.";
                        return(false);
                    }

                    // Filter this list of clients
                    CandidateClients = FilterClients(Clients, Perforce.ServerAndPort, PerforceInfo.HostName, PerforceInfo.UserName);

                    // If we still couldn't find any, fail.
                    if (CandidateClients.Count == 0)
                    {
                        ErrorMessage = String.Format("Couldn't find any Perforce workspace containing {0}. Check your connection settings.", NewSelectedFileName);
                        return(false);
                    }
                }

                // Check there's only one client
                if (CandidateClients.Count > 1)
                {
                    ErrorMessage = String.Format("Found multiple workspaces containing {0}:\n\n{1}\n\nCannot determine which to use.", Path.GetFileName(NewSelectedFileName), String.Join("\n", CandidateClients.Select(x => x.ClientName)));
                    return(false);
                }

                // Take the client we've chosen
                PerforceClient = CandidateClients[0];

                // Get the client path for the project file
                if (!PerforceClient.ConvertToClientPath(NewSelectedFileName, out NewSelectedClientFileName, Log))
                {
                    ErrorMessage = String.Format("Couldn't get client path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Normalize the filename
            NewSelectedFileName = Path.GetFullPath(NewSelectedFileName).Replace('/', Path.DirectorySeparatorChar);

            // Make sure the path case is correct. This can cause UBT intermediates to be out of date if the case mismatches.
            NewSelectedFileName = Utility.GetPathWithCorrectCase(new FileInfo(NewSelectedFileName));

            // Update the selected project with all the data we've found
            SelectedProject = new UserSelectedProjectSettings(SelectedProject.ServerAndPort, SelectedProject.UserName, SelectedProject.Type, NewSelectedClientFileName, NewSelectedFileName);

            // Figure out where the engine is in relation to it
            int EndIdx = NewSelectedClientFileName.Length - 1;

            if (EndIdx != -1 && NewSelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                EndIdx = NewSelectedClientFileName.LastIndexOf('/') - 1;
            }
            for (;; EndIdx--)
            {
                if (EndIdx < 2)
                {
                    ErrorMessage = String.Format("Could not find engine in Perforce relative to project path ({0})", NewSelectedClientFileName);
                    return(false);
                }
                if (NewSelectedClientFileName[EndIdx] == '/')
                {
                    List <PerforceFileRecord> FileRecords;
                    if (PerforceClient.Stat(NewSelectedClientFileName.Substring(0, EndIdx) + "/Engine/Build/Build.version", out FileRecords, Log) && FileRecords.Count > 0)
                    {
                        if (FileRecords[0].ClientPath == null)
                        {
                            ErrorMessage = String.Format("Missing client path for {0}", FileRecords[0].DepotPath);
                            return(false);
                        }

                        BranchClientPath    = NewSelectedClientFileName.Substring(0, EndIdx);
                        BranchDirectoryName = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(FileRecords[0].ClientPath), "..", ".."));
                        break;
                    }
                }
            }
            Log.WriteLine("Found branch root at {0}", BranchClientPath);

            // Find the editor target for this project
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                List <PerforceFileRecord> Files;
                if (PerforceClient.FindFiles(PerforceUtils.GetClientOrDepotDirectoryName(NewSelectedClientFileName) + "/Source/*Editor.Target.cs", out Files, Log) && Files.Count >= 1)
                {
                    string DepotPath = Files[0].DepotPath;
                    NewProjectEditorTarget = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(DepotPath.Substring(DepotPath.LastIndexOf('/') + 1)));
                    Log.WriteLine("Using {0} as editor target name (from {1})", NewProjectEditorTarget, Files[0]);
                }
                if (NewProjectEditorTarget == null)
                {
                    Log.WriteLine("Couldn't find any editor targets for this project.");
                }
            }

            // Get a unique name for the project that's selected. For regular branches, this can be the depot path. For streams, we want to include the stream name to encode imports.
            if (PerforceClient.GetActiveStream(out StreamName, Log))
            {
                string ExpectedPrefix = String.Format("//{0}/", PerforceClient.ClientName);
                if (!NewSelectedClientFileName.StartsWith(ExpectedPrefix, StringComparison.InvariantCultureIgnoreCase))
                {
                    ErrorMessage = String.Format("Unexpected client path; expected '{0}' to begin with '{1}'", NewSelectedClientFileName, ExpectedPrefix);
                    return(false);
                }
                string StreamPrefix;
                if (!TryGetStreamPrefix(PerforceClient, StreamName, Log, out StreamPrefix))
                {
                    ErrorMessage = String.Format("Failed to get stream info for {0}", StreamName);
                    return(false);
                }
                NewSelectedProjectIdentifier = String.Format("{0}/{1}", StreamPrefix, NewSelectedClientFileName.Substring(ExpectedPrefix.Length));
            }
            else
            {
                if (!PerforceClient.ConvertToDepotPath(NewSelectedClientFileName, out NewSelectedProjectIdentifier, Log))
                {
                    ErrorMessage = String.Format("Couldn't get depot path for {0}", NewSelectedFileName);
                    return(false);
                }
            }

            // Read the project logo
            if (NewSelectedFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string LogoFileName = Path.Combine(Path.GetDirectoryName(NewSelectedFileName), "Build", "UnrealGameSync.png");
                if (File.Exists(LogoFileName))
                {
                    try
                    {
                        // Duplicate the image, otherwise we'll leave the file locked
                        using (Image Image = Image.FromFile(LogoFileName))
                        {
                            ProjectLogo = new Bitmap(Image);
                        }
                    }
                    catch
                    {
                        ProjectLogo = null;
                    }
                }
            }

            // Figure out if it's an enterprise project. Use the synced version if we have it.
            if (NewSelectedClientFileName.EndsWith(".uproject", StringComparison.InvariantCultureIgnoreCase))
            {
                string Text;
                if (File.Exists(NewSelectedFileName))
                {
                    Text = File.ReadAllText(NewSelectedFileName);
                }
                else
                {
                    List <string> ProjectLines;
                    if (!PerforceClient.Print(NewSelectedClientFileName, out ProjectLines, Log))
                    {
                        ErrorMessage = String.Format("Unable to get contents of {0}", NewSelectedClientFileName);
                        return(false);
                    }
                    Text = String.Join("\n", ProjectLines);
                }
                bIsEnterpriseProject = Utility.IsEnterpriseProjectFromText(Text);
            }

            // Read the initial config file
            LocalConfigFiles        = new List <KeyValuePair <string, DateTime> >();
            LatestProjectConfigFile = PerforceMonitor.ReadProjectConfigFile(PerforceClient, BranchClientPath, NewSelectedClientFileName, CacheFolder, LocalConfigFiles, Log);

            // Succeed!
            ErrorMessage = null;
            return(true);
        }
示例#7
0
        public PerforceMonitor(PerforceConnection InPerforce, string InBranchClientPath, string InSelectedClientFileName, string InSelectedProjectIdentifier, string InLogPath, bool bInIsEnterpriseProject, ConfigFile InProjectConfigFile, List <KeyValuePair <string, DateTime> > InLocalConfigFiles)
        {
            Perforce                    = InPerforce;
            BranchClientPath            = InBranchClientPath;
            SelectedClientFileName      = InSelectedClientFileName;
            SelectedProjectIdentifier   = InSelectedProjectIdentifier;
            PendingMaxChangesValue      = 100;
            LastChangeByCurrentUser     = -1;
            LastCodeChangeByCurrentUser = -1;
            OtherStreamNames            = new List <string>();
            bIsEnterpriseProject        = bInIsEnterpriseProject;
            LatestProjectConfigFile     = InProjectConfigFile;
            LocalConfigFiles            = InLocalConfigFiles;

            LogWriter = new BoundedLogWriter(InLogPath);
        }
        bool UpdateZippedBinaries()
        {
            // Get the path to the config file
            string ClientConfigFileName = PerforceUtils.GetClientOrDepotDirectoryName(SelectedClientFileName) + "/Build/UnrealGameSync.ini";

            // Find the most recent change to that file (if the file doesn't exist, we succeed and just get 0 changes).
            List <PerforceChangeSummary> ConfigFileChanges;

            if (!Perforce.FindChanges(ClientConfigFileName, 1, out ConfigFileChanges, LogWriter))
            {
                return(false);
            }

            // Update the zipped binaries path if it's changed
            int NewZippedBinariesConfigChangeNumber = (ConfigFileChanges.Count > 0)? ConfigFileChanges[0].Number : 0;

            if (NewZippedBinariesConfigChangeNumber != ZippedBinariesConfigChangeNumber)
            {
                string NewZippedBinariesPath = null;
                if (NewZippedBinariesConfigChangeNumber != 0)
                {
                    List <string> Lines;
                    if (!Perforce.Print(ClientConfigFileName, out Lines, LogWriter))
                    {
                        return(false);
                    }

                    ConfigFile NewConfigFile = new ConfigFile();
                    NewConfigFile.Parse(Lines.ToArray());

                    ConfigSection ProjectSection = NewConfigFile.FindSection(SelectedProjectIdentifier);
                    if (ProjectSection != null)
                    {
                        NewZippedBinariesPath = ProjectSection.GetValue("ZippedBinariesPath", null);
                    }
                }
                ZippedBinariesPath = NewZippedBinariesPath;
                ZippedBinariesConfigChangeNumber = NewZippedBinariesConfigChangeNumber;
            }

            SortedList <int, string> NewChangeNumberToZippedBinaries = new SortedList <int, string>();

            if (ZippedBinariesPath != null)
            {
                List <PerforceFileChangeSummary> Changes;
                if (!Perforce.FindFileChanges(ZippedBinariesPath, 100, out Changes, LogWriter))
                {
                    return(false);
                }

                foreach (PerforceFileChangeSummary Change in Changes)
                {
                    if (Change.Action != "purge")
                    {
                        string[] Tokens = Change.Description.Split(' ');
                        if (Tokens[0].StartsWith("[CL") && Tokens[1].EndsWith("]"))
                        {
                            int OriginalChangeNumber;
                            if (int.TryParse(Tokens[1].Substring(0, Tokens[1].Length - 1), out OriginalChangeNumber) && !NewChangeNumberToZippedBinaries.ContainsKey(OriginalChangeNumber))
                            {
                                NewChangeNumberToZippedBinaries[OriginalChangeNumber] = String.Format("{0}#{1}", ZippedBinariesPath, Change.Revision);
                            }
                        }
                    }
                }
            }

            if (!ChangeNumberToZippedBinaries.SequenceEqual(NewChangeNumberToZippedBinaries))
            {
                ChangeNumberToZippedBinaries = NewChangeNumberToZippedBinaries;
                if (OnUpdateMetadata != null && Changes.Count > 0)
                {
                    OnUpdateMetadata();
                }
            }

            return(true);
        }