Files
UnrealEngineUWP/Engine/Extras/VisualStudioWorkspace/UnrealBuildTool-5.2.patch
joe kirchoff 318c389593 Patches for Visual Studio workspace support in old versions
[CL 31275433 by joe kirchoff in ue5-main branch]
2024-02-07 18:03:23 -05:00

721 lines
30 KiB
Diff

From 377e0a81ea5d312c6703fc8728c9f61a30805307 Mon Sep 17 00:00:00 2001
From: Henrique Fernandes Baggio <hebaggio@microsoft.com>
Date: Wed, 17 Jan 2024 19:49:54 -0800
Subject: [PATCH] [UE5.2] Project Generator for VS Workspace support
Adding a new Project Generator for Visual Studio that will be used for
the new support to opening `.uproject` files directly in the IDE without
generating the traditional `.sln` and .vcxproj files.
This is based on the QueryMode API in `ue5-main` and adapted to the
GenerateProjectFiles flow. The project files will contain information for each
Target+Configuration+Platform combinations that are used the project.
Visual Studio will automatically invoke this new generator during the flow to
open a `.uproject` file directly.
---
.../Modes/GenerateProjectFilesMode.cs | 4 +
.../Platform/Windows/VCToolChain.cs | 21 ++
.../ProjectFiles/ProjectFileGenerator.cs | 5 +-
.../VSWorkspaceProjectFile.cs | 314 ++++++++++++++++++
.../VSWorkspaceProjectFileGenerator.cs | 258 ++++++++++++++
.../UnrealBuildTool/ToolChain/UEToolChain.cs | 15 +
6 files changed, 615 insertions(+), 2 deletions(-)
create mode 100644 Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFile.cs
create mode 100644 Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFileGenerator.cs
diff --git a/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs b/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs
index bbaa8dd2c7cc5..21ee5a01f1bec 100644
--- a/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs
+++ b/Engine/Source/Programs/UnrealBuildTool/Modes/GenerateProjectFilesMode.cs
@@ -36,6 +36,7 @@ class GenerateProjectFilesMode : ToolMode
[CommandLine("-EddieProjectFiles", Value = nameof(ProjectFileFormat.Eddie))]
[CommandLine("-VSCode", Value = nameof(ProjectFileFormat.VisualStudioCode))]
[CommandLine("-VSMac", Value = nameof(ProjectFileFormat.VisualStudioMac))]
+ [CommandLine("-VSWorkspace", Value = nameof(ProjectFileFormat.VisualStudioWorkspace))]
[CommandLine("-CLion", Value = nameof(ProjectFileFormat.CLion))]
[CommandLine("-Rider", Value = nameof(ProjectFileFormat.Rider))]
#if __VPROJECT_AVAILABLE__
@@ -210,6 +211,9 @@ public override int Execute(CommandLineArguments Arguments, ILogger Logger)
case ProjectFileFormat.VisualStudioCode:
Generator = new VSCodeProjectFileGenerator(ProjectFile);
break;
+ case ProjectFileFormat.VisualStudioWorkspace:
+ Generator = new VSWorkspaceProjectFileGenerator(ProjectFile, Arguments);
+ break;
case ProjectFileFormat.CLion:
Generator = new CLionGenerator(ProjectFile);
break;
diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/Windows/VCToolChain.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/Windows/VCToolChain.cs
index a3dbb7ff8016d..4327222e6375d 100644
--- a/Engine/Source/Programs/UnrealBuildTool/Platform/Windows/VCToolChain.cs
+++ b/Engine/Source/Programs/UnrealBuildTool/Platform/Windows/VCToolChain.cs
@@ -268,6 +268,27 @@ public static void AddSourceDependsFile(List<string> Arguments, FileItem SourceD
Arguments.Add($"/clang:-MD /clang:-MF\"{SourceDependsFileString}\"");
}
+ public override IEnumerable<string> GetGlobalCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ List<string> Arguments = new();
+ AppendCLArguments_Global(new(CompileEnvironment), Arguments);
+ return Arguments;
+ }
+
+ public override IEnumerable<string> GetCPPCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ List<string> Arguments = new();
+ AppendCLArguments_CPP(CompileEnvironment, Arguments);
+ return Arguments;
+ }
+
+ public override IEnumerable<string> GetCCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ List<string> Arguments = new();
+ AppendCLArguments_C(CompileEnvironment, Arguments);
+ return Arguments;
+ }
+
protected virtual void AppendCLArguments_Global(CppCompileEnvironment CompileEnvironment, List<string> Arguments)
{
// Suppress generation of object code for unreferenced inline functions. Enabling this option is more standards compliant, and causes a big reduction
diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs
index 5e2b1879b8275..0909a2276b066 100644
--- a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs
+++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/ProjectFileGenerator.cs
@@ -133,6 +133,7 @@ enum ProjectFileFormat
VisualStudio,
VisualStudio2019,
VisualStudio2022,
+ VisualStudioWorkspace,
XCode,
Eddie,
VisualStudioCode,
@@ -2368,7 +2369,7 @@ private ProjectFile FindProjectForModule(FileReference CurModuleFile, List<FileR
/// <param name="ProgramProjects">Map of program names to all of the program projects we created</param>
/// <param name="RulesAssemblies">Map of RuleAssemblies to their base folders</param>
/// <param name="Logger">Logger for output</param>
- private void AddProjectsForAllTargets(
+ protected void AddProjectsForAllTargets(
PlatformProjectGeneratorCollection PlatformProjectGenerators,
List<FileReference> AllGames,
List<FileReference> AllTargetFiles,
@@ -2665,7 +2666,7 @@ protected void AddProjectsForMods(List<ProjectFile> GameProjects, List<ProjectFi
/// </summary>
/// <param name="BaseName">The base name for the project file</param>
/// <returns>Full path to the project file</returns>
- protected FileReference GetProjectLocation(string BaseName)
+ protected virtual FileReference GetProjectLocation(string BaseName)
{
return FileReference.Combine(IntermediateProjectFilesPath, BaseName + ProjectFileExtension);
}
diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFile.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFile.cs
new file mode 100644
index 0000000000000..5e30b56949ab6
--- /dev/null
+++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFile.cs
@@ -0,0 +1,314 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using EpicGames.Core;
+using Microsoft.Extensions.Logging;
+using UnrealBuildBase;
+
+namespace UnrealBuildTool
+{
+ internal class VSWorkspaceProjectFile : ProjectFile
+ {
+ private readonly DirectoryReference RootPath;
+ private readonly HashSet<TargetType> TargetTypes;
+ private readonly CommandLineArguments Arguments;
+
+ /// <summary>
+ /// Collection of output files for this project
+ /// </summary>
+ public List<ExportedTargetInfo> ExportedTargetProjects { get; set; } = new();
+
+ public VSWorkspaceProjectFile(FileReference InProjectFilePath, DirectoryReference BaseDir,
+ DirectoryReference RootPath, HashSet<TargetType> TargetTypes, CommandLineArguments Arguments)
+ : base(InProjectFilePath, BaseDir)
+ {
+ this.RootPath = RootPath;
+ this.TargetTypes = TargetTypes;
+ this.Arguments = Arguments;
+ }
+
+ /// <summary>
+ /// Write project file info in JSON file.
+ /// For every combination of <c>UnrealTargetPlatform</c>, <c>UnrealTargetConfiguration</c> and <c>TargetType</c>
+ /// will be generated separate JSON file.
+ /// </summary>
+ public bool WriteProjectFile(List<UnrealTargetPlatform> InPlatforms,
+ List<UnrealTargetConfiguration> InConfigurations,
+ PlatformProjectGeneratorCollection PlatformProjectGenerators, JsonWriterStyle Minimize, ILogger Logger)
+ {
+ DirectoryReference ProjectRootFolder = RootPath;
+
+ Dictionary<FileReference, (UEBuildTarget BuildTarget, bool bBuildByDefault)> FileToTarget = new();
+
+ foreach (UnrealTargetPlatform Platform in InPlatforms)
+ {
+ foreach (UnrealTargetConfiguration Configuration in InConfigurations)
+ {
+ foreach (ProjectTarget ProjectTarget in ProjectTargets.OfType<ProjectTarget>())
+ {
+ if (TargetTypes.Any() && !TargetTypes.Contains(ProjectTarget.TargetRules!.Type))
+ {
+ continue;
+ }
+
+ // Skip Programs for all configs except for current platform + Development & Debug configurations
+ if (ProjectTarget.TargetRules!.Type == TargetType.Program &&
+ (BuildHostPlatform.Current.Platform != Platform ||
+ !(Configuration == UnrealTargetConfiguration.Development || Configuration == UnrealTargetConfiguration.Debug)))
+ {
+ continue;
+ }
+
+ // Skip Editor for all platforms except for current platform
+ if (ProjectTarget.TargetRules.Type == TargetType.Editor &&
+ (BuildHostPlatform.Current.Platform != Platform ||
+ (Configuration == UnrealTargetConfiguration.Test || Configuration == UnrealTargetConfiguration.Shipping)))
+ {
+ continue;
+ }
+
+ bool bBuildByDefault = ShouldBuildByDefaultForSolutionTargets && ProjectTarget.SupportedPlatforms.Contains(Platform);
+
+ UnrealArchitectures ProjectArchitectures = UEBuildPlatform
+ .GetBuildPlatform(Platform)
+ .ArchitectureConfig.ActiveArchitectures(ProjectTarget.UnrealProjectFilePath, ProjectTarget.Name);
+
+ TargetDescriptor TargetDesc = new(ProjectTarget.UnrealProjectFilePath, ProjectTarget.Name,
+ Platform, Configuration, ProjectArchitectures, Arguments);
+
+ try
+ {
+ FileReference OutputFile = FileReference.Combine(ProjectRootFolder,
+ $"{ProjectTarget.TargetFilePath.GetFileNameWithoutAnyExtensions()}_{Configuration}_{Platform}.json");
+
+ UEBuildTarget BuildTarget = UEBuildTarget.Create(TargetDesc, false, false, false, Logger);
+ FileToTarget.Add(OutputFile, (BuildTarget, bBuildByDefault));
+ }
+ catch (Exception Ex)
+ {
+ Logger.LogWarning("Exception while generating include data for Target:{Target}, Platform: {Platform}, Configuration: {Configuration}", TargetDesc.Name, Platform.ToString(), Configuration.ToString());
+ Logger.LogWarning("{Ex}", Ex.ToString());
+ }
+ }
+ }
+ }
+
+ foreach (var Entry in FileToTarget)
+ {
+ var OutputFile = Entry.Key;
+ var BuildTarget = Entry.Value.BuildTarget;
+ var bBuildByDefault = Entry.Value.bBuildByDefault;
+
+ try
+ {
+ BuildTarget.PreBuildSetup(Logger);
+
+ ExportedTargetInfo TargetInfo = ExportTarget(BuildTarget, bBuildByDefault, PlatformProjectGenerators, Logger);
+
+ DirectoryReference.CreateDirectory(OutputFile.Directory);
+ using FileStream Stream = new(OutputFile.FullName, FileMode.Create, FileAccess.Write);
+ JsonSerializer.Serialize(Stream, TargetInfo, options: new JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = Minimize == JsonWriterStyle.Readable,
+ });
+
+ ExportedTargetProjects.Add(TargetInfo);
+ }
+ catch (Exception Ex)
+ {
+ Logger.LogWarning("Exception while generating include data for Target:{Target}, Platform: {Platform}, Configuration: {Configuration}",
+ BuildTarget.AppName, BuildTarget.Platform.ToString(), BuildTarget.Configuration.ToString());
+ Logger.LogWarning("{Ex}", Ex.ToString());
+ }
+ }
+
+ return true;
+ }
+
+ private ExportedTargetInfo ExportTarget(
+ UEBuildTarget Target, bool bBuildByDefault, PlatformProjectGeneratorCollection PlatformProjectGenerators, ILogger Logger)
+ {
+ ExportedTargetInfo TargetInfo = new()
+ {
+ TargetName = Target.TargetName,
+ TargetPath = Target.TargetRulesFile.FullName,
+ ProjectPath = Target.ProjectFile?.FullName ?? String.Empty,
+ TargetType = Target.TargetType.ToString(),
+ Platform = Target.Platform.ToString(),
+ Configuration = Target.Configuration.ToString(),
+ BuildInfo = ExportBuildInfo(Target, PlatformProjectGenerators, bBuildByDefault, Logger)
+ };
+
+ UEToolChain TargetToolChain = Target.CreateToolchain(Target.Platform);
+ CppCompileEnvironment GlobalCompileEnvironment = Target.CreateCompileEnvironmentForProjectFiles(Logger);
+
+ HashSet<string> ModuleNames = new();
+ foreach (UEBuildBinary Binary in Target.Binaries)
+ {
+ CppCompileEnvironment BinaryCompileEnvironment = Binary.CreateBinaryCompileEnvironment(GlobalCompileEnvironment);
+ IEnumerable<UEBuildModuleCPP> CandidateModules = Binary.Modules.Where(x => x is UEBuildModuleCPP).Cast<UEBuildModuleCPP>();
+
+ foreach (var ModuleCpp in CandidateModules)
+ {
+ if (!ModuleNames.Add(ModuleCpp.Name))
+ {
+ continue;
+ }
+
+ CppCompileEnvironment ModuleCompileEnvironment = ModuleCpp.CreateCompileEnvironmentForIntellisense(Target.Rules, BinaryCompileEnvironment, Logger);
+ TargetInfo.ModuleToCompileSettings.Add(ModuleCpp.Name, ExportModule(ModuleCpp, TargetToolChain, ModuleCompileEnvironment, Logger));
+
+ foreach (DirectoryReference ModuleDirectory in ModuleCpp.ModuleDirectories)
+ {
+ TargetInfo.DirToModule.TryAdd(ModuleDirectory.FullName, ModuleCpp.Name);
+ }
+
+ if (ModuleCpp.GeneratedCodeDirectory != null)
+ {
+ TargetInfo.DirToModule.TryAdd(ModuleCpp.GeneratedCodeDirectory.FullName, ModuleCpp.Name);
+ }
+ }
+ }
+
+ return TargetInfo;
+ }
+
+ private static ExportedModuleInfo ExportModule(UEBuildModuleCPP Module, UEToolChain TargetToolChain, CppCompileEnvironment ModuleCompileEnvironment, ILogger Logger)
+ {
+ ExportedModuleInfo Result = new()
+ {
+ Name = Module.Name,
+ Directory = Module.ModuleDirectory.FullName,
+ Rules = Module.RulesFile.FullName,
+ GeneratedCodeDirectory = Module.GeneratedCodeDirectory != null ? Module.GeneratedCodeDirectory.FullName : String.Empty,
+ Standard = ModuleCompileEnvironment.CppStandard.ToString(),
+ };
+
+ Result.IncludePaths.AddRange(Module.PublicIncludePaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.PublicSystemIncludePaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.InternalIncludePaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.LegacyPublicIncludePaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.LegacyParentIncludePaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.PrivateIncludePaths.Select(x => x.FullName));
+
+ Result.IncludePaths.AddRange(ModuleCompileEnvironment.UserIncludePaths.Select(x => x.FullName));
+
+ Result.IncludePaths.AddRange(Module.PublicSystemLibraryPaths.Select(x => x.FullName));
+ Result.IncludePaths.AddRange(Module.PublicSystemLibraries.Concat(Module.PublicLibraries.Select(x => x.FullName)));
+
+ if (TargetToolChain is VCToolChain TargetVCToolChain
+ && OperatingSystem.IsWindows())
+ {
+ string VCIncludePaths = VCToolChain.GetVCIncludePaths(ModuleCompileEnvironment.Platform, WindowsCompiler.VisualStudio2022, null, Logger);
+ Result.IncludePaths.AddRange(VCIncludePaths.Split(";"));
+ }
+
+ Result.Defines.AddRange(Module.PublicDefinitions);
+ Result.Defines.AddRange(Module.Rules.PrivateDefinitions);
+ Result.Defines.AddRange(Module.Rules.bTreatAsEngineModule ? Array.Empty<string>() : Module.Rules.Target.ProjectDefinitions);
+ Result.Defines.AddRange(Module.GetEmptyApiMacros());
+
+ var ForcedIncludes = ModuleCompileEnvironment.ForceIncludeFiles.ToList();
+ if (ModuleCompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
+ {
+ FileItem IncludeHeader = FileItem.GetItemByFileReference(ModuleCompileEnvironment.PrecompiledHeaderIncludeFilename!);
+ ForcedIncludes.Insert(0, IncludeHeader);
+ }
+
+ Result.ForcedIncludes.AddRange(ForcedIncludes.Select(x => x.FullName));
+
+ Result.CompilerPath = TargetToolChain.GetCppCompilerPath()?.ToString();
+ Result.CompilerArgs.AddRange(TargetToolChain.GetGlobalCommandLineArgs(ModuleCompileEnvironment));
+ Result.CompilerAdditionalArgs.Add("c", TargetToolChain.GetCCommandLineArgs(ModuleCompileEnvironment).ToList());
+ Result.CompilerAdditionalArgs.Add("cpp", TargetToolChain.GetCPPCommandLineArgs(ModuleCompileEnvironment).ToList());
+
+ return Result;
+ }
+
+ private TargetBuildInfo? ExportBuildInfo(UEBuildTarget Target, PlatformProjectGeneratorCollection PlatformProjectGenerators, bool bBuildByDefault, ILogger Logger)
+ {
+ if (!BuildHostPlatform.Current.Platform.IsInGroup(UnrealPlatformGroup.Windows))
+ {
+ Logger.LogWarning("Unsupported platform for Build Information: {Platform}", BuildHostPlatform.Current.Platform.ToString());
+ return null;
+ }
+
+ if (IsStubProject)
+ {
+ return null;
+ }
+
+ ProjectTarget ProjectTarget = ProjectTargets.OfType<ProjectTarget>().Single(It => Target.TargetRulesFile == It.TargetFilePath);
+ UnrealTargetPlatform Platform = Target.Platform;
+ UnrealTargetConfiguration Configuration = Target.Configuration;
+
+ string UProjectPath = "";
+ if (IsForeignProject)
+ {
+ UProjectPath = String.Format("\"{0}\"", ProjectTarget.UnrealProjectFilePath!.FullName);
+ }
+
+ PlatformProjectGenerator? ProjGenerator = PlatformProjectGenerators.GetPlatformProjectGenerator(Platform, true);
+ VCProjectFile.BuildCommandBuilder BuildCommandBuilder = new(Configuration, Platform, ProjectTarget, UProjectPath)
+ {
+ ProjectGenerator = ProjGenerator,
+ bIsForeignProject = IsForeignProject
+ };
+
+ string BuildArguments = BuildCommandBuilder.GetBuildArguments();
+
+ return new TargetBuildInfo()
+ {
+ BuildCmd = $"{BuildCommandBuilder.BuildScript.FullName} {BuildArguments}",
+ RebuildCmd = $"{BuildCommandBuilder.RebuildScript.FullName} {BuildArguments}",
+ CleanCmd = $"{BuildCommandBuilder.CleanScript.FullName} {BuildArguments}",
+ PrimaryOutput = Target.Binaries[0].OutputFilePath.FullName,
+ BuildByDefault = bBuildByDefault,
+ };
+ }
+
+ internal class ExportedTargetInfo
+ {
+ public string TargetName { get; set; } = String.Empty;
+ public string TargetPath { get; set; } = String.Empty;
+ public string ProjectPath { get; set; } = String.Empty;
+ public string TargetType { get; set; } = String.Empty;
+ public string Platform { get; set; } = String.Empty;
+ public string Configuration { get; set; } = String.Empty;
+ public TargetBuildInfo? BuildInfo { get; set; }
+ public Dictionary<string, ExportedModuleInfo> ModuleToCompileSettings { get; set; } = new();
+ public Dictionary<string, string> DirToModule { get; set; } = new();
+ }
+
+ internal class ExportedModuleInfo
+ {
+ public string Name { get; set; } = String.Empty;
+ public string Directory { get; set; } = String.Empty;
+ public string Rules { get; set; } = String.Empty;
+ public string GeneratedCodeDirectory { get; set; } = String.Empty;
+ public List<string> IncludePaths { get; set; } = new();
+ public List<string> Defines { get; set; } = new();
+ public string? Standard { get; set; }
+ public List<string> ForcedIncludes { get; set; } = new();
+ public string? CompilerPath { get; set; }
+ public List<string> CompilerArgs { get; set; } = new();
+ public Dictionary<string, List<string>> CompilerAdditionalArgs { get; set; } = new();
+ public string? WindowsSdkVersion { get; set; }
+ }
+
+ internal class TargetBuildInfo
+ {
+ public string BuildCmd { get; set; } = String.Empty;
+ public string RebuildCmd { get; set; } = String.Empty;
+ public string CleanCmd { get; set; } = String.Empty;
+ public string PrimaryOutput { get; set; } = String.Empty;
+ public bool BuildByDefault { get; internal set; }
+ }
+ }
+}
diff --git a/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFileGenerator.cs b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFileGenerator.cs
new file mode 100644
index 0000000000000..25093e8a4b852
--- /dev/null
+++ b/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/VisualStudioWorkspace/VSWorkspaceProjectFileGenerator.cs
@@ -0,0 +1,258 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using EpicGames.Core;
+using Microsoft.Extensions.Logging;
+using UnrealBuildBase;
+
+namespace UnrealBuildTool
+{
+ class VSWorkspaceProjectFileGenerator : ProjectFileGenerator
+ {
+ public override string ProjectFileExtension => ".json";
+
+ // These properties are used by Visual Studio to determine where to read the project files.
+ // So they must remain constant.
+ private const string VSUnrealWorkspaceFileName = ".vs-unreal-workspace";
+ private const string ProjectFilesFolder = "VisualStudio";
+
+ private readonly CommandLineArguments Arguments;
+
+ /// <summary>
+ /// List of deprecated platforms.
+ /// Don't generate project model for these platforms unless they are specified in "Platforms" console arguments.
+ /// </summary>
+ /// <returns></returns>
+ private readonly HashSet<UnrealTargetPlatform> DeprecatedPlatforms = new();
+
+ /// <summary>
+ /// Platforms to generate project files for
+ /// </summary>
+ [CommandLine("-Platforms=", ListSeparator = '+')]
+ HashSet<UnrealTargetPlatform> Platforms = new();
+
+ /// <summary>
+ /// Target types to generate project files for
+ /// </summary>
+ [CommandLine("-TargetTypes=", ListSeparator = '+')]
+ HashSet<TargetType> TargetTypes = new();
+
+ /// <summary>
+ /// Target configurations to generate project files for
+ /// </summary>
+ [CommandLine("-TargetConfigurations=", ListSeparator = '+')]
+ HashSet<UnrealTargetConfiguration> TargetConfigurations = new();
+
+ /// <summary>
+ /// Projects to generate project files for
+ /// </summary>
+ [CommandLine("-ProjectNames=", ListSeparator = '+')]
+ HashSet<string> ProjectNames = new();
+
+ /// <summary>
+ /// Should format JSON files in human readable form, or use packed one without indents
+ /// </summary>
+ [CommandLine("-Minimize", Value = "Compact")]
+ private JsonWriterStyle Minimize = JsonWriterStyle.Readable;
+
+ public VSWorkspaceProjectFileGenerator(FileReference? InOnlyGameProject,
+ CommandLineArguments InArguments)
+ : base(InOnlyGameProject)
+ {
+ Arguments = InArguments;
+ Arguments.ApplyTo(this);
+ }
+
+ public override bool ShouldGenerateIntelliSenseData() => true;
+
+ public override void CleanProjectFiles(DirectoryReference InPrimaryProjectDirectory, string InPrimaryProjectName,
+ DirectoryReference InIntermediateProjectFilesDirectory, ILogger Logger)
+ {
+ DirectoryReference.Delete(InPrimaryProjectDirectory);
+ }
+
+ protected override void ConfigureProjectFileGeneration(string[] Arguments, ref bool IncludeAllPlatforms, ILogger Logger)
+ {
+ base.ConfigureProjectFileGeneration(Arguments, ref IncludeAllPlatforms, Logger);
+ }
+
+ protected override ProjectFile AllocateProjectFile(FileReference InitFilePath, DirectoryReference BaseDir)
+ {
+ VSWorkspaceProjectFile projectFile = new(InitFilePath, BaseDir, RootPath: InitFilePath.Directory,
+ Arguments: Arguments, TargetTypes: TargetTypes);
+ return projectFile;
+ }
+
+ protected override bool WriteProjectFiles(PlatformProjectGeneratorCollection PlatformProjectGenerators, ILogger Logger)
+ {
+ using ProgressWriter Progress = new("Writing project files...", true, Logger);
+ List<ProjectFile> ProjectsToGenerate = new(GeneratedProjectFiles);
+ if (ProjectNames.Any())
+ {
+ ProjectsToGenerate = ProjectsToGenerate.Where(Project =>
+ ProjectNames.Contains(Project.ProjectFilePath.GetFileNameWithoutAnyExtensions())).ToList();
+ }
+
+ int TotalProjectFileCount = ProjectsToGenerate.Count;
+
+ HashSet<UnrealTargetPlatform> PlatformsToGenerate = new(SupportedPlatforms);
+ if (Platforms.Any())
+ {
+ PlatformsToGenerate.IntersectWith(Platforms);
+ }
+
+ List<UnrealTargetPlatform> FilteredPlatforms = PlatformsToGenerate.Where(Platform =>
+ {
+ // Skip deprecated unless explicitly specified in the command line.
+ return (!DeprecatedPlatforms.Contains(Platform) || Platforms.Contains(Platform))
+ && UEBuildPlatform.IsPlatformAvailable(Platform);
+ }).ToList();
+
+ HashSet<UnrealTargetConfiguration> ConfigurationsToGenerate = new(SupportedConfigurations);
+ if (TargetConfigurations.Any())
+ {
+ ConfigurationsToGenerate.IntersectWith(TargetConfigurations);
+ }
+
+ List<UnrealTargetConfiguration> Configurations = ConfigurationsToGenerate.ToList();
+
+ for (int ProjectFileIndex = 0; ProjectFileIndex < ProjectsToGenerate.Count; ++ProjectFileIndex)
+ {
+ if (ProjectsToGenerate[ProjectFileIndex] is not VSWorkspaceProjectFile CurrentProject)
+ {
+ return false;
+ }
+
+ if (!CurrentProject.WriteProjectFile(FilteredPlatforms, Configurations, PlatformProjectGenerators, Minimize, Logger))
+ {
+ return false;
+ }
+
+ Progress.Write(ProjectFileIndex + 1, TotalProjectFileCount);
+ }
+
+ Progress.Write(TotalProjectFileCount, TotalProjectFileCount);
+
+ return true;
+ }
+
+ public override bool GenerateProjectFiles(PlatformProjectGeneratorCollection PlatformProjectGenerators,
+ String[] Arguments, ILogger Logger)
+ {
+ bool IncludeAllPlatforms = true;
+ ConfigureProjectFileGeneration(Arguments, ref IncludeAllPlatforms, Logger);
+
+ if (bGeneratingGameProjectFiles || Unreal.IsEngineInstalled())
+ {
+ PrimaryProjectPath = OnlyGameProject!.Directory;
+ PrimaryProjectName = OnlyGameProject.GetFileNameWithoutExtension();
+
+ IntermediateProjectFilesPath =
+ DirectoryReference.Combine(PrimaryProjectPath, "Intermediate", "ProjectFiles");
+ }
+
+ SetupSupportedPlatformsAndConfigurations(IncludeAllPlatforms: true, Logger, out string SupportedPlatformNames);
+ Logger.LogDebug("Supported platforms: {Platforms}", SupportedPlatformNames);
+
+ List<FileReference> AllGames = FindGameProjects(Logger);
+
+ {
+ // Find all of the target files.
+ List<FileReference> AllTargetFiles = DiscoverTargets(
+ AllGames,
+ Logger,
+ OnlyGameProject,
+ SupportedPlatforms,
+ bIncludeEngineSource: bIncludeEngineSource,
+ bIncludeTempTargets: bIncludeTempTargets);
+
+ // If there are multiple targets of a given type for a project, use the order to determine which one gets a suffix.
+ AllTargetFiles = AllTargetFiles.OrderBy(x => x.FullName, StringComparer.OrdinalIgnoreCase).ToList();
+
+ ProjectFile? EngineProject = null;
+ List<ProjectFile> GameProjects = new();
+ List<ProjectFile> ModProjects = new();
+ Dictionary<FileReference, ProjectFile> ProgramProjects = new();
+ Dictionary<RulesAssembly, DirectoryReference> RulesAssemblies = new();
+ Dictionary<ProjectFile, FileReference> ProjectFileToUProjectFile = new();
+
+ AddProjectsForAllTargets(
+ PlatformProjectGenerators,
+ AllGames,
+ AllTargetFiles,
+ Arguments,
+ ref EngineProject,
+ GameProjects,
+ ProjectFileToUProjectFile,
+ ProgramProjects,
+ RulesAssemblies,
+ Logger);
+
+ AddAllGameProjects(GameProjects);
+ }
+
+ WriteProjectFiles(PlatformProjectGenerators, Logger);
+ WritePrimaryProjectFile(UBTProject, PlatformProjectGenerators, Logger);
+
+ return true;
+ }
+
+ protected override bool WritePrimaryProjectFile(ProjectFile? UBTProject,
+ PlatformProjectGeneratorCollection PlatformProjectGenerators,
+ ILogger Logger)
+ {
+ try
+ {
+ FileReference PrimaryProjectFile = FileReference.Combine(
+ IntermediateProjectFilesPath, ProjectFilesFolder, VSUnrealWorkspaceFileName);
+
+ DirectoryReference.CreateDirectory(PrimaryProjectFile.Directory);
+
+ // Collect all the resulting project files and aggregate the target-level data
+ var AggregatedProjectInfo = GeneratedProjectFiles
+ .Where(Project => Project is VSWorkspaceProjectFile)
+ .OfType<VSWorkspaceProjectFile>()
+ .SelectMany(Project => Project.ExportedTargetProjects)
+ .GroupBy(TargetProject => TargetProject.TargetName)
+ .Select(g => (g.Key, Target: new
+ {
+ TargetType = g.Select(i => i.TargetType).Distinct().Single(),
+ TargetPath = g.Select(i => i.TargetPath).Distinct().Single(),
+ ProjectPath = g.Select(i => i.ProjectPath).Distinct().Single(),
+ Configurations = g.Select(i => i.Configuration).Distinct().ToList(),
+ Platforms = g.Select(i => i.Platform).Distinct().ToList(),
+ }));
+
+ // The inner Targets object is needed for schema compatibility with the Query Mode API.
+ var Result = new
+ {
+ Targets = AggregatedProjectInfo.ToDictionary(item => item.Key, item => item.Target)
+ };
+
+ using FileStream Stream = new(PrimaryProjectFile.FullName, FileMode.Create, FileAccess.Write);
+ JsonSerializer.Serialize(Stream, Result, new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ WriteIndented = Minimize == JsonWriterStyle.Readable,
+ });
+ }
+ catch (Exception Ex)
+ {
+ Logger.LogWarning("Exception while writing root project file: {Ex}", Ex.ToString());
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <inheritdoc />
+ protected override FileReference GetProjectLocation(string BaseName)
+ {
+ return FileReference.Combine(IntermediateProjectFilesPath, ProjectFilesFolder, BaseName + ProjectFileExtension);
+ }
+ }
+}
diff --git a/Engine/Source/Programs/UnrealBuildTool/ToolChain/UEToolChain.cs b/Engine/Source/Programs/UnrealBuildTool/ToolChain/UEToolChain.cs
index c9a3d197d078a..05eb8ce606b03 100644
--- a/Engine/Source/Programs/UnrealBuildTool/ToolChain/UEToolChain.cs
+++ b/Engine/Source/Programs/UnrealBuildTool/ToolChain/UEToolChain.cs
@@ -149,6 +149,21 @@ public virtual FileItem[] LinkAllFiles(LinkEnvironment LinkEnvironment, bool bBu
return Result.ToArray();
}
+ public virtual IEnumerable<string> GetGlobalCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ return Array.Empty<string>();
+ }
+
+ public virtual IEnumerable<string> GetCPPCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ return Array.Empty<string>();
+ }
+
+ public virtual IEnumerable<string> GetCCommandLineArgs(CppCompileEnvironment CompileEnvironment)
+ {
+ return Array.Empty<string>();
+ }
+
/// <summary>
/// Get the name of the response file for the current linker environment and output file
/// </summary>