Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Platform/Windows/VCPreludeCache.cs
jonathan adamczewski d58996b916 UnrealBuildTool: move some path constants into BuildUtilities
RootDirectory, EngineDirectory, UnrealBuildToolPath are now found in BuildUtilities' UnrealBuild namesapce.

The way these are computed has changed. Previously, it was assumed that the application is UnrealBuildTool, and paths were constructed relative to that assembly.

Now, the assumption is that the process is located under a "Engine/Build/DotNET" sub-path and paths are constructed relative to that.

#jira none

#ROBOMERGE-SOURCE: CL 16607440 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v828-16531559)

[CL 16607455 by jonathan adamczewski in ue5-release-engine-test branch]
2021-06-09 12:55:13 -04:00

183 lines
6.4 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using EpicGames.Core;
using UnrealBuildBase;
namespace UnrealBuildTool
{
/// <summary>
/// Caches predefined state ("prelude") for compiler versions and options
/// </summary>
static class VCPreludeCache
{
/// <summary>
/// Set of options which can influence the prelude state. Any options matching entries in this set will be used to generate a separate prelude file.
/// </summary>
static HashSet<string> FilterArguments = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"/EH", // Exception handling mode
"/GR", // RTTI settings
"/MD", // Multithreaded DLL runtime library
"/MDd", // Multithreaded debug DLL runtime library
"/MT", // Multithreaded static runtime library
"/MTd", // Multithreaded debug static runtime library
};
/// <summary>
/// Object used for ensuring only one thread can create items in the cache at once
/// </summary>
static object LockObject = new object();
/// <summary>
/// File containing a list of macros to be used to generate a prelude file
/// </summary>
static readonly FileReference MacroNamesFile = FileReference.Combine(UnrealBuild.EngineDirectory, "Build", "Windows", "PreludeMacros.txt");
/// <summary>
/// Base directory for the cache
/// </summary>
static readonly DirectoryReference PreludeCacheDir = DirectoryReference.Combine(UnrealBuild.EngineDirectory, "Saved", "UnrealBuildTool", "Prelude", "MSVC");
/// <summary>
/// The parsed list of macros
/// </summary>
static List<string> MacroNames = new List<string>();
/// <summary>
/// Path to the file used to generate prelude headers
/// </summary>
static FileReference? PreludeGeneratorFile;
/// <summary>
/// Cached mapping from compiler filename to its version string
/// </summary>
static Dictionary<FileReference, string> CompilerExeToVersion = new Dictionary<FileReference, string>();
/// <summary>
/// Get the prelude file (or create it) for the given compiler executable and set of options
/// </summary>
/// <param name="Compiler">Path to the compiler executable</param>
/// <param name="Arguments">Command line arguments for generating the prelude file</param>
/// <returns>Path to the prelude file</returns>
public static FileReference GetPreludeHeader(FileReference Compiler, List<string> Arguments)
{
// Cache the compiler version
string? CompilerVersion;
lock (CompilerExeToVersion)
{
if (!CompilerExeToVersion.TryGetValue(Compiler, out CompilerVersion))
{
FileVersionInfo VersionInfo = FileVersionInfo.GetVersionInfo(Compiler.FullName);
CompilerVersion = String.Format("{0} {1}", VersionInfo.ProductName, VersionInfo.ProductVersion);
}
}
// Cache the macro list
lock (MacroNames)
{
if (MacroNames.Count == 0)
{
string[] MacroListLines = FileReference.ReadAllLines(MacroNamesFile);
foreach (string MacroListLine in MacroListLines)
{
string MacroName = MacroListLine.Trim();
if (MacroName.Length > 0 && MacroName[0] != ';')
{
MacroNames.Add(MacroName);
}
}
}
}
// Create a digest from the command line
string CommandLine = String.Join(" ", Arguments.Where(x => FilterArguments.Contains(x)).OrderBy(x => x));
ContentHash Digest = ContentHash.SHA1(String.Format("{0}\n{1}\n{2}", CompilerVersion, CommandLine, String.Join("\n", MacroNames)));
// Get the prelude file
FileReference PreludeHeader = FileReference.Combine(PreludeCacheDir, String.Format("{0}.h", Digest));
if (!FileReference.Exists(PreludeHeader))
{
lock (LockObject)
{
if (!FileReference.Exists(PreludeHeader))
{
CreatePreludeHeader(PreludeHeader, Compiler, CompilerVersion, CommandLine);
}
}
}
return PreludeHeader;
}
/// <summary>
/// Creates a prelude header
/// </summary>
/// <param name="PreludeHeader">Path to the file to create</param>
/// <param name="Compiler">Path to the compiler</param>
/// <param name="CompilerVersion">Version number of the compiler</param>
/// <param name="CommandLine">Command line arguments used to generate the header</param>
static void CreatePreludeHeader(FileReference PreludeHeader, FileReference Compiler, string CompilerVersion, string CommandLine)
{
// Make sure the cache directory exists
DirectoryReference.CreateDirectory(PreludeCacheDir);
// Make sure the prelude generator file exists
if (PreludeGeneratorFile == null)
{
PreludeGeneratorFile = FileReference.Combine(PreludeCacheDir, "PreludeGenerator.cpp");
using (StreamWriter Writer = new StreamWriter(PreludeGeneratorFile.FullName))
{
Writer.WriteLine("#define STRINGIZE_2(x) #x");
Writer.WriteLine("#define STRINGIZE(x) STRINGIZE_2(x)");
foreach (string MacroName in MacroNames)
{
Writer.WriteLine("");
Writer.WriteLine("#ifdef {0}", MacroName);
Writer.WriteLine("#pragma message(\"#define {0} \" STRINGIZE({0}))", MacroName);
Writer.WriteLine("#endif");
}
}
}
// Invoke the compiler and capture the output
using (ManagedProcessGroup ProcessGroup = new ManagedProcessGroup())
{
string FullCommandLine = String.Format("{0} /nologo /c {1}", CommandLine, Utils.MakePathSafeToUseWithCommandLine(PreludeGeneratorFile.FullName));
using (ManagedProcess Process = new ManagedProcess(ProcessGroup, Compiler.FullName, FullCommandLine, PreludeCacheDir.FullName, null, null, ProcessPriorityClass.Normal))
{
// Filter the output
FileReference TempPreludeFile = new FileReference(PreludeHeader.FullName + ".temp");
using (StreamWriter Writer = new StreamWriter(TempPreludeFile.FullName))
{
Writer.WriteLine("// Compiler: {0}", CompilerVersion);
Writer.WriteLine("// Arguments: {0}", CommandLine);
Writer.WriteLine();
string? Line;
while (Process.TryReadLine(out Line))
{
Line = Line.Trim();
if (Line.Length > 0)
{
if (Line.StartsWith("#define", StringComparison.Ordinal))
{
Writer.WriteLine(Line);
}
else if (Line.Trim() != PreludeGeneratorFile.GetFileName())
{
throw new BuildException("Unexpected output from compiler: {0}", Line);
}
}
}
}
FileReference.Move(TempPreludeFile, PreludeHeader);
}
}
}
}
}