/// <summary> /// Creates a text file with the given contents. If the contents of the text file aren't changed, it won't write the new contents to /// the file to avoid causing an action to be considered outdated. /// </summary> /// <param name="Location">Path to the intermediate file to create</param> /// <param name="Contents">Contents of the new file</param> /// <returns>File item for the newly created file</returns> public static FileItem CreateIntermediateTextFile(FileReference Location, string Contents) { // Only write the file if its contents have changed. if (!FileReference.Exists(Location)) { DirectoryReference.CreateDirectory(Location.Directory); FileReference.WriteAllText(Location, Contents, GetEncodingForString(Contents)); } else { string CurrentContents = Utils.ReadAllText(Location.FullName); if (!String.Equals(CurrentContents, Contents, StringComparison.InvariantCultureIgnoreCase)) { FileReference BackupFile = new FileReference(Location.FullName + ".old"); try { Log.TraceLog("Updating {0}: contents have changed. Saving previous version to {1}.", Location, BackupFile); FileReference.Delete(BackupFile); FileReference.Move(Location, BackupFile); } catch (Exception Ex) { Log.TraceWarning("Unable to rename {0} to {1}", Location, BackupFile); Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex)); } FileReference.WriteAllText(Location, Contents, GetEncodingForString(Contents)); } } // Reset the file info, in case it already knows about the old file FileItem Item = GetItemByFileReference(Location); Item.ResetCachedInfo(); return(Item); }
/// <summary> /// Upload all the files in the workspace for the current project /// </summary> void UploadWorkspace(DirectoryReference TempDir) { // Path to the scripts to be uploaded FileReference ScriptPathsFileName = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncEngineScripts.txt"); // Read the list of scripts to be uploaded List <string> ScriptPaths = new List <string>(); foreach (string Line in FileReference.ReadAllLines(ScriptPathsFileName)) { string FileToUpload = Line.Trim(); if (FileToUpload.Length > 0 && FileToUpload[0] != ';') { ScriptPaths.Add(FileToUpload); } } // Fixup the line endings List <FileReference> TargetFiles = new List <FileReference>(); foreach (string ScriptPath in ScriptPaths) { FileReference SourceFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, ScriptPath.TrimStart('/')); if (!FileReference.Exists(SourceFile)) { throw new BuildException("Missing script required for remote upload: {0}", SourceFile); } FileReference TargetFile = FileReference.Combine(TempDir, SourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory)); if (!FileReference.Exists(TargetFile) || FileReference.GetLastWriteTimeUtc(TargetFile) < FileReference.GetLastWriteTimeUtc(SourceFile)) { DirectoryReference.CreateDirectory(TargetFile.Directory); string ScriptText = FileReference.ReadAllText(SourceFile); FileReference.WriteAllText(TargetFile, ScriptText.Replace("\r\n", "\n")); } TargetFiles.Add(TargetFile); } // Write a file that protects all the scripts from being overridden by the standard engine filters FileReference ScriptUploadList = FileReference.Combine(TempDir, "RsyncEngineScripts-Upload.txt"); using (StreamWriter Writer = new StreamWriter(ScriptUploadList.FullName)) { foreach (string ScriptPath in ScriptPaths) { for (int SlashIdx = ScriptPath.IndexOf('/', 1); SlashIdx != -1; SlashIdx = ScriptPath.IndexOf('/', SlashIdx + 1)) { Writer.WriteLine("+ {0}", ScriptPath.Substring(0, SlashIdx)); } Writer.WriteLine("+ {0}", ScriptPath); } Writer.WriteLine("protect *"); } // Write a file that protects all the scripts from being overridden by the standard engine filters FileReference ScriptProtectList = FileReference.Combine(TempDir, "RsyncEngineScripts-Protect.txt"); using (StreamWriter Writer = new StreamWriter(ScriptProtectList.FullName)) { foreach (string ScriptPath in ScriptPaths) { Writer.WriteLine("protect {0}", ScriptPath); } } // Upload these files to the remote List <FileReference> FilterLocations = new List <FileReference>(); FilterLocations.Add(ScriptUploadList); UploadDirectory(TempDir, GetRemotePath(UnrealBuildTool.EngineDirectory), FilterLocations); // Upload the engine files List <FileReference> EngineFilters = new List <FileReference>(); EngineFilters.Add(ScriptProtectList); EngineFilters.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncEngine.txt")); UploadDirectory(UnrealBuildTool.EngineDirectory, GetRemotePath(UnrealBuildTool.EngineDirectory), EngineFilters); // Upload the project files if (ProjectFile != null && !ProjectFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { List <FileReference> ProjectFilters = new List <FileReference>(); FileReference CustomProjectFilter = FileReference.Combine(ProjectFile.Directory, "Build", "Rsync", "RsyncProject.txt"); if (FileReference.Exists(CustomProjectFilter)) { ProjectFilters.Add(CustomProjectFilter); } ProjectFilters.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncProject.txt")); UploadDirectory(ProjectFile.Directory, GetRemotePath(ProjectFile.Directory), ProjectFilters); } }
/// <summary> /// /// </summary> public void Save(FileReference InFile) { DirectoryReference.CreateDirectory(InFile.Directory); FileReference.WriteAllText(InFile, fastJSON.JSON.Instance.ToJSON(this, new fastJSON.JSONParameters { })); }
/// <summary> /// Execute the command, having obtained the appropriate mutex /// </summary> /// <param name="Arguments">Command line arguments</param> /// <returns>Exit code</returns> private int ExecuteInternal(CommandLineArguments Arguments) { // Read the target info WriteMetadataTargetInfo TargetInfo = BinaryFormatterUtils.Load <WriteMetadataTargetInfo>(Arguments.GetFileReference("-Input=")); bool bNoManifestChanges = Arguments.HasOption("-NoManifestChanges"); int VersionNumber = Arguments.GetInteger("-Version="); Arguments.CheckAllArgumentsUsed(); // Make sure the version number is correct if (VersionNumber != CurrentVersionNumber) { throw new BuildException("Version number to WriteMetadataMode is incorrect (expected {0}, got {1})", CurrentVersionNumber, VersionNumber); } // Check if we need to set a build id TargetReceipt Receipt = TargetInfo.Receipt; if (String.IsNullOrEmpty(Receipt.Version.BuildId)) { // Check if there's an existing version file. If it exists, try to merge in any manifests that are valid (and reuse the existing build id) BuildVersion PreviousVersion; if (TargetInfo.VersionFile != null && BuildVersion.TryRead(TargetInfo.VersionFile, out PreviousVersion)) { // Check if we can reuse the existing manifests. This prevents unnecessary builds when switching between projects. Dictionary <FileReference, ModuleManifest> PreviousFileToManifest = new Dictionary <FileReference, ModuleManifest>(); if (TryRecyclingManifests(PreviousVersion.BuildId, TargetInfo.FileToManifest.Keys, PreviousFileToManifest)) { // Merge files from the existing manifests with the new ones foreach (KeyValuePair <FileReference, ModuleManifest> Pair in PreviousFileToManifest) { ModuleManifest TargetManifest = TargetInfo.FileToManifest[Pair.Key]; MergeManifests(Pair.Value, TargetManifest); } // Update the build id to use the current one Receipt.Version.BuildId = PreviousVersion.BuildId; } } // If the build id is still not set, generate a new one from a GUID if (String.IsNullOrEmpty(Receipt.Version.BuildId)) { Receipt.Version.BuildId = Guid.NewGuid().ToString(); } } else { // Read all the manifests and merge them into the new ones, if they have the same build id foreach (KeyValuePair <FileReference, ModuleManifest> Pair in TargetInfo.FileToManifest) { ModuleManifest SourceManifest; if (TryReadManifest(Pair.Key, out SourceManifest) && SourceManifest.BuildId == Receipt.Version.BuildId) { MergeManifests(SourceManifest, Pair.Value); } } } // Update the build id in all the manifests, and write them out foreach (KeyValuePair <FileReference, ModuleManifest> Pair in TargetInfo.FileToManifest) { FileReference ManifestFile = Pair.Key; if (!UnrealBuildTool.IsFileInstalled(ManifestFile)) { ModuleManifest Manifest = Pair.Value; Manifest.BuildId = Receipt.Version.BuildId; if (!FileReference.Exists(ManifestFile)) { // If the file doesn't already exist, just write it out DirectoryReference.CreateDirectory(ManifestFile.Directory); Manifest.Write(ManifestFile); } else { // Otherwise write it to a buffer first string OutputText; using (StringWriter Writer = new StringWriter()) { Manifest.Write(Writer); OutputText = Writer.ToString(); } // And only write it to disk if it's been modified. Note that if a manifest is out of date, we should have generated a new build id causing the contents to differ. string CurrentText = FileReference.ReadAllText(ManifestFile); if (CurrentText != OutputText) { if (bNoManifestChanges) { Log.TraceError("Build modifies {0}. This is not permitted. Before:\n {1}\nAfter:\n {2}", ManifestFile, CurrentText.Replace("\n", "\n "), OutputText.Replace("\n", "\n ")); } else { FileReference.WriteAllText(ManifestFile, OutputText); } } } } } // Write out the version file, if it's changed. Since this file is next to the executable, it may be used by multiple targets, and we should avoid modifying it unless necessary. if (TargetInfo.VersionFile != null && !UnrealBuildTool.IsFileInstalled(TargetInfo.VersionFile)) { DirectoryReference.CreateDirectory(TargetInfo.VersionFile.Directory); StringWriter Writer = new StringWriter(); Receipt.Version.Write(Writer); string Text = Writer.ToString(); if (!FileReference.Exists(TargetInfo.VersionFile) || File.ReadAllText(TargetInfo.VersionFile.FullName) != Text) { File.WriteAllText(TargetInfo.VersionFile.FullName, Text); } } // Write out the receipt if (!UnrealBuildTool.IsFileInstalled(TargetInfo.ReceiptFile)) { DirectoryReference.CreateDirectory(TargetInfo.ReceiptFile.Directory); Receipt.Write(TargetInfo.ReceiptFile); } return(0); }
private bool WriteXcodeWorkspace() { bool bSuccess = true; StringBuilder WorkspaceDataContent = new StringBuilder(); WorkspaceDataContent.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append("<Workspace" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(" version = \"1.0\">" + ProjectFileGenerator.NewLine); System.Action <List <MasterProjectFolder> /* Folders */, string /* Ident */> AddProjectsFunction = null; AddProjectsFunction = (FolderList, Ident) => { int SchemeIndex = 0; foreach (XcodeProjectFolder CurFolder in FolderList) { WorkspaceDataContent.Append(Ident + " <Group" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " location = \"container:\" name = \"" + CurFolder.FolderName + "\">" + ProjectFileGenerator.NewLine); AddProjectsFunction(CurFolder.SubFolders, Ident + " "); List <ProjectFile> ChildProjects = new List <ProjectFile>(CurFolder.ChildProjects); ChildProjects.Sort((ProjectFile A, ProjectFile B) => { return(A.ProjectFilePath.GetFileName().CompareTo(B.ProjectFilePath.GetFileName())); }); foreach (ProjectFile CurProject in ChildProjects) { XcodeProjectFile XcodeProject = CurProject as XcodeProjectFile; if (XcodeProject != null) { WorkspaceDataContent.Append(Ident + " <FileRef" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " location = \"group:" + XcodeProject.ProjectFilePath.MakeRelativeTo(ProjectFileGenerator.MasterProjectPath) + "\">" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " </FileRef>" + ProjectFileGenerator.NewLine); // Also, update project's schemes index so that the schemes list order match projects order in the navigator FileReference SchemeManagementFile = XcodeProject.ProjectFilePath + "/xcuserdata/" + Environment.UserName + ".xcuserdatad/xcschemes/xcschememanagement.plist"; if (FileReference.Exists(SchemeManagementFile)) { string SchemeManagementContent = FileReference.ReadAllText(SchemeManagementFile); SchemeManagementContent = SchemeManagementContent.Replace("<key>orderHint</key>\n\t\t\t<integer>1</integer>", "<key>orderHint</key>\n\t\t\t<integer>" + SchemeIndex.ToString() + "</integer>"); FileReference.WriteAllText(SchemeManagementFile, SchemeManagementContent); SchemeIndex++; } } } WorkspaceDataContent.Append(Ident + " </Group>" + ProjectFileGenerator.NewLine); } }; AddProjectsFunction(RootFolder.SubFolders, ""); WorkspaceDataContent.Append("</Workspace>" + ProjectFileGenerator.NewLine); string ProjectName = MasterProjectName; if (ProjectFilePlatform != XcodeProjectFilePlatform.All) { ProjectName += ProjectFilePlatform == XcodeProjectFilePlatform.Mac ? "_Mac" : (ProjectFilePlatform == XcodeProjectFilePlatform.iOS ? "_IOS" : "_TVOS"); } string WorkspaceDataFilePath = MasterProjectPath + "/" + ProjectName + ".xcworkspace/contents.xcworkspacedata"; bSuccess = WriteFileIfChanged(WorkspaceDataFilePath, WorkspaceDataContent.ToString(), new UTF8Encoding()); if (bSuccess) { string WorkspaceSettingsFilePath = MasterProjectPath + "/" + ProjectName + ".xcworkspace/xcuserdata/" + Environment.UserName + ".xcuserdatad/WorkspaceSettings.xcsettings"; bSuccess = WriteWorkspaceSettingsFile(WorkspaceSettingsFilePath); } return(bSuccess); }
/// <summary> /// Build a target remotely /// </summary> /// <param name="TargetDesc">Descriptor for the target to build</param> /// <param name="RemoteLogFile">Path to store the remote log file</param> /// <returns>True if the build succeeded, false otherwise</returns> public bool Build(TargetDescriptor TargetDesc, FileReference RemoteLogFile) { // Get the directory for working files DirectoryReference BaseDir = DirectoryReference.FromFile(TargetDesc.ProjectFile) ?? UnrealBuildTool.EngineDirectory; DirectoryReference TempDir = DirectoryReference.Combine(BaseDir, "Intermediate", "Remote", TargetDesc.Name, TargetDesc.Platform.ToString(), TargetDesc.Configuration.ToString()); DirectoryReference.CreateDirectory(TempDir); // Compile the rules assembly RulesCompiler.CreateTargetRulesAssembly(TargetDesc.ProjectFile, TargetDesc.Name, false, false, TargetDesc.ForeignPlugin); // Path to the local manifest file. This has to be translated from the remote format after the build is complete. List <FileReference> LocalManifestFiles = new List <FileReference>(); // Path to the remote manifest file FileReference RemoteManifestFile = FileReference.Combine(TempDir, "Manifest.xml"); // Prepare the arguments we will pass to the remote build List <string> RemoteArguments = new List <string>(); RemoteArguments.Add(TargetDesc.Name); RemoteArguments.Add(TargetDesc.Platform.ToString()); RemoteArguments.Add(TargetDesc.Configuration.ToString()); RemoteArguments.Add("-SkipRulesCompile"); // Use the rules assembly built locally RemoteArguments.Add(String.Format("-XmlConfigCache={0}", GetRemotePath(XmlConfig.CacheFile))); // Use the XML config cache built locally, since the remote won't have it RemoteArguments.Add(String.Format("-Log={0}", GetRemotePath(RemoteLogFile))); RemoteArguments.Add(String.Format("-Manifest={0}", GetRemotePath(RemoteManifestFile))); if (TargetDesc.ProjectFile != null) { RemoteArguments.Add(String.Format("-Project={0}", GetRemotePath(TargetDesc.ProjectFile))); } foreach (string LocalArgument in TargetDesc.AdditionalArguments) { int EqualsIdx = LocalArgument.IndexOf('='); if (EqualsIdx == -1) { RemoteArguments.Add(LocalArgument); continue; } string Key = LocalArgument.Substring(0, EqualsIdx); string Value = LocalArgument.Substring(EqualsIdx + 1); if (Key.Equals("-Log", StringComparison.InvariantCultureIgnoreCase)) { // We are already writing to the local log file. The remote will produce a different log (RemoteLogFile) continue; } if (Key.Equals("-Manifest", StringComparison.InvariantCultureIgnoreCase)) { LocalManifestFiles.Add(new FileReference(Value)); continue; } string RemoteArgument = LocalArgument; foreach (RemoteMapping Mapping in Mappings) { if (Value.StartsWith(Mapping.LocalDirectory.FullName, StringComparison.InvariantCultureIgnoreCase)) { RemoteArgument = String.Format("{0}={1}", Key, GetRemotePath(Value)); break; } } RemoteArguments.Add(RemoteArgument); } // Handle any per-platform setup that is required if (TargetDesc.Platform == UnrealTargetPlatform.IOS || TargetDesc.Platform == UnrealTargetPlatform.TVOS) { // Always generate a .stub RemoteArguments.Add("-CreateStub"); // Get the provisioning data for this project IOSProvisioningData ProvisioningData = ((IOSPlatform)UEBuildPlatform.GetBuildPlatform(TargetDesc.Platform)).ReadProvisioningData(TargetDesc.ProjectFile); if (ProvisioningData == null || ProvisioningData.MobileProvisionFile == null) { throw new BuildException("Unable to find mobile provision for {0}. See log for more information.", TargetDesc.Name); } // Create a local copy of the provision FileReference MobileProvisionFile = FileReference.Combine(TempDir, ProvisioningData.MobileProvisionFile.GetFileName()); if (FileReference.Exists(MobileProvisionFile)) { FileReference.SetAttributes(MobileProvisionFile, FileAttributes.Normal); } FileReference.Copy(ProvisioningData.MobileProvisionFile, MobileProvisionFile, true); Log.TraceInformation("[Remote] Uploading {0}", MobileProvisionFile); UploadFile(MobileProvisionFile); // Extract the certificate for the project. Try to avoid calling IPP if we already have it. FileReference CertificateFile = FileReference.Combine(TempDir, "Certificate.p12"); FileReference CertificateInfoFile = FileReference.Combine(TempDir, "Certificate.txt"); string CertificateInfoContents = String.Format("{0}\n{1}", ProvisioningData.MobileProvisionFile, FileReference.GetLastWriteTimeUtc(ProvisioningData.MobileProvisionFile).Ticks); if (!FileReference.Exists(CertificateFile) || !FileReference.Exists(CertificateInfoFile) || FileReference.ReadAllText(CertificateInfoFile) != CertificateInfoContents) { Log.TraceInformation("[Remote] Exporting certificate for {0}...", ProvisioningData.MobileProvisionFile); StringBuilder Arguments = new StringBuilder("ExportCertificate"); if (TargetDesc.ProjectFile == null) { Arguments.AppendFormat(" \"{0}\"", UnrealBuildTool.EngineSourceDirectory); } else { Arguments.AppendFormat(" \"{0}\"", TargetDesc.ProjectFile.Directory); } Arguments.AppendFormat(" -provisionfile \"{0}\"", ProvisioningData.MobileProvisionFile); Arguments.AppendFormat(" -outputcertificate \"{0}\"", CertificateFile); if (TargetDesc.Platform == UnrealTargetPlatform.TVOS) { Arguments.Append(" -tvos"); } ProcessStartInfo StartInfo = new ProcessStartInfo(); StartInfo.FileName = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "DotNET", "IOS", "IPhonePackager.exe").FullName; StartInfo.Arguments = Arguments.ToString(); if (Utils.RunLocalProcessAndLogOutput(StartInfo) != 0) { throw new BuildException("IphonePackager failed."); } FileReference.WriteAllText(CertificateInfoFile, CertificateInfoContents); } // Upload the certificate to the remote Log.TraceInformation("[Remote] Uploading {0}", CertificateFile); UploadFile(CertificateFile); // Tell the remote UBT instance to use them RemoteArguments.Add(String.Format("-ImportProvision={0}", GetRemotePath(MobileProvisionFile))); RemoteArguments.Add(String.Format("-ImportCertificate={0}", GetRemotePath(CertificateFile))); RemoteArguments.Add(String.Format("-ImportCertificatePassword=A")); } // Upload the workspace files UploadWorkspace(TempDir); // Fixup permissions on any shell scripts Execute(RemoteBaseDir, String.Format("chmod +x {0}/Build/BatchFiles/Mac/*.sh", EscapeShellArgument(GetRemotePath(UnrealBuildTool.EngineDirectory)))); // Execute the compile Log.TraceInformation("[Remote] Executing build"); StringBuilder BuildCommandLine = new StringBuilder("Engine/Build/BatchFiles/Mac/Build.sh"); foreach (string RemoteArgument in RemoteArguments) { BuildCommandLine.AppendFormat(" {0}", EscapeShellArgument(RemoteArgument)); } int Result = Execute(GetRemotePath(UnrealBuildTool.RootDirectory), BuildCommandLine.ToString()); if (Result != 0) { if (RemoteLogFile != null) { Log.TraceInformation("[Remote] Downloading {0}", RemoteLogFile); DownloadFile(RemoteLogFile); } return(false); } // Download the manifest Log.TraceInformation("[Remote] Downloading {0}", RemoteManifestFile); DownloadFile(RemoteManifestFile); // Convert the manifest to local form BuildManifest Manifest = Utils.ReadClass <BuildManifest>(RemoteManifestFile.FullName); for (int Idx = 0; Idx < Manifest.BuildProducts.Count; Idx++) { Manifest.BuildProducts[Idx] = GetLocalPath(Manifest.BuildProducts[Idx]).FullName; } // Download the files from the remote if (TargetDesc.AdditionalArguments.Any(x => x.Equals("-GenerateManifest", StringComparison.InvariantCultureIgnoreCase))) { LocalManifestFiles.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "Manifest.xml")); } else { Log.TraceInformation("[Remote] Downloading build products"); List <FileReference> FilesToDownload = new List <FileReference>(); FilesToDownload.Add(RemoteLogFile); FilesToDownload.AddRange(Manifest.BuildProducts.Select(x => new FileReference(x))); DownloadFiles(FilesToDownload); } // Write out all the local manifests foreach (FileReference LocalManifestFile in LocalManifestFiles) { Log.TraceInformation("[Remote] Writing {0}", LocalManifestFile); Utils.WriteClass <BuildManifest>(Manifest, LocalManifestFile.FullName, ""); } return(true); }
/// <summary> /// /// </summary> public void Save(FileReference InFile) { FileReference.WriteAllText(InFile, fastJSON.JSON.Instance.ToJSON(this, new fastJSON.JSONParameters { })); }
public override int Execute(CommandLineArguments Arguments) { FileReference ManifestFile = Arguments.GetFileReference("-ManifestFile="); string[] ParsedFileNames = FileReference.ReadAllLines(ManifestFile); // Load all the file timing data and summarize them for the aggregate. FileTimingData = new TimingData() { Name = "Files", Type = TimingDataType.Summary }; Parallel.ForEach(ParsedFileNames, new ParallelOptions() { MaxDegreeOfParallelism = 4 }, ParseTimingDataFile); // Create aggregate summary. Duration is the duration of the files in the aggregate. string AggregateName = Arguments.GetString("-Name="); TimingData AggregateData = new TimingData() { Name = AggregateName, Type = TimingDataType.Aggregate }; AggregateData.AddChild(FileTimingData); // Group the includes, classes, and functions by name and sum them up then add to the aggregate. GroupTimingDataOnName(AggregateData, "Include Timings", AggregateIncludes); GroupTimingDataOnName(AggregateData, "Class Timings", AggregateClasses); GroupTimingDataOnName(AggregateData, "Function Timings", AggregateFunctions); // Write out aggregate summary. string OutputFile = Path.Combine(ManifestFile.Directory.FullName, String.Format("{0}.timing.bin", AggregateName)); using (BinaryWriter Writer = new BinaryWriter(File.Open(OutputFile, FileMode.Create))) { // Write out the aggregate data. Writer.Write(AggregateData); // Write the look up table for the compressed binary blobs. int Offset = 0; Writer.Write(CompressedFiles.Count); foreach (KeyValuePair <string, byte[]> CompressedFile in CompressedFiles) { Writer.Write(CompressedFile.Key); Writer.Write(Offset); Writer.Write(CompressedFile.Value.Length); Writer.Write(DecompressedFileSizes[CompressedFile.Key]); Offset += CompressedFile.Value.Length; } // Write the compressed binary blobs. foreach (KeyValuePair <string, byte[]> CompressedFile in CompressedFiles) { Writer.Write(CompressedFile.Value); } } if (Arguments.HasValue("-CompileTimingFile=")) { FileReference CompileTimingFile = Arguments.GetFileReference("-CompileTimingFile="); Dictionary <string, double> CompileTimes = new Dictionary <string, double>(); foreach (KeyValuePair <string, TimingData> TimingData in FileTimingData.Children) { CompileTimes.Add(Json.EscapeString(TimingData.Key), TimingData.Value.InclusiveDuration); } string JsonCompileTimes = Json.Serialize(CompileTimes); FileReference.WriteAllText(CompileTimingFile, JsonCompileTimes); } return(0); }
private bool WriteXcodeWorkspace() { bool bSuccess = true; StringBuilder WorkspaceDataContent = new StringBuilder(); WorkspaceDataContent.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append("<Workspace" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(" version = \"1.0\">" + ProjectFileGenerator.NewLine); List <XcodeProjectFile> BuildableProjects = new List <XcodeProjectFile>(); System.Action <List <MasterProjectFolder> /* Folders */, string /* Ident */> AddProjectsFunction = null; AddProjectsFunction = (FolderList, Ident) => { foreach (XcodeProjectFolder CurFolder in FolderList) { WorkspaceDataContent.Append(Ident + " <Group" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " location = \"container:\" name = \"" + CurFolder.FolderName + "\">" + ProjectFileGenerator.NewLine); AddProjectsFunction(CurFolder.SubFolders, Ident + " "); // Filter out anything that isn't an XC project, and that shouldn't be in the workspace IEnumerable <XcodeProjectFile> SupportedProjects = CurFolder.ChildProjects .Where(P => P is XcodeProjectFile) .Select(P => P as XcodeProjectFile) .Where(P => P.ShouldIncludeProjectInWorkspace()) .OrderBy(P => P.ProjectFilePath.GetFileName()); foreach (XcodeProjectFile XcodeProject in SupportedProjects) { WorkspaceDataContent.Append(Ident + " <FileRef" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " location = \"group:" + XcodeProject.ProjectFilePath.MakeRelativeTo(ProjectFileGenerator.MasterProjectPath) + "\">" + ProjectFileGenerator.NewLine); WorkspaceDataContent.Append(Ident + " </FileRef>" + ProjectFileGenerator.NewLine); } BuildableProjects.AddRange(SupportedProjects); WorkspaceDataContent.Append(Ident + " </Group>" + ProjectFileGenerator.NewLine); } }; AddProjectsFunction(RootFolder.SubFolders, ""); WorkspaceDataContent.Append("</Workspace>" + ProjectFileGenerator.NewLine); // Also, update project's schemes index so that the schemes are in a sensible order // (Game, Editor, Client, Server, Programs) int SchemeIndex = 0; BuildableProjects.Sort((ProjA, ProjB) => { var TargetA = ProjA.ProjectTargets.OrderBy(T => T.TargetRules.Type).FirstOrDefault(); var TargetB = ProjB.ProjectTargets.OrderBy(T => T.TargetRules.Type).FirstOrDefault(); var TypeA = TargetA != null ? TargetA.TargetRules.Type : TargetType.Program; var TypeB = TargetB != null ? TargetB.TargetRules.Type : TargetType.Program; if (TypeA != TypeB) { return(TypeA.CompareTo(TypeB)); } return(TargetA.Name.CompareTo(TargetB.Name)); }); foreach (XcodeProjectFile XcodeProject in BuildableProjects) { FileReference SchemeManagementFile = XcodeProject.ProjectFilePath + "/xcuserdata/" + Environment.UserName + ".xcuserdatad/xcschemes/xcschememanagement.plist"; if (FileReference.Exists(SchemeManagementFile)) { string SchemeManagementContent = FileReference.ReadAllText(SchemeManagementFile); SchemeManagementContent = SchemeManagementContent.Replace("<key>orderHint</key>\n\t\t\t<integer>1</integer>", "<key>orderHint</key>\n\t\t\t<integer>" + SchemeIndex.ToString() + "</integer>"); FileReference.WriteAllText(SchemeManagementFile, SchemeManagementContent); SchemeIndex++; } } string ProjectName = MasterProjectName; if (ProjectFilePlatform != XcodeProjectFilePlatform.All) { ProjectName += ProjectFilePlatform == XcodeProjectFilePlatform.Mac ? "_Mac" : (ProjectFilePlatform == XcodeProjectFilePlatform.iOS ? "_IOS" : "_TVOS"); } string WorkspaceDataFilePath = MasterProjectPath + "/" + ProjectName + ".xcworkspace/contents.xcworkspacedata"; bSuccess = WriteFileIfChanged(WorkspaceDataFilePath, WorkspaceDataContent.ToString(), new UTF8Encoding()); if (bSuccess) { string WorkspaceSettingsFilePath = MasterProjectPath + "/" + ProjectName + ".xcworkspace/xcuserdata/" + Environment.UserName + ".xcuserdatad/WorkspaceSettings.xcsettings"; bSuccess = WriteWorkspaceSettingsFile(WorkspaceSettingsFilePath); } return(bSuccess); }