Add initial support for Precompiled Header Files (PCH) in MSVC compilation

This commit is contained in:
Wojtek Figat
2023-11-15 10:30:59 +01:00
parent 3904756e26
commit 80a30f504a
7 changed files with 180 additions and 6 deletions

25
Source/FlaxEngine.pch.h Normal file
View File

@@ -0,0 +1,25 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
// Include common engine headers
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/StringUtils.h"
#include "Engine/Platform/CriticalSection.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Types/Variant.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Quaternion.h"
#include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/BoundingSphere.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Log.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Serialization/SerializationFwd.h"

View File

@@ -35,6 +35,10 @@ namespace Flax.Build
options.ScriptingAPI.Defines.Add("FLAX_GAME");
}
// Use custom precompiled header file for the engine to boost compilation time
options.CompileEnv.PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.CreateManual;
options.CompileEnv.PrecompiledHeaderSource = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, "Source/FlaxEngine.pch.h"));
BinaryModuleName = "FlaxEngine";
options.ScriptingAPI.Defines.Add("FLAX");
options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS");

View File

@@ -23,6 +23,27 @@ namespace Flax.Build.NativeCpp
GenerateProject = 1,
}
/// <summary>
/// Precompiled Headers Files (PCH) usage modes.
/// </summary>
public enum PrecompiledHeaderFileUsage
{
/// <summary>
/// Precompiled Headers Files (PCH) feature is disabled.
/// </summary>
None,
/// <summary>
/// Enables creation and usage of the header file. The input source PCH will be precompiled and included.
/// </summary>
CreateManual,
/// <summary>
/// Enables usage of the header file. The input source PCH will be included in the build (assuming it exists).
/// </summary>
UseManual,
}
/// <summary>
/// The nullable context type used with reference types (C#).
/// </summary>

View File

@@ -32,6 +32,7 @@ namespace Flax.Build
public IGrouping<string, Module>[] BinaryModules;
public BuildTargetInfo BuildInfo;
public Dictionary<ProjectInfo, BuildData> ReferenceBuilds = new Dictionary<ProjectInfo, BuildData>();
public Dictionary<string, string> PrecompiledHeaderFiles = new();
public BuildTargetBinaryModuleInfo FinReferenceBuildModule(string name)
{
@@ -484,6 +485,13 @@ namespace Flax.Build
}
}
// If the PCH was already created (eg. by other engine module) then simply reuse the same file
if (moduleOptions.CompileEnv.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual && buildData.PrecompiledHeaderFiles.TryGetValue(moduleOptions.CompileEnv.PrecompiledHeaderSource, out var pch))
{
moduleOptions.CompileEnv.PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.UseManual;
moduleOptions.CompileEnv.PrecompiledHeaderFile = pch;
}
// Compile all source files
var compilationOutput = buildData.Toolchain.CompileCppFiles(buildData.Graph, moduleOptions, cppFiles, moduleOptions.OutputFolder);
foreach (var e in compilationOutput.ObjectFiles)
@@ -493,6 +501,11 @@ namespace Flax.Build
// TODO: find better way to add generated doc files to the target linker (module exports the output doc files?)
buildData.TargetOptions.LinkEnv.DocumentationFiles.AddRange(compilationOutput.DocumentationFiles);
}
if (moduleOptions.CompileEnv.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual && !string.IsNullOrEmpty(compilationOutput.PrecompiledHeaderFile))
{
// Cache PCH file to be used by other modules that reference the same file
buildData.PrecompiledHeaderFiles.Add(moduleOptions.CompileEnv.PrecompiledHeaderSource, compilationOutput.PrecompiledHeaderFile);
}
if (buildData.Target.LinkType != TargetLinkType.Monolithic)
{

View File

@@ -162,6 +162,21 @@ namespace Flax.Build.NativeCpp
/// </summary>
public readonly HashSet<string> CustomArgs = new HashSet<string>();
/// <summary>
/// The Precompiled Header File (PCH) usage.
/// </summary>
public PrecompiledHeaderFileUsage PrecompiledHeaderUsage = PrecompiledHeaderFileUsage.None;
/// <summary>
/// The Precompiled Header File (PCH) binary path. Null if not created.
/// </summary>
public string PrecompiledHeaderFile;
/// <summary>
/// The Precompiled Header File (PCH) source path. Null if not provided.
/// </summary>
public string PrecompiledHeaderSource;
/// <inheritdoc />
public object Clone()
{
@@ -184,7 +199,10 @@ namespace Flax.Build.NativeCpp
StringPooling = StringPooling,
IntrinsicFunctions = IntrinsicFunctions,
BufferSecurityCheck = BufferSecurityCheck,
TreatWarningsAsErrors = TreatWarningsAsErrors
TreatWarningsAsErrors = TreatWarningsAsErrors,
PrecompiledHeaderUsage = PrecompiledHeaderUsage,
PrecompiledHeaderFile = PrecompiledHeaderFile,
PrecompiledHeaderSource = PrecompiledHeaderSource,
};
clone.PreprocessorDefinitions.AddRange(PreprocessorDefinitions);
clone.IncludePaths.AddRange(IncludePaths);

View File

@@ -23,5 +23,10 @@ namespace Flax.Build.NativeCpp
/// The result documentation files.
/// </summary>
public readonly List<string> DocumentationFiles = new List<string>();
/// <summary>
/// The result precompiled header file (PCH) created during compilation. Can be used in other compilations (as shared).
/// </summary>
public string PrecompiledHeaderFile;
}
}

View File

@@ -430,6 +430,7 @@ namespace Flax.Build.Platforms
var commonArgs = new List<string>();
commonArgs.AddRange(options.CompileEnv.CustomArgs);
SetupCompileCppFilesArgs(graph, options, commonArgs);
var useSeparatePdb = true; //compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.None;
{
// Suppress Startup Banner
commonArgs.Add("/nologo");
@@ -490,7 +491,10 @@ namespace Flax.Build.Platforms
if (compileEnvironment.DebugInformation)
{
// Debug Information Format
commonArgs.Add("/Zi");
if (useSeparatePdb)
commonArgs.Add("/Zi");
else
commonArgs.Add("/Z7");
// Enhance Optimized Debugging
commonArgs.Add("/Zo");
@@ -611,8 +615,65 @@ namespace Flax.Build.Platforms
AddIncludePath(commonArgs, includePath);
}
// Compile all C++ files
var args = new List<string>();
// Create precompiled header
string pchFile = null, pchSource = null;
if (compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.UseManual)
{
pchFile = compileEnvironment.PrecompiledHeaderFile;
pchSource = compileEnvironment.PrecompiledHeaderSource;
}
else if (compileEnvironment.PrecompiledHeaderUsage == PrecompiledHeaderFileUsage.CreateManual)
{
// Use intermediate cpp file that includes the PCH path but also contains compiler info to properly recompile when it's modified
pchSource = compileEnvironment.PrecompiledHeaderSource;
var pchFilName = Path.GetFileName(pchSource);
var pchSourceFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "cpp"));
var contents = Bindings.BindingsGenerator.GetStringBuilder();
contents.AppendLine("// This code was auto-generated. Do not modify it.");
contents.Append("// Compiler: ").AppendLine(_compilerPath);
contents.Append("#include \"").Append(pchSource).AppendLine("\"");
Utilities.WriteFileIfChanged(pchSourceFile, contents.ToString());
Bindings.BindingsGenerator.PutStringBuilder(contents);
// Compile intermediate cpp file into actual PCH (and obj+pdb files)
pchFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "pch"));
if (pchFile.EndsWith(".pch.pch"))
pchFile = pchFile.Substring(0, pchFile.Length - 4);
var pchPdbFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "pdb"));
var pchObjFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "obj"));
var task = graph.Add<Task>();
task.PrerequisiteFiles.Add(pchSourceFile);
task.PrerequisiteFiles.Add(pchSource);
task.PrerequisiteFiles.AddRange(IncludesCache.FindAllIncludedFiles(pchSource));
task.ProducedFiles.Add(pchFile);
task.ProducedFiles.Add(pchObjFile);
args.AddRange(commonArgs);
args.Add(string.Format("/Yc\"{0}\"", pchSource));
args.Add(string.Format("/Fp\"{0}\"", pchFile));
args.Add(string.Format("/Fd\"{0}\"", pchPdbFile));
args.Add(string.Format("/Fo\"{0}\"", pchObjFile));
args.Add("/FS");
args.Add(string.Format("\"{0}\"", pchSourceFile));
task.WorkingDirectory = options.WorkingDirectory;
task.CommandPath = _compilerPath;
task.CommandArguments = string.Join(" ", args);
task.Cost = int.MaxValue; // Run it before any other tasks
// Setup outputs
output.PrecompiledHeaderFile = pchFile;
output.ObjectFiles.Add(pchObjFile);
}
if (pchFile != null)
{
// Include PCH file
commonArgs.Add(string.Format("/FI\"{0}\"", pchSource));
commonArgs.Add(string.Format("/Yu\"{0}\"", pchSource));
commonArgs.Add(string.Format("/Fp\"{0}\"", pchFile));
}
// Compile all C++ files
foreach (var sourceFile in sourceFiles)
{
var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile);
@@ -625,9 +686,25 @@ namespace Flax.Build.Platforms
if (compileEnvironment.DebugInformation)
{
// Program Database File Name
var pdbFile = Path.Combine(outputPath, sourceFilename + ".pdb");
args.Add(string.Format("/Fd\"{0}\"", pdbFile));
output.DebugDataFiles.Add(pdbFile);
string pdbFile = null;
if (pchFile != null)
{
// When using PCH we need to share the same PDB file that was used when building PCH
pdbFile = pchFile + ".pdb";
// Turn on sync for file access to prevent issues when compiling on multiple threads at once
if (useSeparatePdb)
args.Add("/FS");
}
else if (useSeparatePdb)
{
pdbFile = Path.Combine(outputPath, sourceFilename + ".pdb");
}
if (pdbFile != null)
{
args.Add(string.Format("/Fd\"{0}\"", pdbFile));
output.DebugDataFiles.Add(pdbFile);
}
}
if (compileEnvironment.GenerateDocumentation)
@@ -650,6 +727,10 @@ namespace Flax.Build.Platforms
// Request included files to exist
var includes = IncludesCache.FindAllIncludedFiles(sourceFile);
task.PrerequisiteFiles.AddRange(includes);
if (pchFile != null)
{
task.PrerequisiteFiles.Add(pchFile);
}
// Compile
task.WorkingDirectory = options.WorkingDirectory;
@@ -853,6 +934,13 @@ namespace Flax.Build.Platforms
task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles);
foreach (var file in linkEnvironment.InputFiles)
{
if (file.EndsWith(".pch", StringComparison.OrdinalIgnoreCase))
{
// PCH file
args.Add(string.Format("/Yu:\"{0}\"", file));
continue;
}
args.Add(string.Format("\"{0}\"", file));
}