481 lines
17 KiB
C#
481 lines
17 KiB
C#
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using System.Threading.Tasks;
|
|
using EpicGames.Core;
|
|
using Microsoft.Extensions.Logging;
|
|
using OpenTracing.Util;
|
|
using UnrealBuildBase;
|
|
|
|
namespace UnrealBuildTool
|
|
{
|
|
// TODO: Interface for commands/results?
|
|
enum CommandType
|
|
{
|
|
Quit,
|
|
DiscoverTargets,
|
|
SetTarget,
|
|
QueryFile,
|
|
GetBrowseConfiguration,
|
|
GetCompileSettings,
|
|
}
|
|
|
|
interface ICommand
|
|
{
|
|
public CommandType Type { get; }
|
|
public long Sequence { get; set; }
|
|
}
|
|
interface ICommandResponse
|
|
{
|
|
public CommandType Type { get; }
|
|
public long Sequence { get; set; }
|
|
}
|
|
|
|
struct Command : ICommand
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type { get; set; }
|
|
public long Sequence { get; set; }
|
|
};
|
|
|
|
struct SetTargetCommand : ICommand
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type { get; set; }
|
|
public long Sequence { get; set; }
|
|
public string Target { get; set; }
|
|
public string Platform { get; set; }
|
|
public string Configuration { get; set; }
|
|
}
|
|
|
|
struct QueryFileCommand : ICommand
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type { get; set; }
|
|
public long Sequence { get; set; }
|
|
public string AbsolutePath { get; set; }
|
|
}
|
|
|
|
struct GetBrowseConfigurationCommand : ICommand
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type { get; set; }
|
|
public long Sequence { get; set; }
|
|
public string? AbsolutePath { get; set; }
|
|
}
|
|
|
|
struct GetCompileSettingsCommand : ICommand
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type { get; set; }
|
|
public long Sequence { get; set; }
|
|
public List<string> AbsolutePaths { get; set; }
|
|
}
|
|
|
|
struct DiscoverTargetsResponse : ICommandResponse
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type => CommandType.DiscoverTargets;
|
|
public long Sequence { get; set; }
|
|
public List<string> Configurations { get; set; }
|
|
public List<string> Platforms { get; set; }
|
|
public List<string> Targets { get; set; }
|
|
|
|
public string DefaultConfiguration { get; set; }
|
|
public string DefaultPlatform { get; set; }
|
|
public string DefaultTarget { get; set; }
|
|
}
|
|
|
|
struct SetTargetResponse : ICommandResponse
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type => CommandType.SetTarget;
|
|
public long Sequence { get; set; }
|
|
}
|
|
struct QueryFileResponse : ICommandResponse
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type => CommandType.QueryFile;
|
|
public long Sequence { get; set; }
|
|
public bool Found { get; set; }
|
|
}
|
|
|
|
struct GetBrowseConfigurationResponse : ICommandResponse
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type => CommandType.GetBrowseConfiguration;
|
|
public long Sequence { get; set; }
|
|
public bool Success { get; set; }
|
|
|
|
public List<string> Paths { get; set; }
|
|
public string? Standard { get; set; }
|
|
public string? WindowsSdkVersion { get; set; }
|
|
}
|
|
|
|
class GetCompileSettingsResponse : ICommandResponse
|
|
{
|
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
public CommandType Type => CommandType.GetCompileSettings;
|
|
public long Sequence { get; set; }
|
|
public List<TargetIntellisenseInfo.CompileSettings?> Settings { get; set; } = new();
|
|
}
|
|
|
|
// TODO: Deprecate
|
|
[Obsolete]
|
|
[ToolMode("Server", ToolModeOptions.BuildPlatforms | ToolModeOptions.XmlConfig | ToolModeOptions.UseStartupTraceListener)]
|
|
class ServerMode : ToolMode
|
|
{
|
|
[CommandLine("-LogDirectory=")]
|
|
public DirectoryReference? LogDirectory = null;
|
|
|
|
private bool KeepRunning { get; set; } = true;
|
|
|
|
private BuildConfiguration BuildConfiguration = new();
|
|
private UEBuildTarget? CurrentTarget = null;
|
|
private TargetIntellisenseInfo? CurrentTargetIntellisenseInfo = null;
|
|
private GetBrowseConfigurationResponse CurrentBrowseConfiguration;
|
|
|
|
public override Task<int> ExecuteAsync(CommandLineArguments Arguments, ILogger Logger)
|
|
{
|
|
Arguments.ApplyTo(this);
|
|
|
|
if (LogDirectory == null)
|
|
{
|
|
LogDirectory = DirectoryReference.Combine(Unreal.EngineProgramSavedDirectory, "UnrealBuildTool");
|
|
}
|
|
|
|
DirectoryReference.CreateDirectory(LogDirectory);
|
|
|
|
FileReference LogFile = FileReference.Combine(LogDirectory, "Log_Server.txt");
|
|
Log.AddFileWriter("DefaultLogTraceListener", LogFile);
|
|
Logger.LogInformation("Server mode started");
|
|
|
|
XmlConfig.ApplyTo(BuildConfiguration);
|
|
Arguments.ApplyTo(BuildConfiguration);
|
|
|
|
ProjectFileGenerator.bGenerateProjectFiles = true;
|
|
|
|
while (Arguments.TryGetPositionalArgument(out string? PositionalArg))
|
|
{
|
|
HandleCommand(PositionalArg!, Logger);
|
|
}
|
|
|
|
while (KeepRunning)
|
|
{
|
|
string? Line = Console.ReadLine();
|
|
if (Line == null)
|
|
{
|
|
continue;
|
|
}
|
|
HandleCommand(Line, Logger);
|
|
}
|
|
return Task.FromResult(0);
|
|
}
|
|
|
|
private void HandleCommand(string CommandString, ILogger Logger)
|
|
{
|
|
JsonSerializerOptions jsonOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
};
|
|
JsonSerializerOptions responseOptions = new JsonSerializerOptions
|
|
{
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
};
|
|
try
|
|
{
|
|
Command? MaybeCommand = JsonSerializer.Deserialize<Command>(CommandString, jsonOptions);
|
|
if (MaybeCommand is null)
|
|
{
|
|
Console.Error.WriteLine($"Failed to parse command {CommandString}");
|
|
return;
|
|
}
|
|
Logger.LogInformation($"Got command {CommandString}");
|
|
Command command = MaybeCommand.Value;
|
|
switch (command.Type)
|
|
{
|
|
case CommandType.Quit:
|
|
KeepRunning = false;
|
|
break;
|
|
case CommandType.DiscoverTargets:
|
|
{
|
|
DiscoverTargetsResponse response = Handle_DiscoverTargets(Logger);
|
|
response.Sequence = command.Sequence;
|
|
Console.WriteLine(JsonSerializer.Serialize(response, responseOptions));
|
|
}
|
|
break;
|
|
case CommandType.SetTarget:
|
|
{
|
|
SetTargetResponse response = Handle_SetTarget(JsonSerializer.Deserialize<SetTargetCommand>(CommandString, jsonOptions), Logger);
|
|
response.Sequence = command.Sequence;
|
|
Console.WriteLine(JsonSerializer.Serialize(response, responseOptions));
|
|
}
|
|
break;
|
|
case CommandType.QueryFile:
|
|
{
|
|
QueryFileResponse response = Handle_QueryFile(JsonSerializer.Deserialize<QueryFileCommand>(CommandString, jsonOptions), Logger);
|
|
response.Sequence = command.Sequence;
|
|
Console.WriteLine(JsonSerializer.Serialize(response, responseOptions));
|
|
}
|
|
break;
|
|
case CommandType.GetBrowseConfiguration:
|
|
{
|
|
GetBrowseConfigurationResponse response = Handle_GetBrowseConfiguration(JsonSerializer.Deserialize<GetBrowseConfigurationCommand>(CommandString, jsonOptions), Logger);
|
|
response.Sequence = command.Sequence;
|
|
Console.WriteLine(JsonSerializer.Serialize(response, responseOptions));
|
|
}
|
|
break;
|
|
case CommandType.GetCompileSettings:
|
|
{
|
|
GetCompileSettingsResponse response = Handle_GetCompileSettings(JsonSerializer.Deserialize<GetCompileSettingsCommand>(CommandString, jsonOptions), Logger);
|
|
response.Sequence = command.Sequence;
|
|
Console.WriteLine(JsonSerializer.Serialize(response, responseOptions));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
Console.Error.WriteLine($"Failed to parse command {CommandString}");
|
|
}
|
|
}
|
|
|
|
// TODO: make async
|
|
private DiscoverTargetsResponse Handle_DiscoverTargets(ILogger Logger)
|
|
{
|
|
List<UnrealTargetPlatform> Platforms = new();
|
|
foreach (UnrealTargetPlatform Platform in UnrealTargetPlatform.GetValidPlatforms())
|
|
{
|
|
// Only include desktop platforms for now
|
|
if (ProjectFileGenerator.IsValidDesktopPlatform(Platform))
|
|
{
|
|
// If there is a build platform present, add it to the SupportedPlatforms list
|
|
UEBuildPlatform? BuildPlatform;
|
|
if (UEBuildPlatform.TryGetBuildPlatform(Platform, out BuildPlatform))
|
|
{
|
|
if (InstalledPlatformInfo.IsValidPlatform(Platform, EProjectType.Code))
|
|
{
|
|
Platforms.Add(Platform);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
List<UnrealTargetConfiguration> AllowedTargetConfigurations = new List<UnrealTargetConfiguration>();
|
|
AllowedTargetConfigurations = Enum.GetValues(typeof(UnrealTargetConfiguration)).Cast<UnrealTargetConfiguration>().ToList();
|
|
|
|
List<string> Configurations = new();
|
|
foreach (UnrealTargetConfiguration CurConfiguration in AllowedTargetConfigurations)
|
|
{
|
|
if (CurConfiguration != UnrealTargetConfiguration.Unknown)
|
|
{
|
|
if (InstalledPlatformInfo.IsValidConfiguration(CurConfiguration, EProjectType.Code))
|
|
{
|
|
Configurations.Add(CurConfiguration.ToString());
|
|
}
|
|
}
|
|
}
|
|
|
|
List<FileReference> Projects = NativeProjects.EnumerateProjectFiles(Logger).ToList();
|
|
List<FileReference> AllTargetFiles = ProjectFileGenerator.DiscoverTargets(Projects, Logger, null, Platforms, bIncludeEngineSource: true, bIncludeTempTargets: false);
|
|
Projects.RemoveAll(x => !AllTargetFiles.Any(y => y.IsUnderDirectory(x.Directory)));
|
|
|
|
List<string> TargetNames = new();
|
|
foreach (FileReference TargetFilePath in AllTargetFiles)
|
|
{
|
|
string TargetName = TargetFilePath.GetFileNameWithoutAnyExtensions();
|
|
TargetNames.Add(TargetName);
|
|
}
|
|
|
|
// TODO: Handle 0 target names
|
|
|
|
DiscoverTargetsResponse Reply = new DiscoverTargetsResponse
|
|
{
|
|
Configurations = Configurations,
|
|
Platforms = Platforms.Select(x => x.ToString()).ToList(),
|
|
Targets = TargetNames,
|
|
|
|
// TODO: Allow selecting default from project file
|
|
DefaultTarget = TargetNames.FirstOrDefault(x => x == "UnrealEditor", TargetNames[0]),
|
|
DefaultPlatform = Platforms[0].ToString(),
|
|
DefaultConfiguration = UnrealTargetConfiguration.Development.ToString(),
|
|
};
|
|
return Reply;
|
|
}
|
|
|
|
private SetTargetResponse Handle_SetTarget(SetTargetCommand command, ILogger Logger)
|
|
{
|
|
CommandLineArguments Args = new CommandLineArguments(new string[] { command.Target, command.Configuration, command.Platform });
|
|
List<TargetDescriptor> TargetDescriptors = new();
|
|
|
|
TargetDescriptor.ParseSingleCommandLine(Args, false, false, false, TargetDescriptors, Logger);
|
|
if (TargetDescriptors.Count == 0)
|
|
{
|
|
// TOOD: Error
|
|
return new SetTargetResponse { };
|
|
}
|
|
|
|
HashSet<string> BrowseConfigurationFolders = new HashSet<string>();
|
|
try
|
|
{
|
|
using (GlobalTracer.Instance.BuildSpan("UEBuildTarget.Create()").StartActive())
|
|
{
|
|
bool bUsePrecompiled = false;
|
|
CurrentTarget = UEBuildTarget.Create(TargetDescriptors[0], false, false, bUsePrecompiled, Logger);
|
|
}
|
|
|
|
CurrentTargetIntellisenseInfo = new TargetIntellisenseInfo();
|
|
CurrentBrowseConfiguration = new GetBrowseConfigurationResponse { Success = true };
|
|
|
|
// Partially duplicated from UEBuildTarget.Build because we just want to get C++ compile actions without running UHT
|
|
// or generating link actions / full dependency graph
|
|
CppConfiguration CppConfiguration = UEBuildTarget.GetCppConfiguration(CurrentTarget.Configuration);
|
|
SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(null, Logger);
|
|
CppCompileEnvironment GlobalCompileEnvironment = new CppCompileEnvironment(CurrentTarget.Platform, CppConfiguration, CurrentTarget.Architectures, MetadataCache);
|
|
LinkEnvironment GlobalLinkEnvironment = new LinkEnvironment(GlobalCompileEnvironment.Platform, GlobalCompileEnvironment.Configuration, GlobalCompileEnvironment.Architectures);
|
|
|
|
UEToolChain TargetToolChain = CurrentTarget.CreateToolchain(CurrentTarget.Platform, Logger);
|
|
TargetToolChain.SetEnvironmentVariables();
|
|
CurrentTarget.SetupGlobalEnvironment(TargetToolChain, GlobalCompileEnvironment, GlobalLinkEnvironment);
|
|
|
|
// TODO: For installed builds, filter out all the binaries that aren't in mods
|
|
foreach (UEBuildBinary Binary in CurrentTarget.Binaries)
|
|
{
|
|
HashSet<UEBuildModule> LinkEnvironmentVisitedModules = new HashSet<UEBuildModule>();
|
|
CppCompileEnvironment BinaryCompileEnvironment = Binary.CreateBinaryCompileEnvironment(GlobalCompileEnvironment);
|
|
CurrentBrowseConfiguration.Standard = BinaryCompileEnvironment.CppStandard.ToString();
|
|
CurrentBrowseConfiguration.WindowsSdkVersion = CurrentTarget.Rules.WindowsPlatform.WindowsSdkVersion;
|
|
|
|
foreach (UEBuildModuleCPP Module in Binary.Modules.OfType<UEBuildModuleCPP>())
|
|
{
|
|
if (Module.Binary != null && Module.Binary != Binary)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
CppCompileEnvironment ModuleCompileEnvironment = Module.CreateModuleCompileEnvironment(CurrentTarget.Rules, BinaryCompileEnvironment, Logger);
|
|
foreach (DirectoryReference Dir in Module.ModuleDirectories)
|
|
{
|
|
BrowseConfigurationFolders.Add(Dir.ToString());
|
|
CurrentTargetIntellisenseInfo.DirToModule.TryAdd(Dir, Module);
|
|
}
|
|
if (Module.GeneratedCodeDirectory != null)
|
|
{
|
|
CurrentTargetIntellisenseInfo.DirToModule.TryAdd(Module.GeneratedCodeDirectory, Module);
|
|
}
|
|
|
|
foreach (DirectoryReference Dir in ModuleCompileEnvironment.SystemIncludePaths)
|
|
{
|
|
BrowseConfigurationFolders.Add(Dir.ToString());
|
|
}
|
|
foreach (DirectoryReference Dir in ModuleCompileEnvironment.UserIncludePaths)
|
|
{
|
|
BrowseConfigurationFolders.Add(Dir.ToString());
|
|
}
|
|
|
|
TargetIntellisenseInfo.CompileSettings Settings = new TargetIntellisenseInfo.CompileSettings();
|
|
Settings.IncludePaths.AddRange(ModuleCompileEnvironment.SystemIncludePaths.Select(x => x.ToString()));
|
|
Settings.IncludePaths.AddRange(ModuleCompileEnvironment.UserIncludePaths.Select(x => x.ToString()));
|
|
Settings.Defines = ModuleCompileEnvironment.Definitions;
|
|
Settings.Standard = ModuleCompileEnvironment.CppStandard.ToString();
|
|
Settings.ForcedIncludes = ModuleCompileEnvironment.ForceIncludeFiles.Select(x => x.ToString()).ToList();
|
|
Settings.CompilerPath = TargetToolChain.GetCppCompilerPath()?.ToString();
|
|
Settings.WindowsSdkVersion = CurrentTarget.Rules.WindowsPlatform.WindowsSdkVersion;
|
|
CurrentTargetIntellisenseInfo.ModuleToCompileSettings.TryAdd(Module, Settings);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.LogError("Caught exception setting up target: {0}", e);
|
|
}
|
|
|
|
CurrentBrowseConfiguration.Paths = BrowseConfigurationFolders.ToList();
|
|
|
|
SetTargetResponse Reply = new SetTargetResponse
|
|
{
|
|
};
|
|
return Reply;
|
|
}
|
|
|
|
private QueryFileResponse Handle_QueryFile(QueryFileCommand Command, ILogger Logger)
|
|
{
|
|
QueryFileResponse FailResponse = new QueryFileResponse { Found = false };
|
|
if (CurrentTargetIntellisenseInfo == null)
|
|
{
|
|
return FailResponse;
|
|
}
|
|
try
|
|
{
|
|
string Path = System.IO.Path.GetFullPath(Command.AbsolutePath);
|
|
if (CurrentTargetIntellisenseInfo.FindModuleForFile(new FileReference(Path)) != null)
|
|
{
|
|
return new QueryFileResponse { Found = true };
|
|
}
|
|
return FailResponse;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return FailResponse;
|
|
}
|
|
}
|
|
private GetBrowseConfigurationResponse Handle_GetBrowseConfiguration(GetBrowseConfigurationCommand Command, ILogger Logger)
|
|
{
|
|
GetBrowseConfigurationResponse Response = new GetBrowseConfigurationResponse { Success = false, Paths = new List<string>() };
|
|
if (CurrentTargetIntellisenseInfo == null)
|
|
{
|
|
return Response;
|
|
}
|
|
if (Command.AbsolutePath != null)
|
|
{
|
|
string Path = System.IO.Path.GetFullPath(Command.AbsolutePath);
|
|
UEBuildModule? Module = CurrentTargetIntellisenseInfo.FindModuleForDirectory(new DirectoryReference(Path));
|
|
if (Module != null)
|
|
{
|
|
Response.Success = true;
|
|
Response.Paths.AddRange(Module.ModuleDirectories.Select(x => x.ToString()));
|
|
if (CurrentTargetIntellisenseInfo.ModuleToCompileSettings.TryGetValue(Module, out TargetIntellisenseInfo.CompileSettings? Settings))
|
|
{
|
|
Response.Paths.AddRange(Settings.IncludePaths);
|
|
Response.WindowsSdkVersion = Settings.WindowsSdkVersion;
|
|
Response.Standard = Settings.Standard;
|
|
}
|
|
}
|
|
return Response;
|
|
}
|
|
|
|
Response = CurrentBrowseConfiguration;
|
|
return Response;
|
|
}
|
|
|
|
private GetCompileSettingsResponse Handle_GetCompileSettings(GetCompileSettingsCommand Command, ILogger Logger)
|
|
{
|
|
GetCompileSettingsResponse FailResponse = new GetCompileSettingsResponse { };
|
|
try
|
|
{
|
|
GetCompileSettingsResponse Response = new GetCompileSettingsResponse { };
|
|
foreach (string AbsolutePath in Command.AbsolutePaths)
|
|
{
|
|
TargetIntellisenseInfo.CompileSettings? Settings = null;
|
|
string Path = System.IO.Path.GetFullPath(AbsolutePath); // convert absolute path to dos?
|
|
UEBuildModule? Module = CurrentTargetIntellisenseInfo!.FindModuleForFile(new FileReference(Path));
|
|
if (Module is not null)
|
|
{
|
|
CurrentTargetIntellisenseInfo!.ModuleToCompileSettings.TryGetValue(Module, out Settings);
|
|
}
|
|
Response.Settings.Add(Settings);
|
|
}
|
|
return Response;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return FailResponse;
|
|
}
|
|
}
|
|
}
|
|
}
|