/// <summary> /// Inserts variables to make a file relative to $(EngineDir) or $(ProjectDir) /// </summary> /// <param name="File">The file to insert variables into.</param> /// <param name="EngineDir">Value of the $(EngineDir) variable.</param> /// <param name="ProjectDir">Value of the $(ProjectDir) variable.</param> /// <returns>Converted path for the file.</returns> static string InsertPathVariables(FileReference File, DirectoryReference EngineDir, DirectoryReference ProjectDir) { if (File.IsUnderDirectory(EngineDir)) { return("$(EngineDir)/" + File.MakeRelativeTo(EngineDir).Replace(Path.DirectorySeparatorChar, '/')); } else if (ProjectDir != null && File.IsUnderDirectory(ProjectDir)) { return("$(ProjectDir)/" + File.MakeRelativeTo(ProjectDir).Replace(Path.DirectorySeparatorChar, '/')); } else { return(File.FullName); } }
/// <summary> /// Inserts variables to make a file relative to $(EngineDir) or $(ProjectDir) /// </summary> /// <param name="File">The file to insert variables into.</param> /// <param name="EngineDir">Value of the $(EngineDir) variable.</param> /// <param name="ProjectDir">Value of the $(ProjectDir) variable.</param> /// <returns>Converted path for the file.</returns> public static string InsertPathVariables(FileReference File, DirectoryReference EngineDir, DirectoryReference ProjectDir) { if (File.IsUnderDirectory(EngineDir)) { return("$(EngineDir)" + Path.DirectorySeparatorChar + File.MakeRelativeTo(EngineDir)); } else if (File.IsUnderDirectory(ProjectDir)) { return("$(ProjectDir)" + Path.DirectorySeparatorChar + File.MakeRelativeTo(ProjectDir)); } else { return(File.FullName); } }
/// <summary> /// Write the receipt to disk. /// </summary> /// <param name="Location">Output filename</param> /// <param name="EngineDir">Engine directory for expanded paths</param> public void Write(FileReference Location, DirectoryReference EngineDir) { using (JsonWriter Writer = new JsonWriter(Location.FullName)) { Writer.WriteObjectStart(); Writer.WriteValue("TargetName", TargetName); Writer.WriteValue("Platform", Platform.ToString()); Writer.WriteValue("Configuration", Configuration.ToString()); Writer.WriteValue("TargetType", TargetType.ToString()); if (ProjectFile != null) { Writer.WriteValue("Project", ProjectFile.MakeRelativeTo(Location.Directory).Replace(Path.DirectorySeparatorChar, '/')); } if (Launch != null) { Writer.WriteValue("Launch", InsertPathVariables(Launch, EngineDir, ProjectDir)); } Writer.WriteObjectStart("Version"); Version.WriteProperties(Writer); Writer.WriteObjectEnd(); Writer.WriteArrayStart("BuildProducts"); foreach (BuildProduct BuildProduct in BuildProducts) { Writer.WriteObjectStart(); Writer.WriteValue("Path", InsertPathVariables(BuildProduct.Path, EngineDir, ProjectDir)); Writer.WriteValue("Type", BuildProduct.Type.ToString()); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); Writer.WriteArrayStart("RuntimeDependencies"); foreach (RuntimeDependency RuntimeDependency in RuntimeDependencies) { Writer.WriteObjectStart(); Writer.WriteValue("Path", InsertPathVariables(RuntimeDependency.Path, EngineDir, ProjectDir)); Writer.WriteValue("Type", RuntimeDependency.Type.ToString()); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); if (AdditionalProperties.Count > 0) { Writer.WriteArrayStart("AdditionalProperties"); foreach (ReceiptProperty AdditionalProperty in AdditionalProperties) { Writer.WriteObjectStart(); Writer.WriteValue("Name", AdditionalProperty.Name); Writer.WriteValue("Value", AdditionalProperty.Value); Writer.WriteObjectEnd(); } Writer.WriteArrayEnd(); } Writer.WriteObjectEnd(); } }
/// <summary> /// Takes the given path and tries to rebase it relative to the project. /// </summary> public string NormalizeProjectPath(FileReference InputPath) { // Try to make it relative to the solution directory. if (InputPath.IsUnderDirectory(ProjectFileGenerator.MasterProjectPath)) { return(InputPath.MakeRelativeTo(ProjectFileGenerator.IntermediateProjectFilesPath)); } else { return(InputPath.FullName); } }
/// <summary> /// Adds a rule which matches a filename relative to a given base directory. /// </summary> /// <param name="File">The filename to add a rule for</param> /// <param name="BaseDirectory">Base directory for the rule</param> /// <param name="Type">Whether to add an include or exclude rule</param> public void AddRuleForFile(FileReference File, DirectoryReference BaseDirectory, FileFilterType Type) { AddRule("/" + File.MakeRelativeTo(BaseDirectory), Type); }
/// <summary> /// Downloads a single file from the remote /// </summary> /// <param name="LocalFile">The file to download</param> void DownloadFile(FileReference LocalFile) { RemoteMapping Mapping = Mappings.FirstOrDefault(x => LocalFile.IsUnderDirectory(x.LocalDirectory)); if (Mapping == null) { throw new BuildException("File for download '{0}' is not under any mapped directory.", LocalFile); } List <string> Arguments = new List <string>(CommonRsyncArguments); Arguments.Add(String.Format("\"{0}@{1}\":'{2}/{3}'", UserName, ServerName, Mapping.RemoteDirectory, LocalFile.MakeRelativeTo(Mapping.LocalDirectory).Replace('\\', '/'))); Arguments.Add(String.Format("\"{0}/\"", GetLocalCygwinPath(LocalFile.Directory))); Arguments.Add("-q"); int Result = Rsync(String.Join(" ", Arguments)); if (Result != 0) { throw new BuildException("Unable to download '{0}' from the remote Mac (exit code {1}).", LocalFile, Result); } }
/// <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); } }
protected override bool WriteMasterProjectFile(ProjectFile UBTProject, PlatformProjectGeneratorCollection PlatformProjectGenerators) { bool bSuccess = true; string SolutionFileName = MasterProjectName + ".sln"; // Setup solution file content StringBuilder VCSolutionFileContent = new StringBuilder(); // Solution file header. Note that a leading newline is required for file type detection to work correclty in the shell. if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2019) { VCSolutionFileContent.AppendLine(); VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio Version 16"); VCSolutionFileContent.AppendLine("VisualStudioVersion = 16.0.28315.86"); VCSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2017) { VCSolutionFileContent.AppendLine(); VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 15"); VCSolutionFileContent.AppendLine("VisualStudioVersion = 15.0.25807.0"); VCSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2015) { VCSolutionFileContent.AppendLine(); VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 14"); VCSolutionFileContent.AppendLine("VisualStudioVersion = 14.0.22310.1"); VCSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2013) { VCSolutionFileContent.AppendLine(); VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 2013"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012) { VCSolutionFileContent.AppendLine(); VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 2012"); } else { throw new BuildException("Unexpected ProjectFileFormat"); } // Solution folders, files and project entries { // This the GUID that Visual Studio uses to identify a solution folder string SolutionFolderEntryGUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; // Solution folders { List <MasterProjectFolder> AllSolutionFolders = new List <MasterProjectFolder>(); System.Action <List <MasterProjectFolder> /* Folders */> GatherFoldersFunction = null; GatherFoldersFunction = FolderList => { AllSolutionFolders.AddRange(FolderList); foreach (MasterProjectFolder CurSubFolder in FolderList) { GatherFoldersFunction(CurSubFolder.SubFolders); } }; GatherFoldersFunction(RootFolder.SubFolders); AllSolutionFolders.Sort((Lhs, Rhs) => Lhs.FolderName.CompareTo(Rhs.FolderName)); foreach (VisualStudioSolutionFolder CurFolder in AllSolutionFolders) { string FolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("Project(\"" + SolutionFolderEntryGUID + "\") = \"" + CurFolder.FolderName + "\", \"" + CurFolder.FolderName + "\", \"" + FolderGUIDString + "\""); // Add any files that are inlined right inside the solution folder if (CurFolder.Files.Count > 0) { VCSolutionFileContent.AppendLine(" ProjectSection(SolutionItems) = preProject"); foreach (string CurFile in CurFolder.Files) { // Syntax is: <relative file path> = <relative file path> VCSolutionFileContent.AppendLine(" "+ CurFile + " = " + CurFile); } VCSolutionFileContent.AppendLine(" EndProjectSection"); } VCSolutionFileContent.AppendLine("EndProject"); } } // Project files List <MSBuildProjectFile> AllProjectFilesSorted = AllProjectFiles.OrderBy((ProjFile) => ProjFile.ProjectFilePath.GetFileNameWithoutExtension()).Cast <MSBuildProjectFile>().ToList(); foreach (MSBuildProjectFile CurProject in AllProjectFiles) { // Visual Studio uses different GUID types depending on the project type string ProjectTypeGUID = CurProject.ProjectTypeGUID; // NOTE: The project name in the solution doesn't actually *have* to match the project file name on disk. However, // we prefer it when it does match so we use the actual file name here. string ProjectNameInSolution = CurProject.ProjectFilePath.GetFileNameWithoutExtension(); // Use the existing project's GUID that's already known to us string ProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("Project(\"" + ProjectTypeGUID + "\") = \"" + ProjectNameInSolution + "\", \"" + CurProject.ProjectFilePath.MakeRelativeTo(ProjectFileGenerator.MasterProjectPath) + "\", \"" + ProjectGUID + "\""); // Setup dependency on UnrealBuildTool, if we need that. This makes sure that UnrealBuildTool is // freshly compiled before kicking off any build operations on this target project if (!CurProject.IsStubProject) { List <ProjectFile> Dependencies = new List <ProjectFile>(); if (CurProject.IsGeneratedProject && UBTProject != null && CurProject != UBTProject) { Dependencies.Add(UBTProject); Dependencies.AddRange(UBTProject.DependsOnProjects); } Dependencies.AddRange(CurProject.DependsOnProjects); if (Dependencies.Count > 0) { VCSolutionFileContent.AppendLine("\tProjectSection(ProjectDependencies) = postProject"); // Setup any addition dependencies this project has... foreach (ProjectFile DependsOnProject in Dependencies) { string DependsOnProjectGUID = ((MSBuildProjectFile)DependsOnProject).ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("\t\t" + DependsOnProjectGUID + " = " + DependsOnProjectGUID); } VCSolutionFileContent.AppendLine("\tEndProjectSection"); } } VCSolutionFileContent.AppendLine("EndProject"); } // Get the path to the visualizers file. Try to make it relative to the solution directory, but fall back to a full path if it's a foreign project. FileReference VisualizersFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Extras", "VisualStudioDebugging", "UE4.natvis"); // Add the visualizers at the solution level. Doesn't seem to be picked up from a makefile project in VS2017 15.8.5. VCSolutionFileContent.AppendLine(String.Format("Project(\"{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}\") = \"Visualizers\", \"Visualizers\", \"{0}\"", Guid.NewGuid().ToString("B").ToUpperInvariant())); VCSolutionFileContent.AppendLine("\tProjectSection(SolutionItems) = preProject"); VCSolutionFileContent.AppendLine("\t\t{0} = {0}", VisualizersFile.MakeRelativeTo(MasterProjectPath)); VCSolutionFileContent.AppendLine("\tEndProjectSection"); VCSolutionFileContent.AppendLine("EndProject"); } // Solution configuration platforms. This is just a list of all of the platforms and configurations that // appear in Visual Studio's build configuration selector. List <VCSolutionConfigCombination> SolutionConfigCombinations = new List <VCSolutionConfigCombination>(); // The "Global" section has source control, solution configurations, project configurations, // preferences, and project hierarchy data { VCSolutionFileContent.AppendLine("Global"); { { VCSolutionFileContent.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution"); Dictionary <string, Tuple <UnrealTargetConfiguration, TargetType> > SolutionConfigurationsValidForProjects = new Dictionary <string, Tuple <UnrealTargetConfiguration, TargetType> >(); HashSet <UnrealTargetPlatform> PlatformsValidForProjects = new HashSet <UnrealTargetPlatform>(); foreach (UnrealTargetConfiguration CurConfiguration in SupportedConfigurations) { if (InstalledPlatformInfo.IsValidConfiguration(CurConfiguration, EProjectType.Code)) { foreach (UnrealTargetPlatform CurPlatform in SupportedPlatforms) { if (InstalledPlatformInfo.IsValidPlatform(CurPlatform, EProjectType.Code)) { foreach (ProjectFile CurProject in AllProjectFiles) { if (!CurProject.IsStubProject) { if (CurProject.ProjectTargets.Count == 0) { throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!"); } // Figure out the set of valid target configuration names foreach (ProjectTarget ProjectTarget in CurProject.ProjectTargets) { if (VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, CurPlatform, CurConfiguration, PlatformProjectGenerators)) { PlatformsValidForProjects.Add(CurPlatform); // Default to a target configuration name of "Game", since that will collapse down to an empty string TargetType TargetType = TargetType.Game; if (ProjectTarget.TargetRules != null) { TargetType = ProjectTarget.TargetRules.Type; } string SolutionConfigName = MakeSolutionConfigurationName(CurConfiguration, TargetType); SolutionConfigurationsValidForProjects[SolutionConfigName] = new Tuple <UnrealTargetConfiguration, TargetType>(CurConfiguration, TargetType); } } } } } } } } foreach (UnrealTargetPlatform CurPlatform in PlatformsValidForProjects) { foreach (KeyValuePair <string, Tuple <UnrealTargetConfiguration, TargetType> > SolutionConfigKeyValue in SolutionConfigurationsValidForProjects) { // e.g. "Development|Win64 = Development|Win64" string SolutionConfigName = SolutionConfigKeyValue.Key; UnrealTargetConfiguration Configuration = SolutionConfigKeyValue.Value.Item1; TargetType TargetType = SolutionConfigKeyValue.Value.Item2; string SolutionPlatformName = CurPlatform.ToString(); string SolutionConfigAndPlatformPair = SolutionConfigName + "|" + SolutionPlatformName; SolutionConfigCombinations.Add( new VCSolutionConfigCombination { VCSolutionConfigAndPlatformName = SolutionConfigAndPlatformPair, Configuration = Configuration, Platform = CurPlatform, TargetConfigurationName = TargetType } ); } } // Sort the list of solution platform strings alphabetically (Visual Studio prefers it) SolutionConfigCombinations.Sort( new Comparison <VCSolutionConfigCombination>( (x, y) => { return(String.Compare(x.VCSolutionConfigAndPlatformName, y.VCSolutionConfigAndPlatformName, StringComparison.InvariantCultureIgnoreCase)); } ) ); HashSet <string> AppendedSolutionConfigAndPlatformNames = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); foreach (VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations) { // We alias "Game" and "Program" to both have the same solution configuration, so we're careful not to add the same combination twice. if (!AppendedSolutionConfigAndPlatformNames.Contains(SolutionConfigCombination.VCSolutionConfigAndPlatformName)) { VCSolutionFileContent.AppendLine(" "+ SolutionConfigCombination.VCSolutionConfigAndPlatformName + " = " + SolutionConfigCombination.VCSolutionConfigAndPlatformName); AppendedSolutionConfigAndPlatformNames.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName); } } VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Assign each project's "project configuration" to our "solution platform + configuration" pairs. This // also sets up which projects are actually built when building the solution. { VCSolutionFileContent.AppendLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (MSBuildProjectFile CurProject in AllProjectFiles) { foreach (VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations) { // Get the context for the current solution context MSBuildProjectContext ProjectContext = CurProject.GetMatchingProjectContext(SolutionConfigCombination.TargetConfigurationName, SolutionConfigCombination.Configuration, SolutionConfigCombination.Platform, PlatformProjectGenerators); // Write the solution mapping (e.g. "{4232C52C-680F-4850-8855-DC39419B5E9B}.Debug|iOS.ActiveCfg = iOS_Debug|Win32") string CurProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine(" {0}.{1}.ActiveCfg = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); if (ProjectContext.bBuildByDefault) { VCSolutionFileContent.AppendLine(" {0}.{1}.Build.0 = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); if (ProjectContext.bDeployByDefault) { VCSolutionFileContent.AppendLine(" {0}.{1}.Deploy.0 = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); } } } } VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Setup other solution properties { // HideSolutionNode sets whether or not the top-level solution entry is completely hidden in the UI. // We don't want that, as we need users to be able to right click on the solution tree item. VCSolutionFileContent.AppendLine(" GlobalSection(SolutionProperties) = preSolution"); VCSolutionFileContent.AppendLine(" HideSolutionNode = FALSE"); VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Solution directory hierarchy { VCSolutionFileContent.AppendLine(" GlobalSection(NestedProjects) = preSolution"); // Every entry in this section is in the format "Guid1 = Guid2". Guid1 is the child project (or solution // filter)'s GUID, and Guid2 is the solution filter directory to parent the child project (or solution // filter) to. This sets up the hierarchical solution explorer tree for all solution folders and projects. System.Action <StringBuilder /* VCSolutionFileContent */, List <MasterProjectFolder> /* Folders */> FolderProcessorFunction = null; FolderProcessorFunction = (LocalVCSolutionFileContent, LocalMasterProjectFolders) => { foreach (VisualStudioSolutionFolder CurFolder in LocalMasterProjectFolders) { string CurFolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); foreach (MSBuildProjectFile ChildProject in CurFolder.ChildProjects) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.AppendLine(" "+ ChildProject.ProjectGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString); } foreach (VisualStudioSolutionFolder SubFolder in CurFolder.SubFolders) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.AppendLine(" "+ SubFolder.FolderGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString); } // Recurse into subfolders FolderProcessorFunction(LocalVCSolutionFileContent, CurFolder.SubFolders); } }; FolderProcessorFunction(VCSolutionFileContent, RootFolder.SubFolders); VCSolutionFileContent.AppendLine(" EndGlobalSection"); } } VCSolutionFileContent.AppendLine("EndGlobal"); } // Save the solution file if (bSuccess) { string SolutionFilePath = FileReference.Combine(MasterProjectPath, SolutionFileName).FullName; bSuccess = WriteFileIfChanged(SolutionFilePath, VCSolutionFileContent.ToString()); } // Save a solution config file which selects the development editor configuration by default. if (bSuccess && bWriteSolutionOptionFile) { // Figure out the filename for the SUO file. VS will automatically import the options from earlier versions if necessary. FileReference SolutionOptionsFileName; switch (ProjectFileFormat) { case VCProjectFileFormat.VisualStudio2012: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, Path.ChangeExtension(SolutionFileName, "v11.suo")); break; case VCProjectFileFormat.VisualStudio2013: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, Path.ChangeExtension(SolutionFileName, "v12.suo")); break; case VCProjectFileFormat.VisualStudio2015: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, ".vs", Path.GetFileNameWithoutExtension(SolutionFileName), "v14", ".suo"); break; case VCProjectFileFormat.VisualStudio2017: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, ".vs", Path.GetFileNameWithoutExtension(SolutionFileName), "v15", ".suo"); break; case VCProjectFileFormat.VisualStudio2019: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, ".vs", Path.GetFileNameWithoutExtension(SolutionFileName), "v15", ".suo"); // Still uses v15 break; default: throw new BuildException("Unsupported Visual Studio version"); } // Check it doesn't exist before overwriting it. Since these files store the user's preferences, it'd be bad form to overwrite them. if (!FileReference.Exists(SolutionOptionsFileName)) { DirectoryReference.CreateDirectory(SolutionOptionsFileName.Directory); VCSolutionOptions Options = new VCSolutionOptions(ProjectFileFormat); // Set the default configuration and startup project VCSolutionConfigCombination DefaultConfig = SolutionConfigCombinations.Find(x => x.Configuration == UnrealTargetConfiguration.Development && x.Platform == UnrealTargetPlatform.Win64 && x.TargetConfigurationName == TargetType.Editor); if (DefaultConfig != null) { List <VCBinarySetting> Settings = new List <VCBinarySetting>(); Settings.Add(new VCBinarySetting("ActiveCfg", DefaultConfig.VCSolutionConfigAndPlatformName)); if (DefaultProject != null) { Settings.Add(new VCBinarySetting("StartupProject", ((MSBuildProjectFile)DefaultProject).ProjectGUID.ToString("B"))); } Options.SetConfiguration(Settings); } // Mark all the projects as closed by default, apart from the startup project VCSolutionExplorerState ExplorerState = new VCSolutionExplorerState(); if (ProjectFileFormat >= VCProjectFileFormat.VisualStudio2017) { BuildSolutionExplorerState_VS2017(RootFolder, "", ExplorerState, DefaultProject); } else { BuildSolutionExplorerState_VS2015(AllProjectFiles, ExplorerState, DefaultProject, IncludeEnginePrograms); } Options.SetExplorerState(ExplorerState); // Write the file if (Options.Sections.Count > 0) { Options.Write(SolutionOptionsFileName.FullName); } } } return(bSuccess); }
private void MakePackage(TargetReceipt Receipt, TargetReceipt NewReceipt, WindowsArchitecture Architecture, List <string> UpdatedFiles) { string OutputName = String.Format("{0}_{1}_{2}_{3}", Receipt.TargetName, Receipt.Platform, Receipt.Configuration, WindowsExports.GetArchitectureSubpath(Architecture)); string IntermediateDirectory = Path.Combine(Receipt.ProjectDir != null ? Receipt.ProjectDir.FullName : UnrealBuildTool.EngineDirectory.FullName, "Intermediate", "Deploy", WindowsExports.GetArchitectureSubpath(Architecture)); string OutputDirectory = Receipt.Launch.Directory.FullName; string OutputAppX = Path.Combine(OutputDirectory, OutputName + ".appx"); string SigningCertificate = @"Build\HoloLens\SigningCertificate.pfx"; string SigningCertificatePath = Path.Combine(Receipt.ProjectDir != null ? Receipt.ProjectDir.FullName : UnrealBuildTool.EngineDirectory.FullName, SigningCertificate); string MapFilename = Path.Combine(IntermediateDirectory, OutputName + ".pkgmap"); var LocalRoot = Receipt.ProjectDir; var EngineRoot = UnrealBuildTool.RootDirectory; var AddedFiles = new Dictionary <string, string>(); bool PackageFileNeedToBeUpdated = !File.Exists(OutputAppX); DateTime AppXTime = DateTime.Now; PackageFileNeedToBeUpdated = true; if (!PackageFileNeedToBeUpdated) { AppXTime = File.GetLastWriteTimeUtc(OutputAppX); } { foreach (var Product in Receipt.BuildProducts) { if (Product.Type == BuildProductType.Executable || Product.Type == BuildProductType.DynamicLibrary || Product.Type == BuildProductType.RequiredResource) { string Filename; if (AddedFiles.ContainsKey(Product.Path.FullName)) { continue; } if (LocalRoot != null && Product.Path.IsUnderDirectory(LocalRoot)) { Filename = Product.Path.MakeRelativeTo(LocalRoot.ParentDirectory); } else if (Product.Path.IsUnderDirectory(EngineRoot)) { Filename = Product.Path.MakeRelativeTo(EngineRoot); } else { throw new BuildException("Failed to parse target receipt file. See log for details."); } AddedFiles.Add(Product.Path.FullName, Filename); } } foreach (var Dep in Receipt.RuntimeDependencies) { if (Dep.Type == StagedFileType.NonUFS) { if (AddedFiles.ContainsKey(Dep.Path.FullName)) { continue; } string Filename; if (LocalRoot != null && Dep.Path.IsUnderDirectory(LocalRoot)) { Filename = Dep.Path.MakeRelativeTo(LocalRoot.ParentDirectory); } else if (Dep.Path.IsUnderDirectory(EngineRoot)) { Filename = Dep.Path.MakeRelativeTo(EngineRoot); } else { throw new BuildException("Failed to parse target receipt file. See log for details."); } AddedFiles.Add(Dep.Path.FullName, Filename); } } } string ManifestName = String.Format("AppxManifest_{0}.xml", WindowsExports.GetArchitectureSubpath(Architecture)); AddedFiles.Add(Path.Combine(OutputDirectory, ManifestName), "AppxManifest.xml"); //manually add resources string PriFileName = String.Format("resources_{0}.pri", WindowsExports.GetArchitectureSubpath(Architecture)); AddedFiles.Add(Path.Combine(OutputDirectory, PriFileName), "resources.pri"); { DirectoryReference ResourceFolder = DirectoryReference.Combine(Receipt.Launch.Directory, WindowsExports.GetArchitectureSubpath(Architecture)); foreach (var ResourcePath in UpdatedFiles) { var ResourceFile = new FileReference(ResourcePath); if (ResourceFile.IsUnderDirectory(ResourceFolder)) { AddedFiles.Add(ResourceFile.FullName, ResourceFile.MakeRelativeTo(ResourceFolder)); } else { Log.TraceError("Wrong path to resource \'{0}\', the resource should be in \'{1}\'", ResourceFile.FullName, ResourceFolder.FullName); throw new BuildException("Failed to generate AppX file. See log for details."); } } } FileReference SourceNetworkManifestPath = new FileReference(Path.Combine(OutputDirectory, "NetworkManifest.xml")); if (FileReference.Exists(SourceNetworkManifestPath)) { AddedFiles.Add(SourceNetworkManifestPath.FullName, "NetworkManifest.xml"); } FileReference SourceXboxConfigPath = new FileReference(Path.Combine(OutputDirectory, "xboxservices.config")); if (FileReference.Exists(SourceXboxConfigPath)) { AddedFiles.Add(SourceXboxConfigPath.FullName, "xboxservices.config"); } do { if (PackageFileNeedToBeUpdated) { break; } if (!File.Exists(MapFilename)) { PackageFileNeedToBeUpdated = true; break; } string[] lines = File.ReadAllLines(MapFilename, Encoding.UTF8); int filesCount = 0; foreach (var line in lines) { if (line[0] == '[') { continue; } string[] files = line.Split('\t'); if (files.Length != 2) { PackageFileNeedToBeUpdated = true; break; } files[0] = files[0].Trim('\"'); files[1] = files[1].Trim('\"'); if (!AddedFiles.ContainsKey(files[0])) { PackageFileNeedToBeUpdated = true; break; } if (AddedFiles[files[0]] != files[1]) { PackageFileNeedToBeUpdated = true; break; } if (File.GetLastWriteTimeUtc(files[0]).CompareTo(AppXTime) >= 0) { PackageFileNeedToBeUpdated = true; break; } ++filesCount; } if (PackageFileNeedToBeUpdated) { break; } if (filesCount != AddedFiles.Count) { PackageFileNeedToBeUpdated = true; break; } if (File.Exists(SigningCertificatePath) && File.GetLastWriteTimeUtc(SigningCertificatePath).CompareTo(AppXTime) >= 0) { PackageFileNeedToBeUpdated = true; break; } }while(false); if (!PackageFileNeedToBeUpdated) { NewReceipt.BuildProducts.Add(new BuildProduct(new FileReference(OutputAppX), BuildProductType.Package)); return; } try { DeployHelper_DeleteFile(OutputAppX); } catch (Exception exceptionMessage) { Log.TraceError("Failed to delete {0} from deployment: {1}", OutputAppX, exceptionMessage); throw new BuildException("Failed to generate AppX file. See log for details."); } var AppXRecipeBuiltFiles = new StringBuilder(); AppXRecipeBuiltFiles.AppendLine(@"[Files]"); foreach (var f in AddedFiles) { AppXRecipeBuiltFiles.AppendLine(String.Format("\"{0}\"\t\"{1}\"", f.Key, f.Value)); } File.WriteAllText(MapFilename, AppXRecipeBuiltFiles.ToString(), Encoding.UTF8); NewReceipt.BuildProducts.Add(new BuildProduct(new FileReference(MapFilename), BuildProductType.MapFile)); }